open-design/apps/daemon/tests/design-system-generation-jobs.test.ts
Eli 18b947c25f
[codex] Land design system GitHub intake handoff (#2187)
* Add Claude-style design system workflow

* Merge design system workflow into main

* Restore design system workflow UI styles

* Fix design system setup scrolling

* Fix design system setup connector button

* Preserve connector auth link after popup block

* Simplify connected GitHub setup state

* Open generated design system workspace project

* Summarize design system auto prompt in chat

* Add bounded GitHub connector design intake

* Prefer path-scoped GitHub intake tools

* Restore branch GitHub design context intake

* Restore design system review workspace

* Restore design system manager tab

* Let design system workflow routes own details

* Open editable design systems as projects

* Restore design system workspace coverage

* Fix bounded GitHub connector intake

* Hide design system review while generating

* Suppress design system generation questions

* Constrain GitHub design intake to bounded command

* Tolerate oversized GitHub metadata during intake

* Rebuild daemon CLI when sources change

* Fallback when GitHub connector snapshots are rate limited

* Allow GitHub intake without Composio

* Use native GitHub auth for design intake

* Remove design system review group heading

* Improve design system extraction evidence

* Align design system scaffold with Claude output

* Add evidence inventory for design system intake

* Add local design system evidence intake

* Add design system package audit gate

* Allow auditing Claude Design reference packages

* Audit design system package content quality

* Migrate legacy design system artifacts

* Clean migrated design system artifacts

* Require modular design system UI kits

* Reject thin design system UI kits

* Prioritize core design evidence intake

* Require role-based design system UI kits

* Clean stale design system manifest references

* Require representative preserved design assets

* Warn on generic design system visuals

* Enforce design system quality warnings

* Audit connected design system UI kits

* Require mounted design system UI kits

* Require composed design system app shells

* Require runnable JSX design system kits

* Require browser globals for design system components

* Infer design system names from source URLs

* Require source examples in design system packages

* Bind preserved fonts in design system tokens

* Require skill frontmatter in design system packages

* Preserve build icons in design system packages

* Require real assets in brand previews

* Require substantive source examples

* Require product overview in design system README

* Require reusable UI kit README

* Require reusable design system skill docs

* Seed Claude-style UI kit entry contract

* Preserve runtime build assets in design packages

* Audit design system packages after generation

* Audit design system first-run output

* Audit source-backed preview cards

* Align design system UI kit scaffolds

* Materialize design evidence package artifacts

* Show project chat during design system setup

* Hand off design system setup to project chat

* Auto-repair design system audit failures

* Harden design system evidence preservation

* Tighten design system package guidance

* Add targeted design system repair guidance

* Bound design system audit auto repair

* Use connector statuses in design system setup

* Audit design system preview manifests

* Require README preview manifests for design systems

* Fix design system GitHub intake handoff

* Fix daemon prompt CI assertions
2026-05-19 14:30:17 +08:00

203 lines
6.4 KiB
TypeScript

import { mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import {
createDesignSystemGenerationJobStore,
type DesignSystemGenerationJob,
} from '../src/design-system-generation-jobs.js';
import {
createUserDesignSystem,
listUserDesignSystemRevisions,
readDesignSystem,
type UserDesignSystemInput,
updateUserDesignSystemRevisionStatus,
} from '../src/design-systems.js';
describe('design system generation jobs', () => {
let root: string;
beforeEach(async () => {
root = await mkdtemp(path.join(tmpdir(), 'od-design-system-jobs-'));
});
afterEach(async () => {
await rm(root, { recursive: true, force: true });
});
it('creates a pollable job that produces a user design system draft', async () => {
const store = createDesignSystemGenerationJobStore({
root,
delayMs: 0,
idFactory: () => 'job-1',
collectSourceContext: async () => ({ github: [], notes: '' }),
});
const started = store.start({
title: 'Acme Product',
summary: 'Dense product UI.',
category: 'Custom',
status: 'draft',
provenance: {
companyBlurb: 'Acme builds dense product UI.',
githubUrls: ['https://github.com/acme/product'],
},
});
expect(started).toMatchObject({
id: 'job-1',
status: 'running',
progress: 0,
});
const done = await waitForJob(store, 'job-1');
expect(done).toMatchObject({
id: 'job-1',
status: 'succeeded',
progress: 100,
designSystemId: 'user:acme-product',
});
expect(done.steps.map((step) => step.status)).toEqual([
'succeeded',
'succeeded',
'succeeded',
'succeeded',
'succeeded',
]);
expect(done.steps.map((step) => step.message).join('\n')).toContain(
'1 GitHub link(s)',
);
});
it('merges collected source context into the generated draft', async () => {
let capturedInput: UserDesignSystemInput | undefined;
const store = createDesignSystemGenerationJobStore({
root,
delayMs: 0,
idFactory: () => 'job-context',
collectSourceContext: async () => ({
github: [{
url: 'https://github.com/acme/product',
owner: 'acme',
repo: 'product',
description: 'Acme repository.',
}],
notes: 'Fetched GitHub context:\n- acme/product: README excerpt: Dense editor primitives.',
}),
createDesignSystem: async (targetRoot, input) => {
capturedInput = input;
return createUserDesignSystem(targetRoot, input);
},
});
store.start({
title: 'Context Product',
sourceNotes: 'GitHub/code: https://github.com/acme/product',
provenance: {
githubUrls: ['https://github.com/acme/product'],
sourceNotes: 'GitHub/code: https://github.com/acme/product',
},
});
const done = await waitForJob(store, 'job-context');
const body = await readDesignSystem(root, done.designSystemId ?? '', { idPrefix: 'user:' });
expect(done.steps.find((step) => step.id === 'explore-resources')?.message).toContain('read 1 GitHub repo');
expect(capturedInput?.sourceNotes).toContain('GitHub/code: https://github.com/acme/product');
expect(capturedInput?.sourceNotes).toContain('Fetched GitHub context');
expect(capturedInput?.provenance?.sourceNotes).toContain('Dense editor primitives');
expect(capturedInput?.provenance?.sourceNotes).not.toContain('GitHub/code:');
expect(body).toContain('Dense editor primitives');
});
it('exposes failed generation status when draft creation fails', async () => {
const store = createDesignSystemGenerationJobStore({
root,
delayMs: 0,
idFactory: () => 'job-fail',
createDesignSystem: async () => {
throw new Error('draft write failed');
},
});
store.start({ title: 'Broken System' });
const done = await waitForJob(store, 'job-fail');
expect(done).toMatchObject({
id: 'job-fail',
status: 'failed',
error: 'draft write failed',
});
expect(done.steps.find((step) => step.id === 'create-draft')?.status).toBe('failed');
});
it('runs a revision job against an existing user design system', async () => {
const store = createDesignSystemGenerationJobStore({
root,
delayMs: 0,
idFactory: () => 'revision-1',
});
const created = await createUserDesignSystem(root, {
title: 'Revision Product',
summary: 'Initial system.',
status: 'draft',
});
const started = store.revise({
designSystemId: created.id,
sectionTitle: 'Visual Foundations',
feedback: 'Make the palette warmer and reduce decorative effects.',
});
expect(started).toMatchObject({
id: 'revision-1',
kind: 'revision',
designSystemId: created.id,
});
const done = await waitForJob(store, 'revision-1');
const body = await readDesignSystem(root, created.id, { idPrefix: 'user:' });
const revisions = await listUserDesignSystemRevisions(root, created.id);
expect(done).toMatchObject({
id: 'revision-1',
status: 'succeeded',
progress: 100,
designSystemId: created.id,
revisionId: expect.any(String),
});
expect(body).not.toContain('## Revision Request: Visual Foundations');
expect(revisions?.[0]).toMatchObject({
status: 'pending',
feedback: 'Make the palette warmer and reduce decorative effects.',
sectionTitle: 'Visual Foundations',
});
expect(revisions?.[0]?.proposedBody).toContain('## Revision Request: Visual Foundations');
const accepted = await updateUserDesignSystemRevisionStatus(
root,
created.id,
revisions?.[0]?.id ?? '',
'accepted',
);
const acceptedBody = await readDesignSystem(root, created.id, { idPrefix: 'user:' });
expect(accepted?.status).toBe('accepted');
expect(acceptedBody).toContain('## Revision Request: Visual Foundations');
});
});
async function waitForJob(
store: ReturnType<typeof createDesignSystemGenerationJobStore>,
id: string,
): Promise<DesignSystemGenerationJob> {
for (let attempt = 0; attempt < 30; attempt += 1) {
const job = store.get(id);
if (job && (job.status === 'succeeded' || job.status === 'failed')) return job;
await new Promise((resolve) => setTimeout(resolve, 5));
}
throw new Error(`Timed out waiting for ${id}`);
}