import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; import test from 'node:test'; test('design preview serializes the complete visible document before PNG upload', async () => { const source = await readFile(new URL('../src/lib/designPreview.ts', import.meta.url), 'utf8'); assert.match(source, /serializeDocument\(document, stickers, \{ includeBackground: true \}\)/); assert.match(source, /canvas\.toBlob/); }); test('archive client submits document JSON and a PNG preview as multipart data', async () => { const source = await readFile(new URL('../src/lib/productArchive.ts', import.meta.url), 'utf8'); assert.match(source, /form\.append\('document_json'/); assert.match(source, /form\.append\('preview'/); }); test('canvas offers a product archive action and reports detected source count', async () => { const canvas = await readFile(new URL('../src/pages/CanvasStudio.tsx', import.meta.url), 'utf8'); const dialog = await readFile(new URL('../src/components/ProductArchiveDialog.tsx', import.meta.url), 'utf8'); assert.match(canvas, /加入产品列表/); assert.match(dialog, /已检测到.*份画布词云/); assert.match(dialog, /将作为产品封面/); }); test('archive dialog states the server scan is authoritative and covers zero-source drafts', async () => { const dialog = await readFile(new URL('../src/components/ProductArchiveDialog.tsx', import.meta.url), 'utf8'); assert.match(dialog, /(以服务端扫描为准|服务端扫描为最终依据|最终归档数量以服务端为准)/); assert.match(dialog, /无词云归档数据/); }); test('archive success hides raw identifiers and reports the archived count', async () => { const canvas = await readFile(new URL('../src/pages/CanvasStudio.tsx', import.meta.url), 'utf8'); assert.match(canvas, /已归档至产品/); assert.match(canvas, /份词云位置数据已长期保存/); assert.doesNotMatch(canvas, /已归档至产品.*\{product\.product_id\}/); }); test('archive status styles cover archived, empty and pending cleanup states', async () => { const styles = await readFile(new URL('../src/styles.css', import.meta.url), 'utf8'); assert.match(styles, /\.product-status-archived/); assert.match(styles, /\.product-status-empty/); assert.match(styles, /\.product-status-pending-cleanup/); }); test('new product creation archives with the created product id instead of a draft id', async () => { const dialog = await readFile(new URL('../src/components/ProductArchiveDialog.tsx', import.meta.url), 'utf8'); assert.match(dialog, /await createManualProduct\(token, \{ name, sku, specification \}\)/); assert.match(dialog, /archiveProductVersion\(token, product\.product_id, document, previewBlob\)/); }); test('app routes to product archives and list keeps IDs out of primary cells', async () => { const app = await readFile('frontend/src/App.tsx', 'utf8'); const page = await readFile('frontend/src/pages/ProductArchivePage.tsx', 'utf8'); assert.match(app, /'products'/); assert.match(page, /产品档案/); assert.match(page, /产品名称/); assert.doesNotMatch(page, /