mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
* 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
203 lines
6.4 KiB
TypeScript
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}`);
|
|
}
|