This commit is contained in:
@@ -0,0 +1,272 @@
|
||||
/* @vitest-environment jsdom */
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { afterEach, expect, test, vi } from 'vitest';
|
||||
|
||||
import type { CustomWorldWorkSummary } from '../../../packages/shared/src/contracts/customWorldAgent';
|
||||
import { CustomWorldCreationHub } from './CustomWorldCreationHub';
|
||||
|
||||
const noopCreateType = () => {};
|
||||
const originalClipboard = navigator.clipboard;
|
||||
|
||||
afterEach(() => {
|
||||
Object.defineProperty(navigator, 'clipboard', {
|
||||
configurable: true,
|
||||
value: originalClipboard,
|
||||
});
|
||||
});
|
||||
|
||||
const baseDraftItem: CustomWorldWorkSummary = {
|
||||
workId: 'draft:session-1',
|
||||
sourceType: 'agent_session',
|
||||
status: 'draft',
|
||||
title: '潮雾列岛',
|
||||
subtitle: '补齐关键锚点',
|
||||
summary: '玩家是失职返乡的守灯人。',
|
||||
coverImageSrc: null,
|
||||
updatedAt: new Date('2026-04-14T10:00:00.000Z').toISOString(),
|
||||
publishedAt: null,
|
||||
stage: 'object_refining',
|
||||
stageLabel: '待完善草稿',
|
||||
playableNpcCount: 3,
|
||||
landmarkCount: 4,
|
||||
sessionId: 'session-1',
|
||||
profileId: null,
|
||||
canResume: true,
|
||||
canEnterWorld: false,
|
||||
};
|
||||
|
||||
test('creation hub reflects updated draft title summary and counts after rerender', () => {
|
||||
const { rerender } = render(
|
||||
<CustomWorldCreationHub
|
||||
items={[baseDraftItem]}
|
||||
loading={false}
|
||||
error={null}
|
||||
onRetry={() => {}}
|
||||
onCreateType={noopCreateType}
|
||||
onOpenDraft={() => {}}
|
||||
onEnterPublished={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('潮雾列岛')).toBeTruthy();
|
||||
expect(screen.getByText('玩家是失职返乡的守灯人。')).toBeTruthy();
|
||||
expect(screen.getByText('角色 3')).toBeTruthy();
|
||||
expect(screen.getByText('地点 4')).toBeTruthy();
|
||||
expect(screen.getByRole('button', { name: /角色扮演 RPG/u })).toBeTruthy();
|
||||
expect(screen.getByRole('button', { name: /大鱼吃小鱼/u })).toBeTruthy();
|
||||
expect(screen.getByRole('button', { name: /拼图玩法/u })).toBeTruthy();
|
||||
|
||||
rerender(
|
||||
<CustomWorldCreationHub
|
||||
items={[
|
||||
{
|
||||
...baseDraftItem,
|
||||
title: '潮雾列岛·回潮版',
|
||||
summary: '世界总卡和角色网已经继续长出了新的支线。',
|
||||
playableNpcCount: 5,
|
||||
landmarkCount: 6,
|
||||
updatedAt: new Date('2026-04-14T10:10:00.000Z').toISOString(),
|
||||
},
|
||||
]}
|
||||
loading={false}
|
||||
error={null}
|
||||
onRetry={() => {}}
|
||||
onCreateType={noopCreateType}
|
||||
onOpenDraft={() => {}}
|
||||
onEnterPublished={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('潮雾列岛·回潮版')).toBeTruthy();
|
||||
expect(
|
||||
screen.getByText('世界总卡和角色网已经继续长出了新的支线。'),
|
||||
).toBeTruthy();
|
||||
expect(screen.getByText('角色 5')).toBeTruthy();
|
||||
expect(screen.getByText('地点 6')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('creation hub mixes puzzle works into the same grid and uses puzzle tag to distinguish', () => {
|
||||
render(
|
||||
<CustomWorldCreationHub
|
||||
items={[baseDraftItem]}
|
||||
puzzleItems={[
|
||||
{
|
||||
workId: 'puzzle:work-1',
|
||||
profileId: 'puzzle-profile-1',
|
||||
ownerUserId: 'user-1',
|
||||
authorDisplayName: '拼图作者',
|
||||
levelName: '沉钟拼图',
|
||||
summary: '拼图作品会与其他创作作品一起展示。',
|
||||
themeTags: ['潮雾', '沉钟'],
|
||||
coverImageSrc: null,
|
||||
publicationStatus: 'published',
|
||||
updatedAt: new Date('2026-04-22T12:00:00.000Z').toISOString(),
|
||||
publishedAt: new Date('2026-04-22T12:10:00.000Z').toISOString(),
|
||||
playCount: 8,
|
||||
publishReady: true,
|
||||
},
|
||||
]}
|
||||
loading={false}
|
||||
error={null}
|
||||
onRetry={() => {}}
|
||||
onCreateType={noopCreateType}
|
||||
onOpenDraft={() => {}}
|
||||
onEnterPublished={() => {}}
|
||||
onOpenPuzzleDetail={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('潮雾列岛')).toBeTruthy();
|
||||
expect(screen.getByText('沉钟拼图')).toBeTruthy();
|
||||
expect(screen.getByText('PZ-PROFILE1')).toBeTruthy();
|
||||
expect(screen.getAllByText('拼图').length).toBeGreaterThan(0);
|
||||
expect(screen.queryByText('我的拼图作品')).toBeNull();
|
||||
});
|
||||
|
||||
test('creation hub shows RPG public work code from published library entry', () => {
|
||||
render(
|
||||
<CustomWorldCreationHub
|
||||
items={[
|
||||
{
|
||||
...baseDraftItem,
|
||||
workId: 'published:world-public-1',
|
||||
sourceType: 'published_profile',
|
||||
status: 'published',
|
||||
title: '潮雾列岛已发布版',
|
||||
profileId: 'world-public-1',
|
||||
canResume: false,
|
||||
canEnterWorld: true,
|
||||
},
|
||||
]}
|
||||
rpgLibraryEntries={[
|
||||
{
|
||||
ownerUserId: 'user-1',
|
||||
profileId: 'world-public-1',
|
||||
publicWorkCode: 'CW-00000001',
|
||||
authorPublicUserCode: 'SY-00000001',
|
||||
profile: {} as never,
|
||||
visibility: 'published',
|
||||
publishedAt: '2026-04-20T10:00:00.000Z',
|
||||
updatedAt: '2026-04-20T10:00:00.000Z',
|
||||
authorDisplayName: '测试玩家',
|
||||
worldName: '潮雾列岛已发布版',
|
||||
subtitle: '旧灯塔与失控航路',
|
||||
summaryText: '已经发布的群岛世界作品。',
|
||||
coverImageSrc: null,
|
||||
themeMode: 'tide',
|
||||
playableNpcCount: 3,
|
||||
landmarkCount: 4,
|
||||
},
|
||||
]}
|
||||
loading={false}
|
||||
error={null}
|
||||
onRetry={() => {}}
|
||||
onCreateType={noopCreateType}
|
||||
onOpenDraft={() => {}}
|
||||
onEnterPublished={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('潮雾列岛已发布版')).toBeTruthy();
|
||||
expect(screen.getByText('CW-00000001')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('creation hub shows delete action for persisted rpg drafts', () => {
|
||||
render(
|
||||
<CustomWorldCreationHub
|
||||
items={[{ ...baseDraftItem, profileId: 'profile-1' }]}
|
||||
loading={false}
|
||||
error={null}
|
||||
onRetry={() => {}}
|
||||
onCreateType={noopCreateType}
|
||||
onOpenDraft={() => {}}
|
||||
onEnterPublished={() => {}}
|
||||
onDeletePublished={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole('button', { name: '删除' })).toBeTruthy();
|
||||
});
|
||||
|
||||
test('creation hub opens persisted rpg drafts by card click', async () => {
|
||||
const user = userEvent.setup();
|
||||
const openedItems: CustomWorldWorkSummary[] = [];
|
||||
const persistedDraft = {
|
||||
...baseDraftItem,
|
||||
workId: 'draft:profile-1',
|
||||
sourceType: 'published_profile' as const,
|
||||
sessionId: null,
|
||||
profileId: 'profile-1',
|
||||
title: '可继续整理的草稿',
|
||||
};
|
||||
|
||||
render(
|
||||
<CustomWorldCreationHub
|
||||
items={[persistedDraft]}
|
||||
loading={false}
|
||||
error={null}
|
||||
onRetry={() => {}}
|
||||
onCreateType={noopCreateType}
|
||||
onOpenDraft={(item) => {
|
||||
openedItems.push(item);
|
||||
}}
|
||||
onEnterPublished={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole('button', { name: /继续完善《可继续整理的草稿》/u }),
|
||||
);
|
||||
|
||||
expect(openedItems).toEqual([persistedDraft]);
|
||||
});
|
||||
|
||||
test('creation hub work code copy button copies without opening the card', async () => {
|
||||
const user = userEvent.setup();
|
||||
const writeText = vi.fn(async () => undefined);
|
||||
const onOpenPuzzleDetail = vi.fn();
|
||||
Object.defineProperty(navigator, 'clipboard', {
|
||||
configurable: true,
|
||||
value: { writeText },
|
||||
});
|
||||
|
||||
render(
|
||||
<CustomWorldCreationHub
|
||||
items={[]}
|
||||
puzzleItems={[
|
||||
{
|
||||
workId: 'puzzle:work-1',
|
||||
profileId: 'puzzle-profile-1',
|
||||
ownerUserId: 'user-1',
|
||||
authorDisplayName: '拼图作者',
|
||||
levelName: '沉钟拼图',
|
||||
summary: '拼图作品会与其他创作作品一起展示。',
|
||||
themeTags: ['潮雾', '沉钟'],
|
||||
coverImageSrc: null,
|
||||
publicationStatus: 'published',
|
||||
updatedAt: new Date('2026-04-22T12:00:00.000Z').toISOString(),
|
||||
publishedAt: new Date('2026-04-22T12:10:00.000Z').toISOString(),
|
||||
playCount: 8,
|
||||
publishReady: true,
|
||||
},
|
||||
]}
|
||||
loading={false}
|
||||
error={null}
|
||||
onRetry={() => {}}
|
||||
onCreateType={noopCreateType}
|
||||
onOpenDraft={() => {}}
|
||||
onEnterPublished={() => {}}
|
||||
onOpenPuzzleDetail={onOpenPuzzleDetail}
|
||||
/>,
|
||||
);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole('button', { name: '复制作品号 PZ-PROFILE1' }),
|
||||
);
|
||||
|
||||
expect(writeText).toHaveBeenCalledWith('PZ-PROFILE1');
|
||||
expect(onOpenPuzzleDetail).not.toHaveBeenCalled();
|
||||
expect(await screen.findByText('已复制')).toBeTruthy();
|
||||
});
|
||||
Reference in New Issue
Block a user