/* @vitest-environment jsdom */ import { fireEvent, render, screen } from '@testing-library/react'; import { act } from 'react'; import type { ImgHTMLAttributes } from 'react'; import { expect, test, vi } from 'vitest'; import type { PuzzleClearCardAsset, PuzzleClearPatternGroup, PuzzleClearWorkProfileResponse, } from '../../../packages/shared/src/contracts/puzzleClear'; import { PuzzleClearResultView } from './PuzzleClearResultView'; vi.mock('../ResolvedAssetImage', () => ({ ResolvedAssetImage: ({ src, alt, className, ...rest }: { src?: string | null; alt?: string; className?: string; [key: string]: unknown; }) => src ? ( {alt})} /> ) : null, })); function createPatternGroup(index: number): PuzzleClearPatternGroup { return { groupId: `group-${index}`, shape: '1x2', width: 2, height: 1, atlasX: index * 64, atlasY: 0, atlasWidth: 128, atlasHeight: 64, }; } function createCard(index: number): PuzzleClearCardAsset { return { cardId: `card-${index}`, groupId: `group-${Math.floor(index / 2)}`, shape: '1x2', orientation: 'horizontal', partX: index % 2, partY: 0, imageSrc: `/cards/card-${index}.png`, imageObjectKey: `generated-puzzle-clear-assets/card-${index}.png`, assetObjectId: `assetobj_card_${index}`, sourceAtlasCell: `${index}:0:0`, }; } function createProfile( overrides: Partial = {}, ): PuzzleClearWorkProfileResponse { const atlasAsset = { assetId: 'atlas-1', imageSrc: '/atlas.png', imageObjectKey: 'generated-puzzle-clear-assets/atlas.png', assetObjectId: 'assetobj_atlas', generationProvider: 'gpt-image-2', prompt: '星港', width: 2560, height: 2560, }; const boardBackgroundAsset = { ...atlasAsset, assetId: 'board-background-1', imageSrc: '/board-background.png', imageObjectKey: 'generated-puzzle-clear-assets/board-background.png', assetObjectId: 'assetobj_board_background', }; const patternGroups = Array.from({ length: 35 }, (_, index) => createPatternGroup(index), ); const cardAssets = Array.from({ length: 95 }, (_, index) => createCard(index)); const draft = { templateId: 'puzzle-clear', templateName: '拼消消', profileId: 'puzzle-clear-profile-12345678', workTitle: '星港拼消消', workDescription: '霓虹星港主题', themePrompt: '星港', boardBackgroundPrompt: '星港中央棋盘底图', generateBoardBackground: true, boardBackgroundAsset, cardBackImageSrc: '/creation-type-references/puzzle-clear-card-back.webp', atlasAsset, patternGroups, cardAssets, generationStatus: 'ready' as const, }; return { summary: { runtimeKind: 'puzzle-clear', workId: 'puzzle-clear-work-12345678', profileId: 'puzzle-clear-profile-12345678', ownerUserId: 'user-1', sourceSessionId: 'puzzle-clear-session-1', workTitle: '星港拼消消', workDescription: '霓虹星港主题', themePrompt: '星港', coverImageSrc: '/atlas.png', publicationStatus: 'draft', playCount: 0, updatedAt: '2026-05-30T00:00:00.000Z', publishedAt: null, publishReady: true, generationStatus: 'ready', ...overrides, }, draft, boardBackgroundAsset, atlasAsset, patternGroups, cardAssets, }; } test('结果页展示 atlas、中央底图与卡牌预览,并触发试玩、发布和图集重试', async () => { const onStartTestRun = vi.fn(); const onPublish = vi.fn(); const onRegenerateAtlas = vi.fn(); const { container } = render( , ); expect(screen.getByAltText('场地底图').getAttribute('src')).toBe( '/board-background.png', ); expect(screen.getByAltText('素材图集').getAttribute('src')).toBe('/atlas.png'); expect(screen.getByText('35')).not.toBeNull(); expect(screen.getByText('95')).not.toBeNull(); expect(container.querySelectorAll('img[src^="/cards/"]')).toHaveLength(24); fireEvent.click(screen.getByRole('button', { name: /试玩/u })); await act(async () => { fireEvent.click(screen.getByRole('button', { name: /发布/u })); }); fireEvent.click(screen.getByRole('button', { name: /图集/u })); expect(onStartTestRun).toHaveBeenCalledTimes(1); expect(onPublish).toHaveBeenCalledTimes(1); expect(onRegenerateAtlas).toHaveBeenCalledTimes(1); }); test('结果页在素材未发布就绪时禁用发布,且不写入规则说明文案', () => { render( , ); expect( (screen.getByRole('button', { name: /发布/u }) as HTMLButtonElement).disabled, ).toBe(true); expect(screen.queryByText(/规则|玩法说明|拖动卡片|拼接完整/u)).toBeNull(); });