feat: 平台错误与完成弹窗收口
This commit is contained in:
@@ -24,6 +24,14 @@
|
||||
- 验证方式:`npm run test -- src/components/platform-entry/PlatformErrorDialog.test.tsx src/components/platform-entry/PlatformEntryCreationTypeModal.test.tsx`、`npm run typecheck`、`npm run check:encoding` 通过;手测时异步失败应弹出包含“错误来源”和“错误内容”的弹窗,复制按钮应复制完整诊断文本。
|
||||
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
|
||||
|
||||
## 2026-05-26 生成任务完成在离开生成页后弹独立完成弹窗
|
||||
|
||||
- 背景:抓大鹅、拼图等生成任务完成时,用户如果已经离开生成页,草稿页的未读红点不足以表达“这次生成已完成”;但如果用户仍停留在生成页,结果页或试玩页本身就是完成反馈,不需要再叠一个成功提示。
|
||||
- 决策:平台壳层在 `markDraftReady(..., viewedImmediately=false)` 时额外弹出 `PlatformTaskCompletionDialog`,完成弹窗必须带来源和复制按钮;如果 `viewedImmediately=true`,只保留结果页 / 试玩页本身的完成反馈和草稿未读态,不重复弹窗。
|
||||
- 影响范围:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/platform-entry/PlatformTaskCompletionDialog.tsx`、`src/components/platform-entry/PlatformErrorDialog.test.tsx`、`src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
|
||||
- 验证方式:`npm run test -- src/components/platform-entry/PlatformErrorDialog.test.tsx`、`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "completed match3d draft"` 通过后,离开生成页再完成的草稿应出现“生成完成”弹窗,且复制内容包含来源与状态。
|
||||
- 关联文档:`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
|
||||
|
||||
## 2026-05-26 “我的”页任务卡读后端任务摘要并移除常驻填邀请码入口
|
||||
|
||||
- 背景:移动端“我的”页每日任务卡曾硬编码 `0 / 1`,任务领取完成后只刷新弹窗内任务中心,卡片本身不更新;页面底部还保留旧的“填邀请码”次级按钮,和当前五项常用功能宫格口径重复。
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
平台入口、生成页、结果页、作品详情、作品架和运行态的跨流程错误统一收口到 `PlatformErrorDialog`。弹窗必须带明确错误来源,例如某个草稿、某次生成、作品详情或某个游玩实例,并提供复制按钮复制“错误来源 + 错误内容”。页面内不再重复渲染裸错误 banner;表单校验、发布确认弹窗里的局部业务错误可以保留在原弹窗内。
|
||||
|
||||
生成任务在用户离开生成页后异步完成时,平台壳层必须弹出 `PlatformTaskCompletionDialog`。完成弹窗同样要带来源,例如某个草稿或生成会话,并提供复制按钮复制“来源 + 状态”;如果用户仍停留在生成页并被自动带入结果页或试玩页,生成页 / 结果页本身即为完成反馈,不再额外叠加完成弹窗。
|
||||
|
||||
`PlatformEntryFlowShellImpl.tsx` 仍是平台入口编排壳,后续维护时应优先把独立 UI 片段、公开作品映射、草稿生成 notice 和运行态状态 helper 拆到 `src/components/platform-entry/PlatformEntryFlowShellImpl/` 或同目录紧邻 helper 文件。拆分只允许改变文件组织,不改变入口配置事实源、默认导出、props、页面阶段、UI 文案或现有交互;其中拼图首访 onboarding 已拆为 `PlatformEntryFlowShellImpl/PuzzleOnboardingView.tsx`。
|
||||
|
||||
`platformEntryCreationTypes.ts` 只做前端展示派生,分组时必须把后端 `creationTypes` 里的 `categoryId` / `categoryLabel` 当作可缺失字段处理,空值统一回退到 `recent` / `最近创作`,避免旧数据、局部 mock 或异常返回把创作入口初始化直接打崩。
|
||||
|
||||
@@ -429,6 +429,10 @@ import {
|
||||
PlatformErrorDialog,
|
||||
type PlatformErrorDialogPayload,
|
||||
} from './PlatformErrorDialog';
|
||||
import {
|
||||
PlatformTaskCompletionDialog,
|
||||
type PlatformTaskCompletionDialogPayload,
|
||||
} from './PlatformTaskCompletionDialog';
|
||||
import { PlatformFeedbackView } from './PlatformFeedbackView';
|
||||
import { PlatformWorkDetailView } from './PlatformWorkDetailView';
|
||||
import { usePlatformCreationAgentFlowController } from './usePlatformCreationAgentFlowController';
|
||||
@@ -444,6 +448,7 @@ type DraftGenerationNoticeStatus = 'generating' | 'ready';
|
||||
type DraftGenerationNotice = {
|
||||
status: DraftGenerationNoticeStatus;
|
||||
seen: boolean;
|
||||
completedAtMs?: number;
|
||||
};
|
||||
type DraftGenerationNoticeMap = Record<string, DraftGenerationNotice>;
|
||||
type CreationWorkShelfKind = CreationWorkShelfItem['kind'];
|
||||
@@ -2027,12 +2032,74 @@ function formatPlatformErrorSource(label: string, id?: string | null) {
|
||||
return normalizedId ? `${label} ${normalizedId}` : label;
|
||||
}
|
||||
|
||||
function formatPlatformTaskCompletionSource(label: string, id?: string | null) {
|
||||
const normalizedId = id?.trim();
|
||||
return normalizedId ? `${label} ${normalizedId}` : label;
|
||||
}
|
||||
|
||||
function buildPlatformErrorDialogDismissKey(
|
||||
error: (PlatformErrorDialogPayload & { key: string }) | null,
|
||||
) {
|
||||
return error ? `${error.key}:${error.source}:${error.message}` : null;
|
||||
}
|
||||
|
||||
function buildPlatformTaskCompletionDialogDismissKey(
|
||||
completion:
|
||||
| (PlatformTaskCompletionDialogPayload & {
|
||||
key: string;
|
||||
completedAtMs: number | null;
|
||||
})
|
||||
| null,
|
||||
) {
|
||||
return completion
|
||||
? `${completion.key}:${completion.source}:${completion.message}:${completion.completedAtMs ?? 0}`
|
||||
: null;
|
||||
}
|
||||
|
||||
function pickDraftCompletionDialogSourceId(
|
||||
ids: Array<string | null | undefined>,
|
||||
) {
|
||||
const normalizedIds = ids
|
||||
.map((id) => id?.trim() ?? '')
|
||||
.filter((id) => Boolean(id));
|
||||
return (
|
||||
normalizedIds.find((id) => /session/i.test(id)) ??
|
||||
normalizedIds.find((id) => /work/i.test(id)) ??
|
||||
normalizedIds.find((id) => /draft/i.test(id)) ??
|
||||
normalizedIds.find((id) => /run/i.test(id)) ??
|
||||
normalizedIds.find((id) => /profile/i.test(id)) ??
|
||||
normalizedIds[0] ??
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
function buildDraftCompletionDialogSource(
|
||||
kind: CreationWorkShelfKind,
|
||||
ids: Array<string | null | undefined>,
|
||||
) {
|
||||
const sourceId = pickDraftCompletionDialogSourceId(ids);
|
||||
switch (kind) {
|
||||
case 'rpg':
|
||||
return formatPlatformTaskCompletionSource('RPG 草稿', sourceId);
|
||||
case 'big-fish':
|
||||
return formatPlatformTaskCompletionSource('大鱼吃小鱼草稿', sourceId);
|
||||
case 'match3d':
|
||||
return formatPlatformTaskCompletionSource('抓大鹅草稿', sourceId);
|
||||
case 'square-hole':
|
||||
return formatPlatformTaskCompletionSource('方洞挑战草稿', sourceId);
|
||||
case 'jump-hop':
|
||||
return formatPlatformTaskCompletionSource('跳一跳草稿', sourceId);
|
||||
case 'puzzle':
|
||||
return formatPlatformTaskCompletionSource('拼图草稿', sourceId);
|
||||
case 'visual-novel':
|
||||
return formatPlatformTaskCompletionSource('视觉小说草稿', sourceId);
|
||||
case 'bark-battle':
|
||||
return formatPlatformTaskCompletionSource('汪汪声浪草稿', sourceId);
|
||||
case 'baby-object-match':
|
||||
return formatPlatformTaskCompletionSource('宝贝识物草稿', sourceId);
|
||||
}
|
||||
}
|
||||
|
||||
function createMiniGameDraftGenerationStateForRestoredDraft(
|
||||
kind: MiniGameDraftGenerationKind,
|
||||
metadata?: MiniGameDraftGenerationState['metadata'],
|
||||
@@ -3327,6 +3394,16 @@ export function PlatformEntryFlowShellImpl({
|
||||
useState<DraftGenerationNoticeMap>({});
|
||||
const [pendingDraftShelfItems, setPendingDraftShelfItems] =
|
||||
useState<PendingDraftShelfMap>({});
|
||||
const [
|
||||
pendingPlatformTaskCompletionDialog,
|
||||
setPendingPlatformTaskCompletionDialog,
|
||||
] = useState<
|
||||
| (PlatformTaskCompletionDialogPayload & {
|
||||
key: string;
|
||||
completedAtMs: number | null;
|
||||
})
|
||||
| null
|
||||
>(null);
|
||||
const [initialCreationUrlState] = useState(() => readCreationUrlState());
|
||||
const handledInitialCreationUrlStateRef = useRef(false);
|
||||
const [initialPuzzleRuntimeUrlState] = useState(() =>
|
||||
@@ -3404,10 +3481,14 @@ export function PlatformEntryFlowShellImpl({
|
||||
return;
|
||||
}
|
||||
|
||||
const completedAtMs = status === 'ready' ? Date.now() : undefined;
|
||||
setDraftGenerationNotices((current) => {
|
||||
const next = { ...current };
|
||||
for (const key of uniqueKeys) {
|
||||
next[key] = { status, seen };
|
||||
next[key] =
|
||||
completedAtMs === undefined
|
||||
? { status, seen }
|
||||
: { status, seen, completedAtMs };
|
||||
}
|
||||
return next;
|
||||
});
|
||||
@@ -3449,12 +3530,13 @@ export function PlatformEntryFlowShellImpl({
|
||||
);
|
||||
const markDraftGenerating = useCallback(
|
||||
(kind: CreationWorkShelfKind, ids: Array<string | null | undefined>) => {
|
||||
setPendingPlatformTaskCompletionDialog(null);
|
||||
updateDraftGenerationNotices(
|
||||
collectDraftNoticeKeys(kind, ids),
|
||||
'generating',
|
||||
);
|
||||
},
|
||||
[updateDraftGenerationNotices],
|
||||
[setPendingPlatformTaskCompletionDialog, updateDraftGenerationNotices],
|
||||
);
|
||||
const markDraftReady = useCallback(
|
||||
(
|
||||
@@ -3467,17 +3549,27 @@ export function PlatformEntryFlowShellImpl({
|
||||
'ready',
|
||||
viewedImmediately,
|
||||
);
|
||||
if (!viewedImmediately) {
|
||||
const completedAtMs = Date.now();
|
||||
setPendingPlatformTaskCompletionDialog({
|
||||
key: `${kind}:${collectDraftNoticeKeys(kind, ids).join('|')}:${completedAtMs}`,
|
||||
source: buildDraftCompletionDialogSource(kind, ids),
|
||||
message: '生成任务已完成,可以继续查看草稿。',
|
||||
completedAtMs,
|
||||
});
|
||||
}
|
||||
},
|
||||
[updateDraftGenerationNotices],
|
||||
[setPendingPlatformTaskCompletionDialog, updateDraftGenerationNotices],
|
||||
);
|
||||
const markPendingDraftGenerating = useCallback(
|
||||
(
|
||||
kind: Exclude<CreationWorkShelfKind, 'rpg'>,
|
||||
id: string | null | undefined,
|
||||
) => {
|
||||
setPendingPlatformTaskCompletionDialog(null);
|
||||
updatePendingDraftShelfItem(kind, id, 'generating');
|
||||
},
|
||||
[updatePendingDraftShelfItem],
|
||||
[setPendingPlatformTaskCompletionDialog, updatePendingDraftShelfItem],
|
||||
);
|
||||
const markPendingDraftReady = useCallback(
|
||||
(
|
||||
@@ -5790,6 +5882,10 @@ export function PlatformEntryFlowShellImpl({
|
||||
);
|
||||
const [dismissedPlatformErrorDialogKey, setDismissedPlatformErrorDialogKey] =
|
||||
useState<string | null>(null);
|
||||
const [
|
||||
dismissedPlatformTaskCompletionDialogKey,
|
||||
setDismissedPlatformTaskCompletionDialogKey,
|
||||
] = useState<string | null>(null);
|
||||
const currentPlatformErrorDialog = useMemo<
|
||||
(PlatformErrorDialogPayload & { key: string }) | null
|
||||
>(() => {
|
||||
@@ -6013,6 +6109,25 @@ export function PlatformEntryFlowShellImpl({
|
||||
woodenFishRun?.runId,
|
||||
woodenFishSession?.sessionId,
|
||||
]);
|
||||
const currentPlatformTaskCompletionDialog = useMemo<
|
||||
| (PlatformTaskCompletionDialogPayload & {
|
||||
key: string;
|
||||
completedAtMs: number | null;
|
||||
})
|
||||
| null
|
||||
>(() => pendingPlatformTaskCompletionDialog, [
|
||||
pendingPlatformTaskCompletionDialog,
|
||||
]);
|
||||
const activePlatformTaskCompletionDialogDismissKey =
|
||||
buildPlatformTaskCompletionDialogDismissKey(
|
||||
currentPlatformTaskCompletionDialog,
|
||||
);
|
||||
const activePlatformTaskCompletionDialog =
|
||||
activePlatformTaskCompletionDialogDismissKey &&
|
||||
activePlatformTaskCompletionDialogDismissKey ===
|
||||
dismissedPlatformTaskCompletionDialogKey
|
||||
? null
|
||||
: currentPlatformTaskCompletionDialog;
|
||||
const activePlatformErrorDialogDismissKey =
|
||||
buildPlatformErrorDialogDismissKey(currentPlatformErrorDialog);
|
||||
const activePlatformErrorDialog =
|
||||
@@ -6118,6 +6233,19 @@ export function PlatformEntryFlowShellImpl({
|
||||
setSquareHoleError,
|
||||
setVisualNovelError,
|
||||
]);
|
||||
const closePlatformTaskCompletionDialog = useCallback(() => {
|
||||
if (!currentPlatformTaskCompletionDialog) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dismissKey = buildPlatformTaskCompletionDialogDismissKey(
|
||||
currentPlatformTaskCompletionDialog,
|
||||
);
|
||||
if (dismissKey) {
|
||||
setDismissedPlatformTaskCompletionDialogKey(dismissKey);
|
||||
}
|
||||
setPendingPlatformTaskCompletionDialog(null);
|
||||
}, [currentPlatformTaskCompletionDialog]);
|
||||
const shouldPollPuzzleGenerationSession =
|
||||
selectionStage === 'puzzle-generating' &&
|
||||
activePuzzleGenerationSessionId != null &&
|
||||
@@ -7116,6 +7244,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
setIsProfilePlayStatsOpen(false);
|
||||
setDraftGenerationNotices({});
|
||||
setPendingDraftShelfItems({});
|
||||
setPendingPlatformTaskCompletionDialog(null);
|
||||
resetRpgSessionViewState();
|
||||
setRpgGeneratedCustomWorldProfile(null);
|
||||
setRpgCustomWorldError(null);
|
||||
@@ -16871,6 +17000,12 @@ export function PlatformEntryFlowShellImpl({
|
||||
overlayClassName={`platform-theme ${platformThemeClass} !items-center`}
|
||||
panelClassName="platform-remap-surface rounded-[1.5rem]"
|
||||
/>
|
||||
<PlatformTaskCompletionDialog
|
||||
completion={activePlatformTaskCompletionDialog}
|
||||
onClose={closePlatformTaskCompletionDialog}
|
||||
overlayClassName={`platform-theme ${platformThemeClass} !items-center`}
|
||||
panelClassName="platform-remap-surface rounded-[1.5rem]"
|
||||
/>
|
||||
<UnifiedModal
|
||||
open={Boolean(pendingDeleteCreationWork)}
|
||||
title="删除作品"
|
||||
|
||||
@@ -11,6 +11,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import * as clipboardService from '../../services/clipboard';
|
||||
import { PlatformErrorDialog } from './PlatformErrorDialog';
|
||||
import { PlatformTaskCompletionDialog } from './PlatformTaskCompletionDialog';
|
||||
|
||||
vi.mock('../../services/clipboard', () => ({
|
||||
copyTextToClipboard: vi.fn(),
|
||||
@@ -58,3 +59,49 @@ describe('PlatformErrorDialog', () => {
|
||||
expect(screen.queryByRole('dialog', { name: '发生错误' })).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('PlatformTaskCompletionDialog', () => {
|
||||
test('shows source, message, and copies the full completion report', async () => {
|
||||
vi.mocked(clipboardService.copyTextToClipboard).mockResolvedValue(true);
|
||||
|
||||
render(
|
||||
<PlatformTaskCompletionDialog
|
||||
completion={{
|
||||
source: '抓大鹅草稿 match3d-notice-session-1',
|
||||
message: '生成任务已完成,可以继续查看草稿。',
|
||||
}}
|
||||
onClose={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
const dialog = screen.getByRole('dialog', { name: '生成完成' });
|
||||
expect(
|
||||
within(dialog).getByText('抓大鹅草稿 match3d-notice-session-1'),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
within(dialog).getByText('生成任务已完成,可以继续查看草稿。'),
|
||||
).toBeTruthy();
|
||||
|
||||
fireEvent.click(within(dialog).getByRole('button', { name: '复制内容' }));
|
||||
|
||||
expect(clipboardService.copyTextToClipboard).toHaveBeenCalledWith(
|
||||
[
|
||||
'来源:抓大鹅草稿 match3d-notice-session-1',
|
||||
'状态:生成任务已完成,可以继续查看草稿。',
|
||||
].join('\n'),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
within(dialog).getByRole('button', { name: '已复制' }),
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
test('does not render when there is no active completion', () => {
|
||||
render(
|
||||
<PlatformTaskCompletionDialog completion={null} onClose={() => {}} />,
|
||||
);
|
||||
|
||||
expect(screen.queryByRole('dialog', { name: '生成完成' })).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
124
src/components/platform-entry/PlatformTaskCompletionDialog.tsx
Normal file
124
src/components/platform-entry/PlatformTaskCompletionDialog.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
import { CheckCircle2, Copy } from 'lucide-react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { copyTextToClipboard } from '../../services/clipboard';
|
||||
import { UnifiedModal } from '../common/UnifiedModal';
|
||||
|
||||
export type PlatformTaskCompletionDialogPayload = {
|
||||
source: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
type PlatformTaskCompletionDialogProps = {
|
||||
completion: PlatformTaskCompletionDialogPayload | null;
|
||||
onClose: () => void;
|
||||
overlayClassName?: string;
|
||||
panelClassName?: string;
|
||||
};
|
||||
|
||||
function buildPlatformTaskCompletionReport(
|
||||
completion: PlatformTaskCompletionDialogPayload,
|
||||
) {
|
||||
return [`来源:${completion.source}`, `状态:${completion.message}`].join(
|
||||
'\n',
|
||||
);
|
||||
}
|
||||
|
||||
export function PlatformTaskCompletionDialog({
|
||||
completion,
|
||||
onClose,
|
||||
overlayClassName = 'platform-theme platform-theme--light !items-center',
|
||||
panelClassName = 'platform-remap-surface rounded-[1.5rem]',
|
||||
}: PlatformTaskCompletionDialogProps) {
|
||||
const [copyState, setCopyState] = useState<'idle' | 'copied' | 'failed'>(
|
||||
'idle',
|
||||
);
|
||||
const resetTimerRef = useRef<number | null>(null);
|
||||
const reportText = useMemo(
|
||||
() => (completion ? buildPlatformTaskCompletionReport(completion) : ''),
|
||||
[completion],
|
||||
);
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
if (resetTimerRef.current !== null) {
|
||||
window.clearTimeout(resetTimerRef.current);
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setCopyState('idle');
|
||||
}, [completion?.source, completion?.message]);
|
||||
|
||||
const copyCompletion = () => {
|
||||
if (!reportText) {
|
||||
return;
|
||||
}
|
||||
|
||||
void copyTextToClipboard(reportText).then((copied) => {
|
||||
setCopyState(copied ? 'copied' : 'failed');
|
||||
if (resetTimerRef.current !== null) {
|
||||
window.clearTimeout(resetTimerRef.current);
|
||||
}
|
||||
resetTimerRef.current = window.setTimeout(() => {
|
||||
resetTimerRef.current = null;
|
||||
setCopyState('idle');
|
||||
}, 1400);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<UnifiedModal
|
||||
open={Boolean(completion)}
|
||||
title="生成完成"
|
||||
onClose={onClose}
|
||||
size="sm"
|
||||
overlayClassName={overlayClassName}
|
||||
panelClassName={panelClassName}
|
||||
bodyClassName="space-y-3 px-4 py-4 sm:px-5 sm:py-5"
|
||||
footerClassName="justify-end px-4 py-4 sm:px-5"
|
||||
footer={
|
||||
<button
|
||||
type="button"
|
||||
onClick={copyCompletion}
|
||||
disabled={!reportText}
|
||||
className="platform-button platform-button--primary w-full justify-center gap-2 sm:w-auto"
|
||||
>
|
||||
{copyState === 'copied' ? (
|
||||
<CheckCircle2 className="h-4 w-4" />
|
||||
) : (
|
||||
<Copy className="h-4 w-4" />
|
||||
)}
|
||||
{copyState === 'copied'
|
||||
? '已复制'
|
||||
: copyState === 'failed'
|
||||
? '复制失败'
|
||||
: '复制内容'}
|
||||
</button>
|
||||
}
|
||||
>
|
||||
{completion ? (
|
||||
<>
|
||||
<div className="rounded-[1rem] border border-[var(--platform-subpanel-border)] bg-white/72 px-3 py-2">
|
||||
<div className="text-xs font-bold text-[var(--platform-text-soft)]">
|
||||
来源
|
||||
</div>
|
||||
<div className="mt-1 break-words text-sm font-semibold leading-5 text-[var(--platform-text-strong)]">
|
||||
{completion.source}
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-[1rem] border border-[var(--platform-subpanel-border)] bg-white/72 px-3 py-2">
|
||||
<div className="text-xs font-bold text-[var(--platform-text-soft)]">
|
||||
状态
|
||||
</div>
|
||||
<div className="mt-1 whitespace-pre-wrap break-words text-sm leading-6 text-[var(--platform-text-strong)]">
|
||||
{completion.message}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</UnifiedModal>
|
||||
);
|
||||
}
|
||||
@@ -3757,7 +3757,7 @@ test('running match3d form generation can return to draft tab and reopen progres
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await openCreateTemplateHub(user);
|
||||
await user.click(screen.getByRole('tab', { name: '抓大鹅' }));
|
||||
await user.click(await findCreationTypeButton('抓大鹅'));
|
||||
await user.click(
|
||||
await screen.findByRole('button', { name: '生成抓大鹅草稿' }),
|
||||
);
|
||||
@@ -3841,7 +3841,7 @@ test('running match3d persisted draft reopens progress instead of unfinished res
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await openCreateTemplateHub(user);
|
||||
await user.click(screen.getByRole('tab', { name: '抓大鹅' }));
|
||||
await user.click(await findCreationTypeButton('抓大鹅'));
|
||||
await user.click(
|
||||
await screen.findByRole('button', { name: '生成抓大鹅草稿' }),
|
||||
);
|
||||
@@ -4038,7 +4038,7 @@ test('running match3d form generation keeps other creation templates available',
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await openCreateTemplateHub(user);
|
||||
await user.click(screen.getByRole('tab', { name: '抓大鹅' }));
|
||||
await user.click(await findCreationTypeButton('抓大鹅'));
|
||||
await user.click(
|
||||
await screen.findByRole('button', { name: '生成抓大鹅草稿' }),
|
||||
);
|
||||
@@ -4107,7 +4107,7 @@ test('running match3d form generation keeps same template generation available',
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await openCreateTemplateHub(user);
|
||||
await user.click(screen.getByRole('tab', { name: '抓大鹅' }));
|
||||
await user.click(await findCreationTypeButton('抓大鹅'));
|
||||
await user.click(
|
||||
await screen.findByRole('button', { name: '生成抓大鹅草稿' }),
|
||||
);
|
||||
@@ -4721,7 +4721,7 @@ test('match3d draft generation auto starts trial and runtime back opens draft re
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await openCreateTemplateHub(user);
|
||||
await user.click(screen.getByRole('tab', { name: '抓大鹅' }));
|
||||
await user.click(await findCreationTypeButton('抓大鹅'));
|
||||
await user.click(
|
||||
await screen.findByRole('button', { name: '生成抓大鹅草稿' }),
|
||||
);
|
||||
@@ -4953,11 +4953,15 @@ test('completed match3d draft notice first opens trial then reopens result', asy
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await openCreateTemplateHub(user);
|
||||
await user.click(screen.getByRole('tab', { name: '抓大鹅' }));
|
||||
await user.click(await findCreationTypeButton('抓大鹅'));
|
||||
await user.click(
|
||||
await screen.findByRole('button', { name: '生成抓大鹅草稿' }),
|
||||
);
|
||||
expect(await screen.findByText('抓大鹅草稿生成进度')).toBeTruthy();
|
||||
expect(
|
||||
await screen.findByRole('progressbar', {
|
||||
name: '抓大鹅草稿生成进度',
|
||||
}),
|
||||
).toBeTruthy();
|
||||
await user.click(screen.getByRole('button', { name: '返回创作中心' }));
|
||||
await openDraftHub(user);
|
||||
await expectDraftHubGeneratingBadgeCountAtLeast(1);
|
||||
@@ -4966,6 +4970,22 @@ test('completed match3d draft notice first opens trial then reopens result', asy
|
||||
resolveCompile({ session: generatedSession });
|
||||
});
|
||||
|
||||
const completionDialog = await screen.findByRole('dialog', {
|
||||
name: '生成完成',
|
||||
});
|
||||
expect(
|
||||
within(completionDialog).getByText(
|
||||
/抓大鹅草稿 match3d-notice-session-1/u,
|
||||
),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
within(completionDialog).getByText(/生成任务已完成/u),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
within(completionDialog).getByRole('button', { name: '复制内容' }),
|
||||
).toBeTruthy();
|
||||
await user.click(within(completionDialog).getByLabelText('关闭'));
|
||||
|
||||
expect(await screen.findByLabelText('新生成完成')).toBeTruthy();
|
||||
await user.click(
|
||||
await screen.findByRole('button', {
|
||||
|
||||
Reference in New Issue
Block a user