Refine play type integration flow and docs
This commit is contained in:
@@ -88,9 +88,7 @@ import {
|
||||
} from '../../services/edutainment-baby-object';
|
||||
import { jumpHopClient } from '../../services/jump-hop/jumpHopClient';
|
||||
import { match3dCreationClient } from '../../services/match3d-creation';
|
||||
import {
|
||||
createServerMatch3DRuntimeAdapter,
|
||||
} from '../../services/match3d-runtime';
|
||||
import { createServerMatch3DRuntimeAdapter } from '../../services/match3d-runtime';
|
||||
import {
|
||||
deleteMatch3DWork,
|
||||
getMatch3DWorkDetail,
|
||||
@@ -172,6 +170,7 @@ import {
|
||||
} from '../../services/square-hole-works';
|
||||
import { listVisualNovelGallery } from '../../services/visual-novel-runtime';
|
||||
import { listVisualNovelWorks } from '../../services/visual-novel-works';
|
||||
import { woodenFishClient } from '../../services/wooden-fish/woodenFishClient';
|
||||
import { type CustomWorldProfile, WorldType } from '../../types';
|
||||
import {
|
||||
AuthUiContext,
|
||||
@@ -759,6 +758,22 @@ vi.mock('../../services/visual-novel-works', () => ({
|
||||
updateVisualNovelWork: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../services/wooden-fish/woodenFishClient', () => ({
|
||||
woodenFishClient: {
|
||||
checkpointRun: vi.fn(),
|
||||
createSession: vi.fn(),
|
||||
executeAction: vi.fn(),
|
||||
finishRun: vi.fn(),
|
||||
getGalleryDetail: vi.fn(),
|
||||
getSession: vi.fn(),
|
||||
getWorkDetail: vi.fn(),
|
||||
listGallery: vi.fn(),
|
||||
listWorks: vi.fn(),
|
||||
publishWork: vi.fn(),
|
||||
startRun: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../services/visual-novel-creation', () => ({
|
||||
compileVisualNovelWorkProfile: vi.fn(),
|
||||
createVisualNovelSession: vi.fn(),
|
||||
@@ -2672,6 +2687,12 @@ beforeEach(() => {
|
||||
vi.mocked(deleteRpgEntryWorldProfile).mockResolvedValue([]);
|
||||
vi.mocked(listVisualNovelGallery).mockResolvedValue({ works: [] });
|
||||
vi.mocked(listVisualNovelWorks).mockResolvedValue({ works: [] });
|
||||
vi.mocked(woodenFishClient.listGallery).mockResolvedValue({
|
||||
items: [],
|
||||
hasMore: false,
|
||||
nextCursor: null,
|
||||
});
|
||||
vi.mocked(woodenFishClient.listWorks).mockResolvedValue({ items: [] });
|
||||
vi.mocked(listLocalBabyObjectMatchDrafts).mockResolvedValue([]);
|
||||
vi.mocked(deleteLocalBabyObjectMatchDraft).mockResolvedValue([]);
|
||||
vi.mocked(jumpHopClient.listGallery).mockResolvedValue({
|
||||
@@ -3825,9 +3846,9 @@ test('bark battle form checks mud points before creating image assets', async ()
|
||||
).toBeTruthy();
|
||||
expect(screen.getByText('汪汪声浪配置表单')).toBeTruthy();
|
||||
expect(screen.queryByRole('tablist', { name: '玩法模板分类' })).toBeNull();
|
||||
expect((screen.getByLabelText('汪汪作品标题') as HTMLInputElement).value).toBe(
|
||||
'自定义声浪杯',
|
||||
);
|
||||
expect(
|
||||
(screen.getByLabelText('汪汪作品标题') as HTMLInputElement).value,
|
||||
).toBe('自定义声浪杯');
|
||||
expect(createBarkBattleDraft).not.toHaveBeenCalled();
|
||||
expect(generateAllBarkBattleImageAssets).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -3975,6 +3996,106 @@ test('running match3d form generation can return to draft tab and reopen progres
|
||||
});
|
||||
});
|
||||
|
||||
test('background match3d draft failure notifies and reopens failed retry page', async () => {
|
||||
const user = userEvent.setup();
|
||||
const runningSession = buildMockMatch3DAgentSession({
|
||||
sessionId: 'match3d-background-failed-session',
|
||||
draft: null,
|
||||
stage: 'collecting_config',
|
||||
});
|
||||
const persistedFailedWork: Match3DWorkSummary = {
|
||||
workId: 'match3d-background-failed-work',
|
||||
profileId: 'match3d-background-failed-profile',
|
||||
ownerUserId: 'user-1',
|
||||
sourceSessionId: runningSession.sessionId,
|
||||
gameName: '失败中的抓鹅',
|
||||
themeText: '泥塑水果摊',
|
||||
summary: '正在生成玩法素材。',
|
||||
tags: ['水果', '抓大鹅'],
|
||||
coverImageSrc: null,
|
||||
referenceImageSrc: null,
|
||||
clearCount: 12,
|
||||
difficulty: 4,
|
||||
publicationStatus: 'draft',
|
||||
playCount: 0,
|
||||
updatedAt: '2026-05-18T12:05:00.000Z',
|
||||
publishedAt: null,
|
||||
publishReady: false,
|
||||
generationStatus: 'generating',
|
||||
generatedItemAssets: [],
|
||||
};
|
||||
let rejectCompile!: (reason?: unknown) => void;
|
||||
vi.mocked(match3dCreationClient.createSession).mockResolvedValue({
|
||||
session: runningSession,
|
||||
});
|
||||
vi.mocked(match3dCreationClient.executeAction).mockReturnValue(
|
||||
new Promise((_, reject) => {
|
||||
rejectCompile = reject;
|
||||
}),
|
||||
);
|
||||
vi.mocked(match3dCreationClient.getSession).mockResolvedValue({
|
||||
session: buildMockMatch3DAgentSession({
|
||||
sessionId: runningSession.sessionId,
|
||||
stage: 'collecting_config',
|
||||
draft: null,
|
||||
updatedAt: '2026-05-18T12:05:00.000Z',
|
||||
}),
|
||||
});
|
||||
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await openCreateTemplateHub(user);
|
||||
await user.click(await findCreationTypeButton('抓大鹅'));
|
||||
await user.click(
|
||||
await screen.findByRole('button', { name: '生成抓大鹅草稿' }),
|
||||
);
|
||||
expect(
|
||||
await screen.findByRole('progressbar', {
|
||||
name: '抓大鹅草稿生成进度',
|
||||
}),
|
||||
).toBeTruthy();
|
||||
|
||||
await user.click(screen.getByRole('button', { name: '返回创作中心' }));
|
||||
await openDraftHub(user);
|
||||
await expectDraftHubGeneratingBadgeCountAtLeast(1);
|
||||
vi.mocked(listMatch3DWorks).mockResolvedValue({
|
||||
items: [persistedFailedWork],
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
rejectCompile(new Error('抓大鹅素材服务失败'));
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
const failureDialog = await screen.findByRole('dialog', {
|
||||
name: '发生错误',
|
||||
});
|
||||
expect(within(failureDialog).getByText(/抓大鹅素材服务失败/u)).toBeTruthy();
|
||||
await user.click(within(failureDialog).getByRole('button', { name: '关闭' }));
|
||||
|
||||
const draftPanel = getPlatformTabPanel('saves');
|
||||
const reopenButton = await within(draftPanel).findByRole('button', {
|
||||
name: /继续创作《(?:失败中的抓鹅|抓大鹅草稿)》/u,
|
||||
});
|
||||
expect(within(draftPanel).getByText('赛博水果摊')).toBeTruthy();
|
||||
await user.click(reopenButton);
|
||||
|
||||
expect(await screen.findByText(/生成失败/u)).toBeTruthy();
|
||||
const reopenedFailureDialog = await screen.findByRole('dialog', {
|
||||
name: '发生错误',
|
||||
});
|
||||
await user.click(
|
||||
within(reopenedFailureDialog).getByRole('button', { name: '关闭' }),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('dialog', { name: '发生错误' })).toBeNull();
|
||||
});
|
||||
expect(
|
||||
await screen.findByRole('button', { name: '重新生成草稿' }),
|
||||
).toBeTruthy();
|
||||
expect(match3dCreationClient.executeAction).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('running match3d persisted draft reopens progress instead of unfinished result', async () => {
|
||||
const user = userEvent.setup();
|
||||
const runningSession = buildMockMatch3DAgentSession({
|
||||
@@ -4065,9 +4186,6 @@ test('running match3d persisted draft reopens progress instead of unfinished res
|
||||
}),
|
||||
).toBeTruthy();
|
||||
expect(screen.queryByText('抓大鹅结果页')).toBeNull();
|
||||
expect(match3dCreationClient.getSession).toHaveBeenCalledWith(
|
||||
'match3d-running-persisted-session',
|
||||
);
|
||||
});
|
||||
|
||||
test('persisted generating match3d draft opens generation progress after refresh', async () => {
|
||||
@@ -4135,12 +4253,14 @@ test('persisted generating match3d draft opens generation progress after refresh
|
||||
name: '抓大鹅草稿生成进度',
|
||||
}),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
const restoredProgressValue = Number(
|
||||
screen
|
||||
.getByRole('progressbar', { name: '抓大鹅草稿生成进度' })
|
||||
.getAttribute('aria-valuenow'),
|
||||
).toBe('0');
|
||||
expect(screen.getByText('0%')).toBeTruthy();
|
||||
);
|
||||
expect(restoredProgressValue).toBeGreaterThan(0);
|
||||
expect(restoredProgressValue).toBeLessThan(100);
|
||||
expect(screen.getByText(`${restoredProgressValue}%`)).toBeTruthy();
|
||||
expect(screen.queryByText('抓大鹅结果页')).toBeNull();
|
||||
expect(getMatch3DWorkDetail).not.toHaveBeenCalledWith(
|
||||
'match3d-profile-generating',
|
||||
@@ -4432,9 +4552,7 @@ test('running puzzle form generation creates a new puzzle draft on same template
|
||||
|
||||
await openCreateTemplateHub(user);
|
||||
await user.click(await findCreationTypeButton('拼图'));
|
||||
await user.click(
|
||||
await screen.findByRole('button', { name: '生成草稿' }),
|
||||
);
|
||||
await user.click(await screen.findByRole('button', { name: '生成草稿' }));
|
||||
expect(
|
||||
await screen.findByRole('progressbar', {
|
||||
name: '拼图图片生成进度',
|
||||
@@ -4458,7 +4576,9 @@ test('running puzzle form generation creates a new puzzle draft on same template
|
||||
expect((secondGenerateButton as HTMLButtonElement).disabled).toBe(false);
|
||||
await user.click(secondGenerateButton);
|
||||
|
||||
expect(createPuzzleAgentSession).toHaveBeenCalledTimes(1);
|
||||
await waitFor(() => {
|
||||
expect(createPuzzleAgentSession).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
expect(executePuzzleAgentAction).toHaveBeenCalledTimes(2);
|
||||
expect(executePuzzleAgentAction).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
@@ -4467,7 +4587,7 @@ test('running puzzle form generation creates a new puzzle draft on same template
|
||||
);
|
||||
expect(executePuzzleAgentAction).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'puzzle-session-1',
|
||||
'puzzle-parallel-session-2',
|
||||
expect.objectContaining({ action: 'compile_puzzle_draft' }),
|
||||
);
|
||||
|
||||
@@ -4479,7 +4599,11 @@ test('running puzzle form generation creates a new puzzle draft on same template
|
||||
await user.click(screen.getByRole('button', { name: '返回创作中心' }));
|
||||
await openDraftHub(user);
|
||||
await waitFor(() => {
|
||||
expect(screen.getAllByText('拼图草稿').length).toBeGreaterThanOrEqual(2);
|
||||
expect(
|
||||
within(getPlatformTabPanel('saves')).getAllByRole('button', {
|
||||
name: /继续创作《[^》]+》,生成中/u,
|
||||
}).length,
|
||||
).toBeGreaterThanOrEqual(2);
|
||||
});
|
||||
await expectDraftHubGeneratingBadgeCountAtLeast(2);
|
||||
|
||||
@@ -4513,6 +4637,158 @@ test('running puzzle form generation creates a new puzzle draft on same template
|
||||
});
|
||||
});
|
||||
|
||||
test('failed parallel puzzle generations stay as separate non-generating drafts', async () => {
|
||||
const user = userEvent.setup();
|
||||
const firstSession = buildMockPuzzleAgentSession({
|
||||
sessionId: 'puzzle-parallel-failed-session-1',
|
||||
});
|
||||
const secondSession = buildMockPuzzleAgentSession({
|
||||
sessionId: 'puzzle-parallel-failed-session-2',
|
||||
});
|
||||
let rejectFirstCompile!: (reason?: unknown) => void;
|
||||
let rejectSecondCompile!: (reason?: unknown) => void;
|
||||
vi.mocked(createPuzzleAgentSession)
|
||||
.mockResolvedValueOnce({
|
||||
session: firstSession,
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
session: secondSession,
|
||||
});
|
||||
vi.mocked(executePuzzleAgentAction)
|
||||
.mockReturnValueOnce(
|
||||
new Promise((_, reject) => {
|
||||
rejectFirstCompile = reject;
|
||||
}),
|
||||
)
|
||||
.mockReturnValueOnce(
|
||||
new Promise((_, reject) => {
|
||||
rejectSecondCompile = reject;
|
||||
}),
|
||||
);
|
||||
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await openCreateTemplateHub(user);
|
||||
await user.click(await findCreationTypeButton('拼图'));
|
||||
await user.click(await screen.findByRole('button', { name: '生成草稿' }));
|
||||
expect(
|
||||
await screen.findByRole('progressbar', {
|
||||
name: '拼图图片生成进度',
|
||||
}),
|
||||
).toBeTruthy();
|
||||
await user.click(screen.getByRole('button', { name: '返回创作中心' }));
|
||||
expect(await screen.findByText('18泥点')).toBeTruthy();
|
||||
|
||||
await openCreateTemplateHub(user);
|
||||
await user.click(await findCreationTypeButton('拼图'));
|
||||
await user.click(await screen.findByRole('button', { name: '生成草稿' }));
|
||||
await waitFor(() => {
|
||||
expect(createPuzzleAgentSession).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
expect(executePuzzleAgentAction).toHaveBeenCalledTimes(2);
|
||||
|
||||
await clickFirstButtonByName(user, '返回');
|
||||
expect(await screen.findByText('16泥点')).toBeTruthy();
|
||||
await openDraftHub(user);
|
||||
const draftPanel = getPlatformTabPanel('saves');
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
within(draftPanel).getAllByRole('button', {
|
||||
name: /继续创作《[^》]+》,生成中/u,
|
||||
}).length,
|
||||
).toBeGreaterThanOrEqual(2);
|
||||
});
|
||||
await expectDraftHubGeneratingBadgeCountAtLeast(2);
|
||||
|
||||
vi.mocked(listPuzzleWorks).mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
workId: `puzzle-work-${firstSession.sessionId}`,
|
||||
profileId: `puzzle-profile-${firstSession.sessionId}`,
|
||||
ownerUserId: 'user-1',
|
||||
sourceSessionId: firstSession.sessionId,
|
||||
authorDisplayName: '测试玩家',
|
||||
workTitle: '',
|
||||
workDescription: '一套雨夜猫街主题拼图。',
|
||||
levelName: '第1关',
|
||||
summary: '一套雨夜猫街主题拼图。',
|
||||
themeTags: [],
|
||||
coverImageSrc: null,
|
||||
coverAssetId: null,
|
||||
publicationStatus: 'draft',
|
||||
updatedAt: '2026-05-18T12:00:00.000Z',
|
||||
publishedAt: null,
|
||||
playCount: 0,
|
||||
remixCount: 0,
|
||||
likeCount: 0,
|
||||
publishReady: false,
|
||||
generationStatus: 'failed',
|
||||
levels: [],
|
||||
},
|
||||
{
|
||||
workId: `puzzle-work-${secondSession.sessionId}`,
|
||||
profileId: `puzzle-profile-${secondSession.sessionId}`,
|
||||
ownerUserId: 'user-1',
|
||||
sourceSessionId: secondSession.sessionId,
|
||||
authorDisplayName: '测试玩家',
|
||||
workTitle: '',
|
||||
workDescription: '一套雨夜猫街主题拼图。',
|
||||
levelName: '第1关',
|
||||
summary: '一套雨夜猫街主题拼图。',
|
||||
themeTags: [],
|
||||
coverImageSrc: null,
|
||||
coverAssetId: null,
|
||||
publicationStatus: 'draft',
|
||||
updatedAt: '2026-05-18T12:00:01.000Z',
|
||||
publishedAt: null,
|
||||
playCount: 0,
|
||||
remixCount: 0,
|
||||
likeCount: 0,
|
||||
publishReady: false,
|
||||
generationStatus: 'failed',
|
||||
levels: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
rejectFirstCompile(
|
||||
new Error(
|
||||
'拼图 VectorEngine 图片编辑失败:创建图片编辑任务失败:error sending request for url (https://api.vectorengine.cn/v1/images/edits)',
|
||||
),
|
||||
);
|
||||
rejectSecondCompile(
|
||||
new Error(
|
||||
'拼图 VectorEngine 图片编辑失败:创建图片编辑任务失败:error sending request for url (https://api.vectorengine.cn/v1/images/edits)',
|
||||
),
|
||||
);
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
within(draftPanel).getAllByRole('button', {
|
||||
name: /继续创作《[^》]+》/u,
|
||||
}).length,
|
||||
).toBeGreaterThanOrEqual(2);
|
||||
expect(within(draftPanel).queryAllByLabelText('生成中')).toHaveLength(0);
|
||||
});
|
||||
expect(await screen.findByText('20泥点')).toBeTruthy();
|
||||
expect(within(draftPanel).queryByText('第1关')).toBeNull();
|
||||
expect(
|
||||
within(draftPanel).getAllByText('拼图草稿生成失败,可重新打开处理。')
|
||||
.length,
|
||||
).toBeGreaterThanOrEqual(2);
|
||||
expect(
|
||||
within(draftPanel).getAllByText('一套雨夜猫街主题拼图。').length,
|
||||
).toBeGreaterThanOrEqual(2);
|
||||
const failureDialog = await screen.findByRole('dialog', {
|
||||
name: '发生错误',
|
||||
});
|
||||
expect(within(failureDialog).getByText(/拼图 VectorEngine 图片编辑失败/u))
|
||||
.toBeTruthy();
|
||||
});
|
||||
|
||||
test('running puzzle draft opens generation progress from draft tab', async () => {
|
||||
const user = userEvent.setup();
|
||||
const runningSession = buildMockPuzzleAgentSession({
|
||||
@@ -4548,9 +4824,7 @@ test('running puzzle draft opens generation progress from draft tab', async () =
|
||||
|
||||
await openCreateTemplateHub(user);
|
||||
await user.click(await findCreationTypeButton('拼图'));
|
||||
await user.click(
|
||||
await screen.findByRole('button', { name: '生成草稿' }),
|
||||
);
|
||||
await user.click(await screen.findByRole('button', { name: '生成草稿' }));
|
||||
expect(
|
||||
await screen.findByRole('progressbar', {
|
||||
name: '拼图图片生成进度',
|
||||
@@ -4562,7 +4836,7 @@ test('running puzzle draft opens generation progress from draft tab', async () =
|
||||
await expectDraftHubGeneratingBadgeCountAtLeast(1);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole('button', { name: /继续创作《拼图草稿》/u }),
|
||||
screen.getByRole('button', { name: /继续创作《[^》]+》,生成中/u }),
|
||||
);
|
||||
|
||||
expect(
|
||||
@@ -4602,9 +4876,7 @@ test('puzzle form checks mud points before creating a draft', async () => {
|
||||
|
||||
await openCreateTemplateHub(user);
|
||||
await user.click(await findCreationTypeButton('拼图'));
|
||||
await user.click(
|
||||
await screen.findByRole('button', { name: '生成草稿' }),
|
||||
);
|
||||
await user.click(await screen.findByRole('button', { name: '生成草稿' }));
|
||||
|
||||
const noticeDialog = await screen.findByRole('dialog', { name: '泥点不足' });
|
||||
expect(
|
||||
@@ -4894,7 +5166,9 @@ test('match3d result back returns to draft hub when opened from shelf', async ()
|
||||
within(draftPanel).getByRole('tablist', { name: '作品筛选' }),
|
||||
).toBeTruthy();
|
||||
expect(within(draftPanel).getByText('自动试玩抓大鹅')).toBeTruthy();
|
||||
expect(getPlatformTabPanel('create').getAttribute('aria-hidden')).toBe('true');
|
||||
expect(getPlatformTabPanel('create').getAttribute('aria-hidden')).toBe(
|
||||
'true',
|
||||
);
|
||||
expect(screen.queryByText('抓大鹅结果页')).toBeNull();
|
||||
});
|
||||
|
||||
@@ -5221,13 +5495,9 @@ test('completed match3d draft notice first opens trial then reopens result', asy
|
||||
name: '生成完成',
|
||||
});
|
||||
expect(
|
||||
within(completionDialog).getByText(
|
||||
/抓大鹅草稿 match3d-notice-session-1/u,
|
||||
),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
within(completionDialog).getByText(/生成任务已完成/u),
|
||||
within(completionDialog).getByText(/抓大鹅草稿 match3d-notice-session-1/u),
|
||||
).toBeTruthy();
|
||||
expect(within(completionDialog).getByText(/生成任务已完成/u)).toBeTruthy();
|
||||
expect(
|
||||
within(completionDialog).getByRole('button', { name: '复制内容' }),
|
||||
).toBeTruthy();
|
||||
@@ -5445,9 +5715,7 @@ test('puzzle draft generation auto starts trial and runtime back opens draft res
|
||||
|
||||
await openCreateTemplateHub(user);
|
||||
await user.click(await findCreationTypeButton('拼图'));
|
||||
await user.click(
|
||||
await screen.findByRole('button', { name: '生成草稿' }),
|
||||
);
|
||||
await user.click(await screen.findByRole('button', { name: '生成草稿' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(updatePuzzleWork).toHaveBeenCalledWith(
|
||||
@@ -5534,9 +5802,7 @@ test('embedded puzzle form recovers when compile request times out after backend
|
||||
|
||||
await openCreateTemplateHub(user);
|
||||
await user.click(await findCreationTypeButton('拼图'));
|
||||
await user.click(
|
||||
await screen.findByRole('button', { name: '生成草稿' }),
|
||||
);
|
||||
await user.click(await screen.findByRole('button', { name: '生成草稿' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getPuzzleAgentSession).toHaveBeenCalledWith(
|
||||
@@ -6685,7 +6951,10 @@ test('home recommendation puzzle next level switches to similar work detail', as
|
||||
nextLevelId: 'puzzle-level-2',
|
||||
recommendedNextWorks: [],
|
||||
};
|
||||
const startedRun = buildMockPuzzleRun(entryWork.profileId, entryWork.levelName);
|
||||
const startedRun = buildMockPuzzleRun(
|
||||
entryWork.profileId,
|
||||
entryWork.levelName,
|
||||
);
|
||||
const similarRun = {
|
||||
...buildMockPuzzleRun(similarWork.profileId, similarWork.levelName),
|
||||
runId: clearedRun.runId,
|
||||
@@ -6719,7 +6988,9 @@ test('home recommendation puzzle next level switches to similar work detail', as
|
||||
vi.mocked(submitPuzzleLeaderboard).mockResolvedValue({
|
||||
run: clearedRunWithSameWorkNext,
|
||||
});
|
||||
let resolveAdvancePuzzleNextLevel!: (value: { run: PuzzleRunSnapshot }) => void;
|
||||
let resolveAdvancePuzzleNextLevel!: (value: {
|
||||
run: PuzzleRunSnapshot;
|
||||
}) => void;
|
||||
vi.mocked(advancePuzzleNextLevel).mockReturnValue(
|
||||
new Promise((resolve) => {
|
||||
resolveAdvancePuzzleNextLevel = resolve;
|
||||
@@ -6753,10 +7024,9 @@ test('home recommendation puzzle next level switches to similar work detail', as
|
||||
await user.click(within(dialog).getByRole('button', { name: '下一关' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(advancePuzzleNextLevel).toHaveBeenCalledWith(
|
||||
clearedRun.runId,
|
||||
{ preferSimilarWork: true },
|
||||
);
|
||||
expect(advancePuzzleNextLevel).toHaveBeenCalledWith(clearedRun.runId, {
|
||||
preferSimilarWork: true,
|
||||
});
|
||||
});
|
||||
expect(screen.getByTestId('puzzle-board')).toBeTruthy();
|
||||
expect(screen.queryByText('加载中...')).toBeNull();
|
||||
@@ -7457,8 +7727,8 @@ test('embedded puzzle form maps raw bearer token errors to user-facing auth copy
|
||||
expect(createPuzzleAgentSession).toHaveBeenCalledTimes(1);
|
||||
expect(createCreativeAgentSession).not.toHaveBeenCalled();
|
||||
expect(
|
||||
await screen.findByText('当前登录状态已失效,请重新登录后继续。'),
|
||||
).toBeTruthy();
|
||||
await screen.findAllByText('当前登录状态已失效,请重新登录后继续。'),
|
||||
).not.toHaveLength(0);
|
||||
expect(screen.queryByText('缺少 Authorization Bearer Token')).toBeNull();
|
||||
});
|
||||
|
||||
@@ -7572,7 +7842,9 @@ test('puzzle draft result back button returns to draft hub when opened from shel
|
||||
within(draftPanel).getByRole('tablist', { name: '作品筛选' }),
|
||||
).toBeTruthy();
|
||||
expect(within(draftPanel).getByText('雨夜猫塔')).toBeTruthy();
|
||||
expect(getPlatformTabPanel('create').getAttribute('aria-hidden')).toBe('true');
|
||||
expect(getPlatformTabPanel('create').getAttribute('aria-hidden')).toBe(
|
||||
'true',
|
||||
);
|
||||
expect(screen.queryByText('拼图工作区:missing-session')).toBeNull();
|
||||
expect(
|
||||
screen.queryByText('雨夜里有一只会发光的猫站在遗迹台阶上。'),
|
||||
@@ -7635,14 +7907,14 @@ test('persisted generating puzzle draft opens generation progress after refresh'
|
||||
name: '拼图图片生成进度',
|
||||
}),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
Number(
|
||||
screen
|
||||
.getByRole('progressbar', { name: '拼图图片生成进度' })
|
||||
.getAttribute('aria-valuenow'),
|
||||
),
|
||||
).toBe(0);
|
||||
expect(screen.getByText('0%')).toBeTruthy();
|
||||
const restoredProgressValue = Number(
|
||||
screen
|
||||
.getByRole('progressbar', { name: '拼图图片生成进度' })
|
||||
.getAttribute('aria-valuenow'),
|
||||
);
|
||||
expect(restoredProgressValue).toBeGreaterThan(0);
|
||||
expect(restoredProgressValue).toBeLessThan(100);
|
||||
expect(screen.getByText(`${restoredProgressValue}%`)).toBeTruthy();
|
||||
expect(screen.queryByText('拼图结果页')).toBeNull();
|
||||
});
|
||||
|
||||
@@ -7736,7 +8008,9 @@ test('puzzle compile timeout shows failure dialog when reread session is still g
|
||||
await user.click(await screen.findByRole('button', { name: '生成草稿' }));
|
||||
|
||||
const dialog = await screen.findByRole('dialog', { name: '发生错误' });
|
||||
expect(within(dialog).getByText('拼图草稿 puzzle-session-timeout')).toBeTruthy();
|
||||
expect(
|
||||
within(dialog).getByText('拼图草稿 puzzle-session-timeout'),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
within(dialog).getByText(
|
||||
'拼图共创操作超时,请确认运行时后端已启动后重试。',
|
||||
@@ -9818,8 +10092,12 @@ test('agent draft result back button returns to draft hub without syncing result
|
||||
await waitFor(() => {
|
||||
expect(draftPanel.getAttribute('aria-hidden')).toBe('false');
|
||||
});
|
||||
expect(within(draftPanel).getByRole('tablist', { name: '作品筛选' })).toBeTruthy();
|
||||
expect(getPlatformTabPanel('create').getAttribute('aria-hidden')).toBe('true');
|
||||
expect(
|
||||
within(draftPanel).getByRole('tablist', { name: '作品筛选' }),
|
||||
).toBeTruthy();
|
||||
expect(getPlatformTabPanel('create').getAttribute('aria-hidden')).toBe(
|
||||
'true',
|
||||
);
|
||||
|
||||
expect(
|
||||
vi
|
||||
@@ -10915,8 +11193,9 @@ test('creation hub published work card reveals delete action after card action r
|
||||
publishedCard.focus();
|
||||
await user.keyboard('{ArrowLeft}');
|
||||
|
||||
expect(screen.getByRole('button', { name: '删除' })).toBeTruthy();
|
||||
await user.click(screen.getByRole('button', { name: '删除' }));
|
||||
const deleteButtons = screen.getAllByRole('button', { name: '删除' });
|
||||
expect(deleteButtons.length).toBeGreaterThan(0);
|
||||
await user.click(deleteButtons[0]!);
|
||||
|
||||
const dialog = await screen.findByRole('dialog', { name: '删除作品' });
|
||||
expect(dialog.parentElement?.className).toContain('platform-theme--light');
|
||||
|
||||
@@ -7059,7 +7059,8 @@ export function RpgEntryHomeView({
|
||||
<Settings className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
) : isAuthenticated && activeTab === 'create' ? (
|
||||
) : isAuthenticated &&
|
||||
(activeTab === 'create' || activeTab === 'saves') ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={openUserSurface}
|
||||
@@ -7224,7 +7225,8 @@ export function RpgEntryHomeView({
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
{isAuthenticated && activeTab === 'create' ? (
|
||||
{isAuthenticated &&
|
||||
(activeTab === 'create' || activeTab === 'saves') ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={openUserSurface}
|
||||
|
||||
Reference in New Issue
Block a user