/* @vitest-environment jsdom */ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { expect, test, vi } from 'vitest'; import type { BabyObjectMatchDraft } from '../../../packages/shared/src/contracts/edutainmentBabyObject'; import { BABY_OBJECT_MATCH_EDUTAINMENT_TAG, BABY_OBJECT_MATCH_TEMPLATE_ID, BABY_OBJECT_MATCH_TEMPLATE_NAME, } from '../../../packages/shared/src/contracts/edutainmentBabyObject'; import { BabyObjectMatchResultView } from './BabyObjectMatchResultView'; vi.mock('../ResolvedAssetImage', () => ({ ResolvedAssetImage: ({ src, alt, className, }: { src?: string | null; alt?: string; className?: string; }) => (src ? {alt} : null), })); function createDraft(overrides: Partial = {}) { const draft: BabyObjectMatchDraft = { draftId: 'baby-object-draft-1', profileId: 'baby-object-profile-1', templateId: BABY_OBJECT_MATCH_TEMPLATE_ID, templateName: BABY_OBJECT_MATCH_TEMPLATE_NAME, workTitle: '宝贝识物', workDescription: '苹果和香蕉识物分类', itemNames: ['苹果', '香蕉'], itemAssets: [ { itemId: 'baby-object-item-1', itemName: '苹果', imageSrc: 'data:image/svg+xml;utf8,a', assetObjectId: null, generationProvider: 'placeholder', prompt: '苹果', }, { itemId: 'baby-object-item-2', itemName: '香蕉', imageSrc: 'data:image/svg+xml;utf8,b', assetObjectId: null, generationProvider: 'placeholder', prompt: '香蕉', }, ], visualPackage: null, themeTags: ['宝贝识物'], publicationStatus: 'draft', createdAt: '2026-05-11T00:00:00.000Z', updatedAt: '2026-05-11T00:00:00.000Z', publishedAt: null, ...overrides, }; return draft; } function createGeneratedDraft() { return createDraft({ itemAssets: [ { itemId: 'baby-object-item-1', itemName: '苹果', imageSrc: 'data:image/png;base64,a', assetObjectId: null, generationProvider: 'vector-engine-gpt-image-2', prompt: '苹果', }, { itemId: 'baby-object-item-2', itemName: '香蕉', imageSrc: 'data:image/png;base64,b', assetObjectId: null, generationProvider: 'vector-engine-gpt-image-2', prompt: '香蕉', }, ], visualPackage: { themePrompt: '果园主题', assets: [ { assetId: 'baby-object-visual-background', assetKind: 'background', imageSrc: 'data:image/png;base64,background', assetObjectId: null, generationProvider: 'vector-engine-gpt-image-2', prompt: 'background', }, { assetId: 'baby-object-visual-ui-frame', assetKind: 'ui-frame', imageSrc: 'data:image/png;base64,ui', assetObjectId: null, generationProvider: 'vector-engine-gpt-image-2', prompt: 'ui', }, { assetId: 'baby-object-visual-gift-box', assetKind: 'gift-box', imageSrc: 'data:image/png;base64,gift', assetObjectId: null, generationProvider: 'vector-engine-gpt-image-2', prompt: 'gift', }, { assetId: 'baby-object-visual-basket', assetKind: 'basket', imageSrc: 'data:image/png;base64,basket', assetObjectId: null, generationProvider: 'vector-engine-gpt-image-2', prompt: 'basket', }, { assetId: 'baby-object-visual-smoke-puff', assetKind: 'smoke-puff', imageSrc: 'data:image/png;base64,smoke', assetObjectId: null, generationProvider: 'vector-engine-gpt-image-2', prompt: 'smoke', }, ], }, }); } test('baby object result publishes with exact edutainment tag', async () => { const user = userEvent.setup(); const onPublish = vi.fn(); render( {}} onPublish={onPublish} />, ); await user.click(screen.getByRole('button', { name: '发布' })); expect(onPublish).toHaveBeenCalledTimes(1); expect(onPublish.mock.calls[0]?.[0].themeTags[0]).toBe( BABY_OBJECT_MATCH_EDUTAINMENT_TAG, ); expect(onPublish.mock.calls[0]?.[0].themeTags).toContain('宝贝识物'); }); test('baby object result exposes save and test run actions', async () => { const user = userEvent.setup(); const onSaveDraft = vi.fn(); const onStartTestRun = vi.fn(); render( {}} onSaveDraft={onSaveDraft} onStartTestRun={onStartTestRun} />, ); await user.click(screen.getByRole('button', { name: '保存草稿' })); await user.click(screen.getByRole('button', { name: '试玩' })); expect(onSaveDraft).toHaveBeenCalledTimes(1); expect(onStartTestRun).toHaveBeenCalledTimes(1); }); test('baby object result blocks placeholder assets and exposes regeneration', async () => { const user = userEvent.setup(); const onPublish = vi.fn(); const onStartTestRun = vi.fn(); const onRegenerateAssets = vi.fn(); render( {}} onPublish={onPublish} onStartTestRun={onStartTestRun} onRegenerateAssets={onRegenerateAssets} />, ); expect( screen.getByText('当前作品仍是占位资源,请重新生成 image-2 资源后再试玩或发布。'), ).toBeTruthy(); expect( (screen.getByRole('button', { name: '试玩' }) as HTMLButtonElement) .disabled, ).toBe(true); expect( (screen.getByRole('button', { name: '发布' }) as HTMLButtonElement) .disabled, ).toBe(true); await user.click(screen.getByRole('button', { name: '重新生成资源' })); expect(onRegenerateAssets).toHaveBeenCalledTimes(1); expect(onPublish).not.toHaveBeenCalled(); expect(onStartTestRun).not.toHaveBeenCalled(); });