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
287 lines
9.7 KiB
TypeScript
287 lines
9.7 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { act } from 'react';
|
|
import { createRoot, type Root } from 'react-dom/client';
|
|
import { renderToStaticMarkup } from 'react-dom/server';
|
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import { FileWorkspace } from '../../src/components/FileWorkspace';
|
|
import type { AgentEvent, DesignSystemSummary, ProjectFile } from '../../src/types';
|
|
|
|
const registryMocks = vi.hoisted(() => ({
|
|
updateDesignSystemDraft: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('../../src/providers/registry', async () => {
|
|
const actual = await vi.importActual<typeof import('../../src/providers/registry')>(
|
|
'../../src/providers/registry',
|
|
);
|
|
return {
|
|
...actual,
|
|
updateDesignSystemDraft: registryMocks.updateDesignSystemDraft,
|
|
};
|
|
});
|
|
|
|
let root: Root | null = null;
|
|
let host: HTMLDivElement | null = null;
|
|
|
|
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
|
|
|
afterEach(() => {
|
|
if (root) {
|
|
act(() => root?.unmount());
|
|
root = null;
|
|
}
|
|
host?.remove();
|
|
host = null;
|
|
vi.clearAllMocks();
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
function workspaceFile(name: string): ProjectFile {
|
|
return {
|
|
name,
|
|
path: name,
|
|
type: 'file',
|
|
size: 100,
|
|
mtime: Date.parse('2026-05-14T00:00:00.000Z'),
|
|
kind: name.endsWith('.html') ? 'html' : name.endsWith('.svg') ? 'image' : 'text',
|
|
mime: name.endsWith('.html') ? 'text/html' : name.endsWith('.svg') ? 'image/svg+xml' : 'text/plain',
|
|
};
|
|
}
|
|
|
|
function designSystem(overrides: Partial<DesignSystemSummary> = {}): DesignSystemSummary {
|
|
return {
|
|
id: 'user:acme',
|
|
title: 'Acme Design System',
|
|
category: 'Custom',
|
|
summary: 'Context project for Acme.',
|
|
swatches: [],
|
|
surface: 'web',
|
|
source: 'user',
|
|
status: 'draft',
|
|
isEditable: true,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function renderWorkspace(element: React.ReactElement) {
|
|
host = document.createElement('div');
|
|
document.body.appendChild(host);
|
|
act(() => {
|
|
root = createRoot(host!);
|
|
root.render(element);
|
|
});
|
|
return host;
|
|
}
|
|
|
|
type ToolUseEvent = Extract<AgentEvent, { kind: 'tool_use' }>;
|
|
type ToolResultEvent = Extract<AgentEvent, { kind: 'tool_result' }>;
|
|
|
|
function toolUse(name: string, input: unknown, id: string): ToolUseEvent {
|
|
return { kind: 'tool_use', id, name, input };
|
|
}
|
|
|
|
function toolOk(id: string): ToolResultEvent {
|
|
return { kind: 'tool_result', toolUseId: id, content: '', isError: false };
|
|
}
|
|
|
|
function todoWrite(
|
|
todos: Array<{ content: string; status: 'pending' | 'in_progress' | 'completed'; activeForm?: string }>,
|
|
): ToolUseEvent {
|
|
return toolUse('TodoWrite', { todos }, 'todo-write');
|
|
}
|
|
|
|
describe('FileWorkspace design-system project surface', () => {
|
|
it('keeps project-backed design systems inside the normal workspace tabs with inline preview cards', () => {
|
|
const markup = renderToStaticMarkup(
|
|
<FileWorkspace
|
|
projectId="ds-acme"
|
|
projectKind="prototype"
|
|
files={[
|
|
workspaceFile('DESIGN.md'),
|
|
workspaceFile('colors_and_type.css'),
|
|
workspaceFile('preview/typography-specimens.html'),
|
|
workspaceFile('preview/colors-primary.html'),
|
|
workspaceFile('preview/spacing-tokens.html'),
|
|
workspaceFile('ui_kits/app/index.html'),
|
|
workspaceFile('preview/brand-assets.html'),
|
|
]}
|
|
liveArtifacts={[]}
|
|
onRefreshFiles={vi.fn()}
|
|
isDeck={false}
|
|
tabsState={{ tabs: [], active: null }}
|
|
onTabsStateChange={vi.fn()}
|
|
designSystemProject={designSystem()}
|
|
/>,
|
|
);
|
|
|
|
expect(markup).toContain('data-testid="design-system-project-tab"');
|
|
expect(markup).toContain('data-testid="design-files-tab"');
|
|
expect(markup).toContain('Review draft design system');
|
|
expect(markup).not.toContain('<h2>Needs review</h2>');
|
|
expect(markup).toContain('Type');
|
|
expect(markup).toContain('Colors');
|
|
expect(markup).toContain('Spacing');
|
|
expect(markup).toContain('Components');
|
|
expect(markup).toContain('Brand');
|
|
expect(markup).toContain('typography-specimens');
|
|
expect(markup).toContain('colors-primary');
|
|
expect(markup).toContain('spacing-tokens');
|
|
expect(markup).toContain('app');
|
|
expect(markup).toContain('brand-assets');
|
|
expect(markup).toContain('<iframe');
|
|
expect(markup).not.toContain('Preview cards will appear here as the agent creates them.');
|
|
});
|
|
|
|
it('shows the creating state while the initial design-system project is still source-only', () => {
|
|
const markup = renderToStaticMarkup(
|
|
<FileWorkspace
|
|
projectId="ds-acme"
|
|
projectKind="prototype"
|
|
files={[workspaceFile('context/source-context.md')]}
|
|
liveArtifacts={[]}
|
|
onRefreshFiles={vi.fn()}
|
|
isDeck={false}
|
|
streaming
|
|
tabsState={{ tabs: [], active: null }}
|
|
onTabsStateChange={vi.fn()}
|
|
designSystemProject={designSystem({ provenance: { companyBlurb: 'Acme analytics workspace' } })}
|
|
designSystemActivityEvents={[
|
|
todoWrite([
|
|
{ content: 'Create README.md with high-level company/product understanding', status: 'in_progress' },
|
|
{ content: 'Create colors_and_type.css with CSS variables', status: 'pending' },
|
|
]),
|
|
]}
|
|
/>,
|
|
);
|
|
|
|
expect(markup).toContain('Creating your design system...');
|
|
expect(markup).toContain('Keep this tab open. You can come back in a few minutes.');
|
|
expect(markup).toContain('role="progressbar"');
|
|
expect(markup).not.toContain('Review draft design system');
|
|
});
|
|
|
|
it('keeps generated preview cards hidden until the initial run finishes', () => {
|
|
const markup = renderToStaticMarkup(
|
|
<FileWorkspace
|
|
projectId="ds-acme"
|
|
projectKind="prototype"
|
|
files={[
|
|
workspaceFile('DESIGN.md'),
|
|
workspaceFile('preview/typography-specimens.html'),
|
|
workspaceFile('preview/colors-primary.html'),
|
|
workspaceFile('ui_kits/app/index.html'),
|
|
]}
|
|
liveArtifacts={[]}
|
|
onRefreshFiles={vi.fn()}
|
|
isDeck={false}
|
|
streaming
|
|
tabsState={{ tabs: [], active: null }}
|
|
onTabsStateChange={vi.fn()}
|
|
designSystemProject={designSystem()}
|
|
designSystemActivityEvents={[
|
|
toolUse('Write', { file_path: '/project/preview/typography-specimens.html' }, 'write-preview'),
|
|
]}
|
|
/>,
|
|
);
|
|
|
|
expect(markup).toContain('Creating your design system...');
|
|
expect(markup).not.toContain('Review draft design system');
|
|
expect(markup).not.toContain('typography-specimens');
|
|
expect(markup).not.toContain('<iframe');
|
|
});
|
|
|
|
it('keeps source evidence files out of the Design System review tab', () => {
|
|
const container = renderWorkspace(
|
|
<FileWorkspace
|
|
projectId="ds-acme"
|
|
projectKind="prototype"
|
|
files={[
|
|
workspaceFile('DESIGN.md'),
|
|
workspaceFile('context/source-context.md'),
|
|
workspaceFile('context/github/acme-product.md'),
|
|
workspaceFile('context/github/acme-product/files/src/components/Button.tsx'),
|
|
workspaceFile('assets/logo.svg'),
|
|
workspaceFile('preview/brand-assets.html'),
|
|
]}
|
|
liveArtifacts={[]}
|
|
onRefreshFiles={vi.fn()}
|
|
isDeck={false}
|
|
tabsState={{ tabs: [], active: null }}
|
|
onTabsStateChange={vi.fn()}
|
|
designSystemProject={designSystem({
|
|
provenance: {
|
|
githubUrls: ['https://github.com/acme/product'],
|
|
sourceNotes: 'GitHub metadata: React UI library with token CSS.',
|
|
},
|
|
})}
|
|
/>,
|
|
);
|
|
|
|
expect(container.textContent).toContain('Brand');
|
|
expect(container.textContent).toContain('brand-assets');
|
|
expect(container.textContent).not.toContain('context/github/acme-product.md');
|
|
expect(container.textContent).not.toContain('GitHub metadata: React UI library with token CSS.');
|
|
});
|
|
|
|
it('marks a section for review after the latest agent run edits it', () => {
|
|
const markup = renderToStaticMarkup(
|
|
<FileWorkspace
|
|
projectId="ds-acme"
|
|
projectKind="prototype"
|
|
files={[workspaceFile('DESIGN.md'), workspaceFile('preview/colors.html')]}
|
|
liveArtifacts={[]}
|
|
onRefreshFiles={vi.fn()}
|
|
isDeck={false}
|
|
tabsState={{ tabs: [], active: null }}
|
|
onTabsStateChange={vi.fn()}
|
|
designSystemProject={designSystem()}
|
|
designSystemActivityEvents={[
|
|
toolUse('Write', { file_path: '/project/preview/colors.html' }, 'write-preview'),
|
|
toolOk('write-preview'),
|
|
]}
|
|
/>,
|
|
);
|
|
|
|
expect(markup).toContain('This section changed during the latest run. Review it before publishing.');
|
|
});
|
|
|
|
it('blocks publishing GitHub-backed design systems until connector evidence snapshots exist', async () => {
|
|
const container = renderWorkspace(
|
|
<FileWorkspace
|
|
projectId="ds-acme"
|
|
projectKind="prototype"
|
|
files={[
|
|
workspaceFile('DESIGN.md'),
|
|
workspaceFile('context/source-context.md'),
|
|
workspaceFile('preview/colors.html'),
|
|
]}
|
|
liveArtifacts={[]}
|
|
onRefreshFiles={vi.fn()}
|
|
isDeck={false}
|
|
tabsState={{ tabs: [], active: null }}
|
|
onTabsStateChange={vi.fn()}
|
|
designSystemProject={designSystem({
|
|
provenance: {
|
|
companyBlurb: 'Acme analytics workspace',
|
|
githubUrls: ['https://github.com/acme/product'],
|
|
},
|
|
})}
|
|
/>,
|
|
);
|
|
const publishToggle = container.querySelector<HTMLInputElement>(
|
|
'.ds-project-publish-card input[type="checkbox"]',
|
|
);
|
|
|
|
expect(container.textContent).toContain('Waiting for GitHub connector evidence');
|
|
expect(publishToggle?.disabled).toBe(true);
|
|
|
|
await act(async () => {
|
|
publishToggle?.click();
|
|
await Promise.resolve();
|
|
});
|
|
|
|
expect(registryMocks.updateDesignSystemDraft).not.toHaveBeenCalled();
|
|
});
|
|
});
|