This commit is contained in:
2026-05-05 14:40:41 +08:00
parent e847fcea6f
commit 07e777fef8
76 changed files with 4246 additions and 444 deletions

View File

@@ -138,7 +138,9 @@ async function clickFirstAsyncButtonByName(
async function openCreationHub(user: ReturnType<typeof userEvent.setup>) {
await clickFirstButtonByName(user, '创作');
expect(await screen.findByText('角色扮演')).toBeTruthy();
expect(
await screen.findByRole('button', { name: /.*/u }),
).toBeTruthy();
}
async function openExistingRpgDraft(
@@ -1867,7 +1869,7 @@ beforeEach(() => {
vi.mocked(streamRpgCreationMessage).mockResolvedValue(mockSession);
});
test('create hub opens RPG while keeping AIRP and visual novel locked', async () => {
test('create hub hides RPG and Match3D while keeping AIRP and visual novel locked', async () => {
const user = userEvent.setup();
render(<TestWrapper withAuth />);
@@ -1881,15 +1883,10 @@ test('create hub opens RPG while keeping AIRP and visual novel locked', async ()
expect((airpButton as HTMLButtonElement).disabled).toBe(true);
expect((visualNovelButton as HTMLButtonElement).disabled).toBe(true);
const rpgButton = screen.getByRole('button', { name: //u });
expect((rpgButton as HTMLButtonElement).disabled).toBe(false);
await user.click(rpgButton);
expect(createRpgCreationSession).toHaveBeenCalledTimes(1);
expect(
await screen.findByText('Agent工作区custom-world-agent-session-1'),
).toBeTruthy();
expect(screen.queryByRole('button', { name: //u })).toBeNull();
expect(screen.queryByRole('button', { name: //u })).toBeNull();
expect(createRpgCreationSession).not.toHaveBeenCalled();
expect(match3dCreationClient.createSession).not.toHaveBeenCalled();
});
test('platform create hub does not prefetch hidden big fish platform data', async () => {
@@ -1900,7 +1897,7 @@ test('platform create hub does not prefetch hidden big fish platform data', asyn
await openCreationHub(user);
expect(
await screen.findByRole('button', { name: //u }),
await screen.findByRole('button', { name: /.*/u }),
).toBeTruthy();
expect(screen.queryByRole('button', { name: //u })).toBeNull();
expect(listBigFishWorks).not.toHaveBeenCalled();
@@ -2643,7 +2640,7 @@ test('published puzzle detail returns to the ranking platform tab', async () =>
});
});
test('selecting RPG creation while logged out routes through requireAuth', async () => {
test('selecting puzzle creation while logged out routes through requireAuth', async () => {
const user = userEvent.setup();
const requireAuth = vi.fn();
@@ -2658,12 +2655,15 @@ test('selecting RPG creation while logged out routes through requireAuth', async
);
await openCreationHub(user);
const rpgButton = await screen.findByRole('button', { name: //u });
const puzzleButton = await screen.findByRole('button', {
name: /.*/u,
});
expect((rpgButton as HTMLButtonElement).disabled).toBe(false);
await user.click(rpgButton);
expect((puzzleButton as HTMLButtonElement).disabled).toBe(false);
await user.click(puzzleButton);
expect(requireAuth).toHaveBeenCalledTimes(1);
expect(createRpgCreationSession).not.toHaveBeenCalled();
expect(createPuzzleAgentSession).not.toHaveBeenCalled();
});
test('restoring an agent workspace while logged out opens login modal before loading the protected session', async () => {
@@ -2772,10 +2772,10 @@ test('refreshing RPG agent path restores stored agent workspace pointer', async
).toBeTruthy();
});
test('new creation entry maps raw bearer token errors to user-facing auth copy', async () => {
test('new puzzle creation entry maps raw bearer token errors to user-facing auth copy', async () => {
const user = userEvent.setup();
vi.mocked(createRpgCreationSession).mockRejectedValueOnce(
vi.mocked(createPuzzleAgentSession).mockRejectedValueOnce(
new ApiClientError({
message: '缺少 Authorization Bearer Token',
status: 401,
@@ -2786,13 +2786,15 @@ test('new creation entry maps raw bearer token errors to user-facing auth copy',
render(<TestWrapper withAuth />);
await openCreationHub(user);
const rpgButton = screen.getByRole('button', { name: //u });
const puzzleButton = screen.getByRole('button', {
name: /.*/u,
});
expect((rpgButton as HTMLButtonElement).disabled).toBe(false);
await user.click(rpgButton);
expect((puzzleButton as HTMLButtonElement).disabled).toBe(false);
await user.click(puzzleButton);
expect(listPuzzleWorks).toHaveBeenCalled();
expect(createRpgCreationSession).toHaveBeenCalledTimes(1);
expect(createPuzzleAgentSession).toHaveBeenCalledTimes(1);
expect(
await within(getPlatformTabPanel('create')).findByText(
'当前登录状态已失效,请重新登录后继续。',
@@ -2839,7 +2841,7 @@ test('puzzle creation timeout exits busy state and shows a readable error', asyn
expect(screen.queryByText(//u)).toBeNull();
});
test('match3d creation card opens workspace even when public galleries fail', async () => {
test('hidden match3d creation card stays closed even when public galleries fail', async () => {
const user = userEvent.setup();
const match3dSession = buildMockMatch3DAgentSession();
@@ -2858,20 +2860,13 @@ test('match3d creation card opens workspace even when public galleries fail', as
await openCreationHub(user);
expect(screen.queryByText('读取作品广场失败')).toBeNull();
expect(screen.queryByText('读取抓大鹅广场失败')).toBeNull();
const button = screen.getByRole('button', {
name: /.*/u,
});
expect(button as HTMLButtonElement).toHaveProperty('disabled', false);
await user.click(button);
await waitFor(() => {
expect(match3dCreationClient.createSession).toHaveBeenCalledWith({});
});
expect(await screen.findByText('抓大鹅工作区match3d-agent-session-1')).toBeTruthy();
expect(
screen.queryByRole('button', { name: /.*/u }),
).toBeNull();
expect(match3dCreationClient.createSession).not.toHaveBeenCalled();
});
test('puzzle draft card restores the bound agent session and opens the result view', async () => {
test('puzzle draft result back button returns to creation hub', async () => {
const user = userEvent.setup();
vi.mocked(listPuzzleWorks).mockResolvedValue({
@@ -2913,9 +2908,12 @@ test('puzzle draft card restores the bound agent session and opens the result vi
await user.click(screen.getByRole('button', { name: '返回' }));
expect(
await screen.findByText('雨夜里有一只会发光的猫站在遗迹台阶上。'),
await screen.findByRole('button', { name: /.*/u }),
).toBeTruthy();
expect(screen.queryByText('拼图玩法共创')).toBeNull();
expect(
screen.queryByText('雨夜里有一只会发光的猫站在遗迹台阶上。'),
).toBeNull();
expect(screen.queryByText('拼图结果页')).toBeNull();
});
test('published puzzle work card restores its source session for editing', async () => {
@@ -4361,7 +4359,9 @@ test('agent draft result back button returns to creation hub without syncing res
await user.click(screen.getByRole('button', { name: //u }));
await waitFor(() => {
expect(screen.getByText('角色扮演')).toBeTruthy();
expect(
screen.getByRole('button', { name: /.*/u }),
).toBeTruthy();
});
expect(
@@ -4677,13 +4677,17 @@ test('manual tab switch is preserved after platform bootstrap requests finish',
render(<TestWrapper withAuth />);
await clickFirstButtonByName(user, '创作');
expect(await screen.findByText('角色扮演')).toBeTruthy();
expect(
await screen.findByRole('button', { name: /.*/u }),
).toBeTruthy();
resolveGalleryRequest([]);
await waitFor(() => {
expect(
within(getPlatformTabPanel('create')).getByText('角色扮演'),
within(getPlatformTabPanel('create')).getByRole('button', {
name: /.*/u,
}),
).toBeTruthy();
});
@@ -5045,9 +5049,22 @@ test('creation hub published work card keeps delete action guarded by detail flo
render(<TestWrapper withAuth />);
await openCreationHub(user);
await clickFirstButtonByName(user, '创作');
expect(await screen.findByRole('button', { name: //u })).toBeTruthy();
expect(screen.getByRole('button', { name: '删除' })).toBeTruthy();
await user.click(screen.getByRole('button', { name: '删除' }));
const dialog = await screen.findByRole('dialog', { name: '删除作品' });
expect(dialog.parentElement?.className).toContain('platform-theme--light');
expect(dialog.parentElement?.className).toContain('!items-center');
expect(dialog.className).toContain('platform-modal-shell');
expect(dialog.className).toContain('platform-remap-surface');
expect(dialog.className).toContain('rounded-[1.75rem]');
expect(
within(dialog).getByText('确认删除《潮雾列岛》吗?'),
).toBeTruthy();
expect(
within(dialog).getByRole('button', { name: '确认删除' }),
).toBeTruthy();
expect(deleteRpgEntryWorldProfile).not.toHaveBeenCalled();
});