Files
Genarrative/src/components/common/PlatformAssetPickerCard.test.tsx
T
kdletters 7f8400fd3a 继续扩展共享异步状态壳
将 PlatformAsyncStatePanel 扩展到公共素材选择网格

将 PlatformAsyncStatePanel 扩展到视觉小说存档面板与账号安全子区块

将兑换码弹窗提交动作改为使用标准 profile modal footer

补充对应测试并更新 PlatformUiKit 收口计划与共享决策记录
2026-06-11 04:11:32 +08:00

265 lines
7.3 KiB
TypeScript

/* @vitest-environment jsdom */
import { fireEvent, render, screen } from '@testing-library/react';
import { expect, test, vi } from 'vitest';
import {
PlatformAssetPickerCard,
PlatformAssetPickerGrid,
} from './PlatformAssetPickerCard';
vi.mock('../ResolvedAssetImage', () => ({
ResolvedAssetImage: ({
src,
alt,
className,
}: {
src?: string | null;
alt?: string;
className?: string;
}) => <img src={src ?? ''} alt={alt} className={className} />,
}));
test('renders historical asset thumbnail with title and subtitle', () => {
render(
<PlatformAssetPickerCard
imageSrc="/history/a.png"
imageAlt="历史图片"
assetTitle="封面图"
subtitle="2026-06-09 10:00"
aria-label="选择封面图"
/>,
);
const button = screen.getByRole('button', { name: '选择封面图' });
const image = screen.getByRole('img', { name: '历史图片' });
expect(button.className).toContain(
'border-[var(--platform-subpanel-border)]',
);
expect(button.className).toContain('hover:border-amber-300/70');
expect(image.getAttribute('src')).toBe('/history/a.png');
expect(screen.getByText('封面图')).toBeTruthy();
expect(screen.getByText('2026-06-09 10:00')).toBeTruthy();
});
test('keeps disabled picker card inert', () => {
const onClick = vi.fn();
render(
<PlatformAssetPickerCard
imageSrc="/history/a.png"
imageAlt=""
assetTitle="历史素材"
disabled
onClick={onClick}
/>,
);
const button = screen.getByRole('button', { name: '历史素材' });
fireEvent.click(button);
expect(button).toHaveProperty('disabled', true);
expect(button.className).toContain('cursor-not-allowed');
expect(onClick).not.toHaveBeenCalled();
});
test('supports local radius and body classes for dense pickers', () => {
render(
<PlatformAssetPickerCard
imageSrc="/history/a.png"
imageAlt="历史图片"
subtitle="刚刚"
cardRadiusClassName="rounded-[1.25rem]"
bodyClassName="px-4 py-3"
aria-label="选择历史图片"
/>,
);
const button = screen.getByRole('button', { name: '选择历史图片' });
expect(button.className).toContain('rounded-[1.25rem]');
expect(screen.getByText('刚刚').parentElement?.className).toContain('px-4');
});
test('supports local image shell classes for landscape assets', () => {
render(
<PlatformAssetPickerCard
imageSrc="/history/scene.png"
imageAlt="场景图"
imageShellClassName="aspect-[16/9]"
aria-label="选择场景图"
/>,
);
expect(
screen.getByRole('img', { name: '场景图' }).parentElement?.className,
).toContain('aspect-[16/9]');
});
test('renders selected picker cards with shared selected chrome', () => {
render(
<PlatformAssetPickerCard
imageSrc="/history/a.png"
imageAlt="历史图片"
assetTitle="已选择素材"
aria-label="选择历史图片"
selected
/>,
);
const button = screen.getByRole('button', { name: '选择历史图片' });
expect(button.className).toContain('ring-2');
expect(button.className).toContain('border-[var(--platform-warm-border)]');
});
test('renders shared loading and empty states for asset grids', () => {
const { rerender } = render(
<PlatformAssetPickerGrid
items={[]}
isLoading
loadingLabel="读取中..."
emptyLabel="暂无历史素材"
getKey={(item: { id: string }) => item.id}
getImageSrc={(item) => item.id}
getImageAlt={() => ''}
onSelect={() => {}}
/>,
);
expect(screen.getByText('读取中...').className).toContain('border-dashed');
rerender(
<PlatformAssetPickerGrid
items={[]}
loadingLabel="读取中..."
emptyLabel="暂无历史素材"
getKey={(item: { id: string }) => item.id}
getImageSrc={(item) => item.id}
getImageAlt={() => ''}
onSelect={() => {}}
/>,
);
expect(screen.getByText('暂无历史素材').className).toContain('border-dashed');
});
test('renders selectable asset grid cards with shared error chrome', () => {
const onSelect = vi.fn();
render(
<PlatformAssetPickerGrid
items={[
{
id: 'asset-1',
imageSrc: '/history/a.png',
title: '历史素材',
createdAt: '2026-06-09',
},
]}
error="历史素材读取失败。"
loadingLabel="读取中..."
emptyLabel="暂无历史素材"
getKey={(item) => item.id}
getImageSrc={(item) => item.imageSrc}
getImageAlt={() => '历史素材'}
getTitle={(item) => item.title}
getSubtitle={(item) => item.createdAt}
getAriaLabel={(item) => `选择${item.title}`}
isSelected={(item) => item.id === 'asset-1'}
onSelect={onSelect}
gridClassName="grid grid-cols-1"
/>,
);
expect(screen.getByText('历史素材读取失败。').className).toContain(
'text-[var(--platform-button-danger-text)]',
);
fireEvent.click(screen.getByRole('button', { name: '选择历史素材' }));
expect(
screen.getByRole('button', { name: '选择历史素材' }).className,
).toContain('ring-2');
expect(onSelect).toHaveBeenCalledWith(
expect.objectContaining({ id: 'asset-1' }),
);
});
test('asset grid keeps error banner while loading state remains mutually exclusive with empty state', () => {
render(
<PlatformAssetPickerGrid
items={[]}
isLoading
error="历史素材读取失败。"
loadingLabel="读取中..."
emptyLabel="暂无历史素材"
getKey={(item: { id: string }) => item.id}
getImageSrc={(item) => item.id}
getImageAlt={() => ''}
onSelect={() => {}}
/>,
);
expect(screen.getByText('历史素材读取失败。')).toBeTruthy();
expect(screen.getByText('读取中...')).toBeTruthy();
expect(screen.queryByText('暂无历史素材')).toBeNull();
});
test('supports dark editor surface with an in-card select affordance', () => {
const onSelect = vi.fn();
render(
<PlatformAssetPickerGrid
items={[
{
id: 'asset-dark-1',
imageSrc: '/history/dark.png',
title: '角色立绘',
createdAt: '2026-06-09',
},
]}
surface="editorDark"
selectLabel="使用"
loadingLabel="读取中..."
emptyLabel="暂无历史素材"
getKey={(item) => item.id}
getImageSrc={(item) => item.imageSrc}
getImageAlt={(item) => item.title}
getTitle={(item) => item.title}
getSubtitle={(item) => item.createdAt}
onSelect={onSelect}
/>,
);
const button = screen.getByRole('button', { name: /角色立绘/u });
expect(button.className).toContain('bg-black/20');
expect(screen.getByText('使用').className).toContain('bg-sky-500/12');
fireEvent.click(button);
expect(onSelect).toHaveBeenCalledWith(
expect.objectContaining({ id: 'asset-dark-1' }),
);
});
test('uses dark empty-state chrome for editor asset grids', () => {
render(
<PlatformAssetPickerGrid
items={[]}
surface="editorDark"
loadingLabel="读取中..."
emptyLabel="暂无历史素材"
getKey={(item: { id: string }) => item.id}
getImageSrc={(item) => item.id}
getImageAlt={() => ''}
onSelect={() => {}}
/>,
);
expect(screen.getByText('暂无历史素材').className).toContain('bg-black/20');
expect(screen.getByText('暂无历史素材').className).toContain('text-zinc-300');
});