Files
Genarrative/src/components/platform-entry/PlatformWorkDetailView.test.tsx
2026-05-14 14:21:17 +08:00

289 lines
7.8 KiB
TypeScript

/* @vitest-environment jsdom */
import { fireEvent, render, screen } from '@testing-library/react';
import { act } from 'react';
import { afterEach, expect, test, vi } from 'vitest';
import {
EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID,
EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME,
type PlatformEdutainmentGalleryCard,
type PlatformPuzzleGalleryCard,
} from '../rpg-entry/rpgEntryWorldPresentation';
import { PlatformWorkDetailView } from './PlatformWorkDetailView';
vi.mock('../ResolvedAssetImage', () => ({
ResolvedAssetImage: ({
src,
alt,
className,
'aria-hidden': ariaHidden,
}: {
src?: string | null;
alt?: string;
className?: string;
'aria-hidden'?: boolean | 'true' | 'false';
}) => (
<img
src={src ?? ''}
alt={alt ?? ''}
className={className}
aria-hidden={ariaHidden}
/>
),
}));
function createPuzzleEntry(): PlatformPuzzleGalleryCard {
return {
sourceType: 'puzzle',
workId: 'work-1',
profileId: 'profile-1',
publicWorkCode: 'PZ-001',
ownerUserId: 'user-1',
authorDisplayName: '137****6613',
worldName: '关键词:逍遥游拼图',
subtitle: '拼图关卡',
summaryText: '适合公开游玩的拼图作品。',
coverImageSrc: null,
coverSlides: [],
themeTags: ['拼图'],
playCount: 12,
remixCount: 3,
likeCount: 4,
recentPlayCount7d: 0,
visibility: 'published',
publishedAt: '2026-04-20T10:00:00.000Z',
updatedAt: '2026-04-25T12:00:00.000Z',
};
}
function createBabyObjectMatchEntry(): PlatformEdutainmentGalleryCard {
return {
sourceType: 'edutainment',
templateId: EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_ID,
templateName: EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME,
workId: 'baby-object-match-work-1',
profileId: 'baby-object-match-profile-1',
publicWorkCode: 'EDU-BABY01',
ownerUserId: 'user-1',
authorDisplayName: '陶泥儿主',
worldName: '宝贝识物水果篮',
subtitle: EDUTAINMENT_BABY_OBJECT_MATCH_TEMPLATE_NAME,
summaryText: '将物品放入对应的篮子里。',
coverImageSrc: null,
themeTags: ['寓教于乐'],
playCount: 12,
remixCount: 0,
likeCount: 4,
recentPlayCount7d: 0,
visibility: 'published',
publishedAt: '2026-05-11T10:00:00.000Z',
updatedAt: '2026-05-11T12:00:00.000Z',
};
}
afterEach(() => {
vi.useRealTimers();
});
test('PlatformWorkDetailView renders compact stats and date time', () => {
render(
<PlatformWorkDetailView
entry={createPuzzleEntry()}
isBusy={false}
error={null}
onBack={vi.fn()}
onLike={vi.fn()}
onStart={vi.fn()}
onRemix={vi.fn()}
/>,
);
expect(screen.getByText('改造')).toBeTruthy();
expect(screen.getByText('游玩')).toBeTruthy();
expect(screen.getAllByText('点赞').length).toBeGreaterThanOrEqual(2);
expect(screen.getByText('日期')).toBeTruthy();
expect(screen.queryByText('改造次数')).toBeNull();
expect(screen.queryByText('游玩次数')).toBeNull();
expect(screen.queryByText('上线日期')).toBeNull();
expect(screen.queryByText('最近更新')).toBeNull();
expect(screen.getByText('2026-04-25')).toBeTruthy();
expect(screen.getAllByText('次')).toHaveLength(2);
expect(screen.getByText('赞')).toBeTruthy();
expect(screen.getByRole('button', { name: '点赞 4赞' })).toBeTruthy();
expect(screen.getByRole('button', { name: '作品改造' })).toBeTruthy();
expect(screen.getByRole('button', { name: '启动' })).toBeTruthy();
});
test('PlatformWorkDetailView prefers resolved public user display name', () => {
render(
<PlatformWorkDetailView
entry={createPuzzleEntry()}
authorDisplayName="新的作者昵称"
isBusy={false}
error={null}
onBack={vi.fn()}
onLike={vi.fn()}
onStart={vi.fn()}
onRemix={vi.fn()}
/>,
);
expect(screen.getByText('新的作者昵称')).toBeTruthy();
expect(screen.queryByText('137****6613')).toBeNull();
});
test('PlatformWorkDetailView calls like handler', () => {
const onLike = vi.fn();
render(
<PlatformWorkDetailView
entry={createPuzzleEntry()}
isBusy={false}
error={null}
onBack={vi.fn()}
onLike={onLike}
onStart={vi.fn()}
onRemix={vi.fn()}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '点赞 4赞' }));
expect(onLike).toHaveBeenCalledTimes(1);
});
test('PlatformWorkDetailView switches remix action label for owned work edit', () => {
render(
<PlatformWorkDetailView
entry={createPuzzleEntry()}
actionMode="edit"
isBusy={false}
error={null}
onBack={vi.fn()}
onLike={vi.fn()}
onStart={vi.fn()}
onRemix={vi.fn()}
/>,
);
expect(screen.getByRole('button', { name: '作品编辑' })).toBeTruthy();
expect(screen.queryByRole('button', { name: '作品改造' })).toBeNull();
});
test('PlatformWorkDetailView labels baby object match works', () => {
render(
<PlatformWorkDetailView
entry={createBabyObjectMatchEntry()}
isBusy={false}
error={null}
onBack={vi.fn()}
onLike={vi.fn()}
onStart={vi.fn()}
onRemix={vi.fn()}
/>,
);
expect(screen.getByText('宝贝识物')).toBeTruthy();
expect(screen.getByText('EDU-BABY01')).toBeTruthy();
});
test('PlatformWorkDetailView cycles puzzle level cover slides', () => {
vi.useFakeTimers();
const { container } = render(
<PlatformWorkDetailView
entry={{
...createPuzzleEntry(),
coverImageSrc: '/fallback-cover.png',
coverSlides: [
{
id: 'level-1',
imageSrc: '/level-1.png',
label: '第一关',
},
{
id: 'level-2',
imageSrc: '/level-2.png',
label: '第二关',
},
],
}}
isBusy={false}
error={null}
onBack={vi.fn()}
onLike={vi.fn()}
onStart={vi.fn()}
onRemix={vi.fn()}
/>,
);
expect(screen.getAllByAltText('关键词:逍遥游拼图')[0]?.getAttribute('src')).toBe(
'/level-1.png',
);
const appIconImage = container.querySelector(
'.platform-work-detail__app-icon img',
);
expect(appIconImage?.getAttribute('src')).toBe('/level-1.png');
fireEvent.click(screen.getByRole('button', { name: '下一张关卡图' }));
expect(screen.getAllByAltText('关键词:逍遥游拼图')[0]?.getAttribute('src')).toBe(
'/level-2.png',
);
expect(appIconImage?.getAttribute('src')).toBe('/level-1.png');
expect(
container.querySelector('.platform-work-detail__cover-image--locked'),
).toBeTruthy();
expect(
container.querySelector('.platform-work-detail__cover-lock-icon'),
).toBeTruthy();
act(() => {
vi.advanceTimersByTime(4200);
});
expect(screen.getAllByAltText('关键词:逍遥游拼图')[0]?.getAttribute('src')).toBe(
'/level-1.png',
);
});
test('PlatformWorkDetailView unlocks later puzzle covers by visible cover count', () => {
const { container } = render(
<PlatformWorkDetailView
entry={{
...createPuzzleEntry(),
coverSlides: [
{
id: 'level-1',
imageSrc: '/level-1.png',
label: '第一关',
},
{
id: 'level-2',
imageSrc: '/level-2.png',
label: '第二关',
},
],
}}
visibleCoverCount={2}
isBusy={false}
error={null}
onBack={vi.fn()}
onLike={vi.fn()}
onStart={vi.fn()}
onRemix={vi.fn()}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '下一张关卡图' }));
expect(screen.getAllByAltText('关键词:逍遥游拼图')[0]?.getAttribute('src')).toBe(
'/level-2.png',
);
expect(
container.querySelector('.platform-work-detail__cover-image--locked'),
).toBeNull();
expect(
container.querySelector('.platform-work-detail__cover-lock-icon'),
).toBeNull();
});