收口前端平台组件库能力

新增 PlatformUiKit 通用弹窗、按钮、状态、空态、媒体、表单和标签等公共组件
迁移结果页、创作工作台、认证入口、RPG 暗色面板和运行态弹窗的重复 UI chrome
补充组件测试、页面回归测试、技术文档和 Hermes 共享决策记录
This commit is contained in:
2026-06-10 10:24:18 +08:00
parent a4ee6ff698
commit 1ad25e30f8
226 changed files with 23364 additions and 7825 deletions

View File

@@ -253,7 +253,9 @@ async function openCreateTemplateHub(user: ReturnType<typeof userEvent.setup>) {
).toBeTruthy();
// 中文注释:真实最近创作存在时会成为默认页签,模板入口用例需显式切回模板分类。
if (!within(panel).queryByRole('button', { name: //u })) {
await user.click(await within(panel).findByRole('tab', { name: '热门推荐' }));
await user.click(
await within(panel).findByRole('tab', { name: '热门推荐' }),
);
}
expect(
await within(panel).findByRole('button', { name: //u }),
@@ -3815,9 +3817,7 @@ test('create tab shows recent template cards when backend returns failed drafts'
expect(
await within(panel).findByRole('button', { name: /文字冒险/u }),
).toBeTruthy();
expect(
within(panel).getByText('仅显示最近7天内使用过的模板'),
).toBeTruthy();
expect(within(panel).getByText('仅显示最近7天内使用过的模板')).toBeTruthy();
expect(within(panel).getByText('经典 RPG 体验')).toBeTruthy();
expect(within(panel).queryByText('入口可见的失败草稿')).toBeNull();
expect(
@@ -3842,7 +3842,9 @@ test('create tab refreshes recent works after opening from an empty draft shelf'
render(<TestWrapper withAuth />);
await openDraftHub(user);
expect(within(getPlatformTabPanel('saves')).getByText('还没有作品')).toBeTruthy();
expect(
within(getPlatformTabPanel('saves')).getByText('还没有作品'),
).toBeTruthy();
await clickFirstButtonByName(user, '创作');
const panel = getPlatformTabPanel('create');
@@ -3850,9 +3852,7 @@ test('create tab refreshes recent works after opening from an empty draft shelf'
expect(
await within(panel).findByRole('button', { name: /文字冒险/u }),
).toBeTruthy();
expect(
within(panel).getByText('仅显示最近7天内使用过的模板'),
).toBeTruthy();
expect(within(panel).getByText('仅显示最近7天内使用过的模板')).toBeTruthy();
expect(within(panel).getByText('经典 RPG 体验')).toBeTruthy();
expect(within(panel).queryByText('点击创作后出现的失败草稿')).toBeNull();
expect(
@@ -4015,9 +4015,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();
});
@@ -5063,8 +5063,9 @@ test('failed parallel puzzle generations stay as separate non-generating drafts'
const failureDialog = await screen.findByRole('dialog', {
name: '发生错误',
});
expect(within(failureDialog).getByText(/拼图 VectorEngine 图片编辑失败/u))
.toBeTruthy();
expect(
within(failureDialog).getByText(/拼图 VectorEngine 图片编辑失败/u),
).toBeTruthy();
});
test('failed puzzle draft retry reuses current session instead of creating another draft', async () => {
@@ -7502,8 +7503,9 @@ test('home recommendation share opens publish share modal', async () => {
await screen.findByRole('dialog', { name: '分享给朋友' }),
).toBeTruthy();
expect(screen.getByText(/作品号PZ-SHARE001/u)).toBeTruthy();
expect(screen.getByText(/\/gallery\/puzzle\/detail\?work=PZ-SHARE001/u))
.toBeTruthy();
expect(
screen.getByText(/\/gallery\/puzzle\/detail\?work=PZ-SHARE001/u),
).toBeTruthy();
});
test('home recommendation wooden fish like does not call RPG gallery like', async () => {
@@ -7780,9 +7782,7 @@ test('home recommendation keeps cover while switching during a pending puzzle st
await user.click(await screen.findByRole('button', { name: '下一个' }));
expect(
screen.queryByText('作品暂时无法进入,请稍后再试。'),
).toBeNull();
expect(screen.queryByText('作品暂时无法进入,请稍后再试。')).toBeNull();
expect(
await screen.findByLabelText('贝壳潮汐 作品信息', undefined, {
timeout: 3000,
@@ -7807,9 +7807,7 @@ test('home recommendation keeps cover while switching during a pending puzzle st
}),
);
});
expect(
screen.queryByText('作品暂时无法进入,请稍后再试。'),
).toBeNull();
expect(screen.queryByText('作品暂时无法进入,请稍后再试。')).toBeNull();
});
test('home recommendation puzzle next level uses unified recommend switching', async () => {
@@ -7902,7 +7900,8 @@ test('home recommendation puzzle next level uses unified recommend switching', a
items: [entryWork, nextRecommendWork],
});
vi.mocked(getPuzzleGalleryDetail).mockImplementation(async (profileId) => ({
item: profileId === nextRecommendWork.profileId ? nextRecommendWork : entryWork,
item:
profileId === nextRecommendWork.profileId ? nextRecommendWork : entryWork,
}));
vi.mocked(startPuzzleRun).mockImplementation(async (payload) => {
const run =
@@ -8727,7 +8726,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.findAllByText('当前登录状态已失效,请重新登录后继续。')).length,
(await screen.findAllByText('当前登录状态已失效,请重新登录后继续。'))
.length,
).toBeTruthy();
expect(screen.queryByText('缺少 Authorization Bearer Token')).toBeNull();
});
@@ -9720,7 +9720,8 @@ test('missing puzzle public detail returns to platform home', async () => {
expect(screen.queryByText('资源不存在')).toBeNull();
});
test('direct missing public work detail alert returns to platform home', async () => {
test('direct missing public work detail shows unified dialog before returning home', async () => {
const user = userEvent.setup();
const alertSpy = vi.spyOn(window, 'alert').mockImplementation(() => {});
window.history.replaceState(null, '', '/works/detail?work=PZ-7A7B18D9');
@@ -9732,9 +9733,12 @@ test('direct missing public work detail alert returns to platform home', async (
expect(await screen.findByText('正在读取作品详情...')).toBeTruthy();
await waitFor(() => {
expect(alertSpy).toHaveBeenCalledWith('作品不存在或已下架,将返回首页。');
});
const dialog = await screen.findByRole('dialog', { name: '作品不可用' });
expect(within(dialog).getByText('作品不存在或已下架,将返回首页。')).toBeTruthy();
expect(alertSpy).not.toHaveBeenCalled();
await user.click(within(dialog).getByRole('button', { name: '知道了' }));
await waitFor(() => {
expect(window.location.pathname).toBe('/');
});
@@ -9747,6 +9751,8 @@ test('direct missing public work detail alert returns to platform home', async (
expect(screen.queryByText('详情')).toBeNull();
expect(screen.queryByText('未找到拼图作品。')).toBeNull();
expect(startPuzzleRun).toHaveBeenCalledTimes(0);
alertSpy.mockRestore();
});
test('public code search opens a published big fish work by BF code', async () => {
@@ -12326,7 +12332,9 @@ test('creation hub gives jump hop wooden fish and bark battle cards the shared d
if (!shell) {
throw new Error('作品卡应位于统一操作壳内');
}
await user.click(within(shell as HTMLElement).getByRole('button', { name: '删除' }));
await user.click(
within(shell as HTMLElement).getByRole('button', { name: '删除' }),
);
const dialog = await screen.findByRole('dialog', { name: '删除作品' });
expect(within(dialog).getByText(`${title}`)).toBeTruthy();