import test from 'node:test'; import assert from 'node:assert/strict'; import { readFile, access } from 'node:fs/promises'; import { ART_DIRECTIONS, directionTheme } from '../src/art-directions.js'; import { THEME } from '../src/design-tokens.js'; import { recordDesign } from '../design/examples/render-design.mjs'; const root = new URL('../', import.meta.url); const read = path => readFile(new URL(path, root), 'utf8'); const manifest = JSON.parse(await read('design/manifest.json')); test('an agent can discover all authored specs, resolved tokens, previews and implementation sources', async () => { assert.equal(manifest.schemaVersion, 1); assert.equal(manifest.pathBase, 'repository-root'); assert.deepEqual(manifest.designs.map(d => d.id), ART_DIRECTIONS.map(d => d.id)); assert.equal(manifest.defaultDesignId, 'calm'); for (const path of [manifest.entry, manifest.agentGuide, manifest.sharedRules, manifest.catalog, manifest.example, ...Object.values(manifest.sources)]) { await access(new URL(path, root)); } for (const entry of manifest.designs) { const spec = await read(entry.designDoc); assert.match(spec, new RegExp('设计 ID:`' + entry.id + '`')); assert.ok(spec.includes('../../shared.md')); assert.ok(spec.includes('tokens.json')); assert.ok(spec.includes('## 验收与接入')); const html = await read(entry.preview); assert.ok(html.includes(`data-design="${entry.id}"`)); assert.ok(html.includes('href="design.md"')); assert.ok(html.includes('href="tokens.json"')); assert.ok((await read(entry.legacyPreview)).includes(`url=${entry.id}/index.html`)); assert.ok((await read(`design/directions/${entry.id}.md`)).includes(`${entry.id}/design.md`)); } }); test('generated design data stays aligned with runtime palettes at every supported color count', async () => { for (const entry of manifest.designs) { const direction = ART_DIRECTIONS.find(d => d.id === entry.id); const data = JSON.parse(await read(entry.tokens)); assert.equal(data.designId, entry.id); assert.equal(data.artStyle, direction.style); assert.deepEqual(data.pigments.map(({ index, ...pigment }) => pigment), direction.pigments); assert.deepEqual(data.themes.map(t => t.id), manifest.themeIds); for (const theme of data.themes) { const source = direction.themes.find(t => t.id === theme.id); assert.equal(theme.background, source.background); assert.equal(theme.ink, source.ink); for (const [key, value] of Object.entries(theme.materials)) assert.equal(value, source.materials?.[key] ?? THEME[key]); for (const count of manifest.paletteCounts) assert.deepEqual(theme.palettes[count], directionTheme(direction, theme.id, count).palette); } } }); test('canonical design documents and previews have no broken local links after the directory move', async () => { const paths = ['design/README.md', 'design/AGENTS.md', 'design/shared.md', 'design/index.html', ...manifest.designs.flatMap(d => [d.designDoc, d.preview, d.legacyPreview])]; for (const path of paths) { const source = (await read(path)).replace(/```[\s\S]*?```/g, ''); const links = path.endsWith('.md') ? [...source.matchAll(/\[[^\]]+\]\(([^)]+)\)/g)] : [...source.matchAll(/(?:href|src)="([^"]+)"/g)]; for (const [, href] of links) { if (/^(?:https?:|data:|#)/.test(href)) continue; const target = new URL(href, new URL(path, root)); target.search = ''; target.hash = ''; await assert.doesNotReject(access(target), `${path} → ${href}`); } } }); test('the documented integration example renders all design and theme combinations and rejects typos', () => { for (const direction of ART_DIRECTIONS) { for (const theme of direction.themes) { const result = recordDesign(direction.id, theme.id, 7); assert.equal(result.designId, direction.id); assert.equal(result.background, theme.background); assert.ok(result.commands.some(command => command.type === 'line')); assert.ok(result.commands.some(command => command.type === 'polygon')); for (const command of result.commands) { if (command.points) assert.ok(command.points.every(point => Number.isFinite(point.x) && Number.isFinite(point.y))); if (command.width !== undefined) assert.ok(command.width > 0); } } } assert.throws(() => recordDesign('paepr'), /Unknown design/); assert.throws(() => recordDesign('paper', 'night'), /Unknown theme/); assert.throws(() => recordDesign('paper', 'lagoon', 5), /Unsupported color count/); });