Files
wehub-resource-sync 070959e133
landing-page-staging / Deploy landing page to staging (push) Has been skipped
landing-page-ci / Validate landing page (push) Failing after 4s
visual-baseline / Capture visual baselines (push) Has been cancelled
bake-plugin-previews / Bake plugin previews (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:00:47 +08:00

151 lines
5.4 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { parseDesignMd } from '../../src/runtime/design-md-parse';
// The brand-generated emitter shape (apps/daemon/src/brands/design-md.ts):
// YAML frontmatter + `## Color Palette` table + `## Typography` font lines +
// `## Voice & Tone` / `## Imagery` / `## Layout`. The web parser must round-trip
// this so a brand-derived design system renders its modules.
const BRAND_EMITTER_MD = [
'---',
'name: "The Economist"',
'category: Brands',
'surface: web',
'colors:',
' economist-red: "#e3120b"',
' ink: "#0d0d0d"',
'---',
'',
'# The Economist',
'> Category: Brands',
'> Surface: web',
'*Go beyond breaking news*',
'The Economist is a weekly newspaper covering world politics and economics.',
'',
'## Color Palette',
'',
'| Role | Name | Hex | Usage |',
'| --- | --- | --- | --- |',
'| accent | Economist Red | `#E3120B` | masthead, links, CTAs |',
'| foreground | Ink | `#0D0D0D` | primary text |',
'',
'## Typography',
'',
'- **Display:** EconomistSans — weights 400, 700 — fallbacks: Source Sans 3, system-ui',
'- **Body:** EconomistSerif — weights 400 — fallbacks: Georgia, serif',
'',
'## Voice & Tone',
'',
'- **Adjectives:** authoritative, incisive, witty',
'- **Tone:** Plain, confident and analytical with a dry wit.',
'',
'### Messaging pillars',
'- Go beyond breaking news',
'- A single, coherent editorial voice',
'',
'### Vocabulary',
'- **Use:** leader, why it matters',
'- **Avoid:** clickbait, emoji',
'',
'## Imagery',
'',
'- **Style:** Bold conceptual editorial illustration.',
'- **Subjects:** world leaders, markets',
'- **Treatment:** flat colour over ink line',
'- **Avoid:** stock-photo clichés',
'',
'## Layout',
'',
'- **Radius:** 0px',
'- **Border weight:** 1px',
'- **Spacing:** 8px baseline grid',
'',
'### Posture rules',
'- White ground, near-black text, thin grey hairlines.',
].join('\n');
// A bundled preset shape (design-systems/*/DESIGN.md): numbered headings, bullet
// palette + `Families: primary=…` typography, no Voice/Imagery sections.
const PRESET_MD = [
'# Design System Inspired by Agentic',
'',
'> Category: Themed & Unique',
'> Conversational AI-first interface.',
'',
'## 2. Color',
'',
'- **Primary:** `#FF5701` — Token from style foundations.',
'- **Surface:** `#FFFFFF` — Large backgrounds and cards.',
'- **Text:** `#111827` — Body copy.',
'',
'## 3. Typography',
'',
'- **Families:** primary=Playfair Display, display=Playfair Display, mono=JetBrains Mono',
'- **Weights:** 400, 600, 700',
].join('\n');
describe('parseDesignMd', () => {
it('round-trips the brand emitter DESIGN.md shape', () => {
const parsed = parseDesignMd(BRAND_EMITTER_MD);
expect(parsed.name).toBe('The Economist');
expect(parsed.tagline).toBe('Go beyond breaking news');
expect(parsed.description).toContain('weekly newspaper');
// Color table wins over frontmatter; hexes normalize to lowercase.
const accent = parsed.colors.find((c) => c.hex === '#e3120b');
expect(accent?.name).toBe('Economist Red');
expect(accent?.usage).toContain('masthead');
expect(parsed.colors.some((c) => c.hex === '#0d0d0d')).toBe(true);
expect(parsed.typography.display?.family).toBe('EconomistSans');
expect(parsed.typography.display?.weights).toContain(400);
expect(parsed.typography.display?.fallbacks).toContain('Source Sans 3');
expect(parsed.typography.body?.family).toBe('EconomistSerif');
expect(parsed.voice.adjectives).toContain('authoritative');
expect(parsed.voice.tone).toContain('dry wit');
expect(parsed.voice.messagingPillars).toContain('Go beyond breaking news');
expect(parsed.voice.vocabulary.use).toContain('leader');
expect(parsed.voice.vocabulary.avoid).toContain('emoji');
expect(parsed.imagery.style).toContain('editorial illustration');
expect(parsed.imagery.subjects).toContain('markets');
expect(parsed.layout.radius).toBe('0px');
expect(parsed.layout.spacing).toContain('baseline grid');
expect(parsed.layout.postureRules.length).toBeGreaterThan(0);
});
it('falls back to frontmatter colors when no palette table exists', () => {
const md = ['---', 'name: "Acme"', 'colors:', ' brand: "#123456"', '---', '', '# Acme'].join('\n');
const parsed = parseDesignMd(md);
expect(parsed.colors).toHaveLength(1);
expect(parsed.colors[0]?.hex).toBe('#123456');
});
it('parses the bundled preset DESIGN.md shape (numbered headings, bullet palette)', () => {
const parsed = parseDesignMd(PRESET_MD);
expect(parsed.name).toBe('Design System Inspired by Agentic');
expect(parsed.category).toBe('Themed & Unique');
expect(parsed.colors.map((c) => c.hex)).toEqual(
expect.arrayContaining(['#ff5701', '#ffffff', '#111827']),
);
const primary = parsed.colors.find((c) => c.hex === '#ff5701');
expect(primary?.role).toBe('accent');
expect(parsed.typography.display?.family).toBe('Playfair Display');
expect(parsed.typography.mono?.family).toBe('JetBrains Mono');
expect(parsed.typography.display?.weights).toEqual(expect.arrayContaining([400, 600, 700]));
});
it('returns empty modules for an empty document without throwing', () => {
const parsed = parseDesignMd('');
expect(parsed.colors).toEqual([]);
expect(parsed.typography.display).toBeUndefined();
expect(parsed.voice.adjectives).toEqual([]);
});
});