feat(FN-1640): add Frontend UX Design built-in workflow template

- Add Frontend UX Design template for verifying UI/UX design implementation
- Include WCAG 2.1 compliance checks, design system adherence, and user flow validation
- Add template to WorkflowStepManager with category-based organization
- Update documentation with new template description
- Add tests for template API endpoints and workflow step manager
This commit is contained in:
gsxdsm
2026-04-13 17:41:33 -07:00
parent b1a34abda5
commit dbaa93aabc
8 changed files with 196 additions and 16 deletions

View File

@@ -5622,6 +5622,23 @@ Task with acceptance criteria
expect(steps.filter((step) => step.templateId === "browser-verification")).toHaveLength(1);
});
it("should materialize frontend-ux-design built-in template when creating a task", async () => {
const task = await store.createTask({
description: "Task with frontend UX design review",
enabledWorkflowSteps: ["frontend-ux-design"],
});
expect(task.enabledWorkflowSteps).toEqual(["WS-001"]);
const step = await store.getWorkflowStep("WS-001");
expect(step).toMatchObject({
id: "WS-001",
templateId: "frontend-ux-design",
name: "Frontend UX Design",
toolMode: "readonly",
});
});
it("should not set enabledWorkflowSteps when empty array provided", async () => {
const task = await store.createTask({
description: "Task without workflow steps",
@@ -5880,6 +5897,19 @@ Task with acceptance criteria
});
});
it("should resolve frontend-ux-design built-in template from getWorkflowStep", async () => {
const step = await store.getWorkflowStep("frontend-ux-design");
expect(step).toMatchObject({
id: "frontend-ux-design",
templateId: "frontend-ux-design",
name: "Frontend UX Design",
mode: "prompt",
phase: "pre-merge",
toolMode: "readonly",
});
});
// ── Workflow Step Phase ──────────────────────────────────────────────
it("should default phase to 'pre-merge' when creating a workflow step", async () => {

View File

@@ -365,6 +365,33 @@ Use these agent-browser commands for verification:
Note: Refs (@e1, @e2) are invalidated after page navigation. Re-snapshot after clicking links or form submissions.`,
},
{
id: "frontend-ux-design",
name: "Frontend UX Design",
description: "Verify visual polish and consistency with existing UI patterns and design tokens",
category: "Quality",
icon: "layout-grid",
toolMode: "readonly",
prompt: `You are a UX design reviewer. Verify frontend changes maintain visual polish and consistency with existing UI patterns and design tokens.
Design System Review:
1. **Visual Hierarchy** — Check that the changes maintain consistent heading levels, content flow, and information architecture
2. **Spacing and Typography** — Verify consistent spacing (margins, padding, gaps) and typography scale usage
3. **Color and Token Consistency** — Check that CSS custom properties and design tokens are used correctly; no hardcoded color values that bypass the design system
4. **Component Reuse** — Verify existing UI components are reused instead of creating one-off styling; identify any duplication that could be refactored
5. **Responsive Behavior** — Check that layouts adapt properly across viewport sizes and maintain usability on mobile
6. **Fit with Design Language** — Verify the visual style matches existing patterns (border radius, shadows, transitions, icon style, etc.)
Files to Review:
- Modified UI components (React, Vue, Angular, HTML)
- CSS/SCSS/styled-component files
- Design token or theme configuration files
Output Requirements:
- If design is consistent and polished: call task_done() with success status
- If issues found: describe each finding with specific file paths and suggested corrections via task_log()
- Prioritize issues by impact: layout breaks > visual inconsistency > style preferences`,
},
];
export interface PrInfo {

View File

@@ -98,6 +98,8 @@ function getTemplateIcon(iconName: string | undefined) {
return Eye;
case "globe":
return Globe;
case "layout-grid":
return LayoutGrid;
default:
return CheckCircle;
}

View File

@@ -59,21 +59,47 @@ vi.mock("../../api", () => ({
icon: "globe",
toolMode: "coding",
},
{
id: "frontend-ux-design",
name: "Frontend UX Design",
description: "Verify visual polish and consistency with existing UI patterns and design tokens",
prompt: "Test prompt",
category: "Quality",
icon: "layout-grid",
toolMode: "readonly",
},
],
})),
createWorkflowStepFromTemplate: vi.fn(() => Promise.resolve({
id: "WS-010",
templateId: "browser-verification",
name: "Browser Verification",
description: "Verify web application functionality using browser automation",
mode: "prompt",
phase: "pre-merge",
prompt: "Test prompt",
toolMode: "coding",
enabled: true,
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
})),
createWorkflowStepFromTemplate: vi.fn((templateId?: string) => {
if (templateId === "frontend-ux-design") {
return Promise.resolve({
id: "WS-011",
templateId: "frontend-ux-design",
name: "Frontend UX Design",
description: "Verify visual polish and consistency with existing UI patterns and design tokens",
mode: "prompt",
phase: "pre-merge",
prompt: "Test prompt",
toolMode: "readonly",
enabled: true,
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
});
}
return Promise.resolve({
id: "WS-010",
templateId: "browser-verification",
name: "Browser Verification",
description: "Verify web application functionality using browser automation",
mode: "prompt",
phase: "pre-merge",
prompt: "Test prompt",
toolMode: "coding",
enabled: true,
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
});
}),
fetchScripts: vi.fn(() => Promise.resolve({ test: "pnpm test", lint: "pnpm lint" })),
fetchModels: vi.fn(() => Promise.resolve({
models: [
@@ -678,6 +704,58 @@ describe("WorkflowStepManager templates tab", () => {
expect(addToast).toHaveBeenCalledWith("Added Browser Verification workflow step", "success");
});
});
it("renders frontend-ux-design template", async () => {
vi.mocked(fetchWorkflowSteps).mockResolvedValueOnce([]);
render(<WorkflowStepManager isOpen={true} onClose={onClose} addToast={addToast} />);
await waitFor(() => {
expect(screen.getByTestId("tab-templates")).toBeInTheDocument();
});
fireEvent.click(screen.getByTestId("tab-templates"));
await waitFor(() => {
expect(screen.getByTestId("template-frontend-ux-design")).toBeInTheDocument();
expect(screen.getByText("Frontend UX Design")).toBeInTheDocument();
});
});
it("displays the LayoutGrid icon for frontend-ux-design template", async () => {
vi.mocked(fetchWorkflowSteps).mockResolvedValueOnce([]);
render(<WorkflowStepManager isOpen={true} onClose={onClose} addToast={addToast} />);
await waitFor(() => {
expect(screen.getByTestId("tab-templates")).toBeInTheDocument();
});
fireEvent.click(screen.getByTestId("tab-templates"));
const templateCard = await screen.findByTestId("template-frontend-ux-design");
expect(templateCard.querySelector(".lucide-layout-grid")).toBeInTheDocument();
});
it("can add frontend-ux-design template as a workflow step", async () => {
vi.mocked(fetchWorkflowSteps).mockResolvedValue([]);
render(<WorkflowStepManager isOpen={true} onClose={onClose} addToast={addToast} />);
await waitFor(() => {
expect(screen.getByTestId("tab-templates")).toBeInTheDocument();
});
fireEvent.click(screen.getByTestId("tab-templates"));
const templateCard = await screen.findByTestId("template-frontend-ux-design");
fireEvent.click(within(templateCard).getByTestId("add-template-frontend-ux-design"));
await waitFor(() => {
expect(createWorkflowStepFromTemplate).toHaveBeenCalledWith("frontend-ux-design", undefined);
expect(addToast).toHaveBeenCalledWith("Added Frontend UX Design workflow step", "success");
});
});
});
describe("WorkflowStepManager theme class structure", () => {

View File

@@ -11024,6 +11024,8 @@ describe("GET /workflow-step-templates", () => {
expect(ids).toContain("security-audit");
expect(ids).toContain("performance-review");
expect(ids).toContain("accessibility-check");
expect(ids).toContain("browser-verification");
expect(ids).toContain("frontend-ux-design");
});
});
@@ -11100,6 +11102,37 @@ describe("POST /workflow-step-templates/:id/create", () => {
});
});
it("creates workflow step from frontend-ux-design template", async () => {
const created = {
id: "WS-003",
name: "Frontend UX Design",
description: "Verify visual polish and consistency with existing UI patterns and design tokens",
prompt: expect.stringContaining("UX design reviewer"),
toolMode: "readonly",
enabled: true,
createdAt: "2026-01-01",
updatedAt: "2026-01-01",
};
(store.listWorkflowSteps as ReturnType<typeof vi.fn>).mockResolvedValueOnce([]);
(store.createWorkflowStep as ReturnType<typeof vi.fn>).mockResolvedValueOnce(created);
const res = await REQUEST(buildApp(), "POST", "/api/workflow-step-templates/frontend-ux-design/create", JSON.stringify({}), {
"Content-Type": "application/json",
});
expect(res.status).toBe(201);
expect(res.body.name).toBe("Frontend UX Design");
expect(res.body.toolMode).toBe("readonly");
expect(store.createWorkflowStep).toHaveBeenCalledWith({
templateId: "frontend-ux-design",
name: "Frontend UX Design",
description: "Verify visual polish and consistency with existing UI patterns and design tokens",
prompt: expect.stringContaining("UX design reviewer"),
toolMode: "readonly",
enabled: true,
});
});
it("returns 404 for non-existent template", async () => {
const res = await REQUEST(buildApp(), "POST", "/api/workflow-step-templates/nonexistent/create", JSON.stringify({}), {
"Content-Type": "application/json",