import * as React from 'react' import { createElement } from 'react' import type { ComponentProps, ReactElement } from 'react' import { describe, expect, it } from 'vitest' import { renderToStaticMarkup } from 'react-dom/server' import { NextIntlClientProvider } from 'next-intl' import type { AbstractIntlMessages } from 'next-intl' import ScriptViewAssetsPanel from '@/app/[locale]/workspace/[projectId]/modes/novel-promotion/components/script-view/ScriptViewAssetsPanel' const messages = { scriptView: { inSceneAssets: '出场资产', assetView: { allClips: '全部片段', }, segment: { title: '片段 {index}', }, asset: { activeCharacters: '出场角色', activeLocations: '出场场景', defaultAppearance: '默认形象', }, screenplay: { noCharacter: '当前片段未选择角色', noLocation: '当前片段未选择场景', }, generate: { startGenerate: '开始生成', }, }, assets: { character: { primary: '初始形象', }, }, novelPromotion: { buttons: { assetLibrary: '资产库', }, }, common: { edit: '编辑', cancel: '取消', confirm: '确定', }, } as const function renderWithIntl(node: ReactElement) { const providerProps: ComponentProps = { locale: 'zh', messages: messages as unknown as AbstractIntlMessages, timeZone: 'Asia/Shanghai', children: node, } return renderToStaticMarkup( createElement(NextIntlClientProvider, providerProps), ) } function renderPanel(propsCount: number) { Reflect.set(globalThis, 'React', React) const props = Array.from({ length: propsCount }, (_, index) => ({ id: `prop-${index + 1}`, name: `道具${index + 1}`, summary: `道具描述${index + 1}`, selectedImageId: null, images: [], })) return renderWithIntl( createElement(ScriptViewAssetsPanel, { clips: [{ id: 'clip-1', location: null, props: null }], assetViewMode: 'all', setAssetViewMode: () => undefined, setSelectedClipId: () => undefined, characters: [], locations: [], props, activeCharIds: [], activeLocationIds: [], activePropIds: [], selectedAppearanceKeys: new Set(), onUpdateClipAssets: async () => undefined, onOpenAssetLibrary: () => undefined, assetsLoading: false, assetsLoadingState: null, allAssetsHaveImages: true, globalCharIds: [], globalLocationIds: [], globalPropIds: [], missingAssetsCount: 0, onGenerateStoryboard: () => undefined, isSubmittingStoryboardBuild: false, getSelectedAppearances: () => [], tScript: (key: string, values?: Record) => { if (key === 'inSceneAssets') return '出场资产' if (key === 'assetView.allClips') return '全部片段' if (key === 'segment.title') return `片段 ${String(values?.index ?? '')}` if (key === 'asset.activeCharacters') return '出场角色' if (key === 'asset.activeLocations') return '出场场景' if (key === 'screenplay.noCharacter') return '当前片段未选择角色' if (key === 'screenplay.noLocation') return '当前片段未选择场景' if (key === 'generate.startGenerate') return '开始生成' if (key === 'asset.defaultAppearance') return '默认形象' return key }, tAssets: (key: string) => (key === 'character.primary' ? '初始形象' : key), tNP: (key: string) => (key === 'buttons.assetLibrary' ? '资产库' : key), tCommon: (key: string) => { if (key === 'edit') return '编辑' if (key === 'cancel') return '取消' if (key === 'confirm') return '确定' return key }, }), ) } describe('ScriptViewAssetsPanel', () => { it('hides the prop section when the project has no prop assets', () => { const html = renderPanel(0) expect(html).not.toContain('道具 (0)') expect(html).not.toContain('当前片段未选择道具') }) it('keeps the prop section visible when the project has prop assets even if none are selected in the current clip', () => { const html = renderPanel(1) expect(html).toContain('道具 (0)') expect(html).toContain('当前片段未选择道具') }) })