From 217e9718bb87fdacba438ed4f87b9fbd905d06f1 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Fri, 16 Jan 2026 01:03:13 +0000 Subject: [PATCH] Generated by Spark: Designers for Playwright, Storybook and unit testing. --- PRD.md | 29 ++ src/App.tsx | 45 ++- src/components/PlaywrightDesigner.tsx | 335 ++++++++++++++++++++++ src/components/StorybookDesigner.tsx | 324 +++++++++++++++++++++ src/components/UnitTestDesigner.tsx | 396 ++++++++++++++++++++++++++ src/lib/generators.ts | 156 +++++++++- src/types/project.ts | 46 +++ 7 files changed, 1327 insertions(+), 4 deletions(-) create mode 100644 src/components/PlaywrightDesigner.tsx create mode 100644 src/components/StorybookDesigner.tsx create mode 100644 src/components/UnitTestDesigner.tsx diff --git a/PRD.md b/PRD.md index 76ca03f..c3b7ac9 100644 --- a/PRD.md +++ b/PRD.md @@ -47,6 +47,27 @@ This is a full-featured low-code IDE with multiple integrated tools (code editor - **Progression**: Select or create theme variant → Adjust standard colors or add custom colors → Configure typography and spacing → Preview updates live across all variants → Switch between variants → Export theme configuration with all variants - **Success criteria**: Color pickers work; custom colors can be added/removed; multiple theme variants persist; AI themes match descriptions and have good contrast; generates valid MUI theme code for all variants including light and dark modes +### Playwright E2E Test Designer +- **Functionality**: Visual designer for Playwright end-to-end tests with step-by-step action configuration and AI-powered test generation +- **Purpose**: Create comprehensive E2E tests without writing Playwright code manually +- **Trigger**: Opening the Playwright tab +- **Progression**: Create test suite manually or with AI → Add test steps (navigate, click, fill, expect) → Configure selectors and assertions → AI can generate complete test flows → Export Playwright test files +- **Success criteria**: Can create tests with multiple steps; selectors and assertions are properly configured; AI-generated tests are executable; generates valid Playwright test code + +### Storybook Story Designer +- **Functionality**: Visual designer for Storybook stories with component args/props configuration and AI story generation +- **Purpose**: Document component variations and states without manually writing story files +- **Trigger**: Opening the Storybook tab +- **Progression**: Create story manually or with AI → Configure component name and story name → Add args/props → Organize by category → AI can suggest appropriate variations → Export Storybook story files +- **Success criteria**: Can create stories for any component; args can be added/edited with type detection; organized by categories; AI stories showcase meaningful variations; generates valid Storybook CSF3 format + +### Unit Test Designer +- **Functionality**: Visual designer for unit tests with test case and assertion management, supports component/function/hook/integration tests, and AI test generation +- **Purpose**: Create comprehensive test suites with proper setup, assertions, and teardown without writing test code manually +- **Trigger**: Opening the Unit Tests tab +- **Progression**: Create test suite manually or with AI → Select test type (component/function/hook/integration) → Add test cases → Configure setup, assertions, and teardown → AI can generate complete test suites → Export test files for Vitest/React Testing Library +- **Success criteria**: Can create test suites for different types; test cases have multiple assertions; setup/teardown code is optional; AI tests are comprehensive; generates valid Vitest test code + ### Project Generator - **Functionality**: Exports complete Next.js project with all configurations - **Purpose**: Converts visual designs into downloadable, runnable applications @@ -63,6 +84,10 @@ This is a full-featured low-code IDE with multiple integrated tools (code editor - **AI Generation Failures**: Provide clear error messages and fallback to manual editing when AI requests fail - **Rate Limiting**: Handle OpenAI API rate limits gracefully with user-friendly messages - **Invalid AI Responses**: Validate and sanitize AI-generated code before insertion +- **Empty Test Suites**: Show helpful empty states with guidance for creating first test/story +- **Invalid Test Selectors**: Warn when Playwright selectors might be problematic +- **Missing Test Assertions**: Highlight test cases without assertions as incomplete +- **Storybook Args Type Mismatch**: Auto-detect arg types and provide appropriate input controls ## Design Direction The design should evoke a professional IDE environment while remaining approachable - think Visual Studio Code meets Figma. Clean panels, clear hierarchy, and purposeful use of space to avoid overwhelming users with options. @@ -121,9 +146,13 @@ Animations should feel responsive and purposeful - quick panel transitions (200m - Database (database icon) for models - Tree (tree-structure icon) for components - PaintBrush (paint-brush icon) for styling + - Play (play icon) for Playwright E2E tests + - BookOpen (book-open icon) for Storybook stories + - Flask (flask icon) for unit tests - FileCode (file-code icon) for individual files - Plus (plus icon) for create actions - Download (download icon) for export + - Sparkle (sparkle icon) for AI generation - **Spacing**: - Panel padding: p-6 (24px) for main content areas diff --git a/src/App.tsx b/src/App.tsx index 1427f45..28283f9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,14 +4,17 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Button } from '@/components/ui/button' import { Card } from '@/components/ui/card' import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable' -import { Code, Database, Tree, PaintBrush, Download, Sparkle } from '@phosphor-icons/react' -import { ProjectFile, PrismaModel, ComponentNode, ThemeConfig } from '@/types/project' +import { Code, Database, Tree, PaintBrush, Download, Sparkle, Flask, BookOpen, Play } from '@phosphor-icons/react' +import { ProjectFile, PrismaModel, ComponentNode, ThemeConfig, PlaywrightTest, StorybookStory, UnitTest } from '@/types/project' import { CodeEditor } from '@/components/CodeEditor' import { ModelDesigner } from '@/components/ModelDesigner' import { ComponentTreeBuilder } from '@/components/ComponentTreeBuilder' import { StyleDesigner } from '@/components/StyleDesigner' import { FileExplorer } from '@/components/FileExplorer' -import { generateNextJSProject, generatePrismaSchema, generateMUITheme } from '@/lib/generators' +import { PlaywrightDesigner } from '@/components/PlaywrightDesigner' +import { StorybookDesigner } from '@/components/StorybookDesigner' +import { UnitTestDesigner } from '@/components/UnitTestDesigner' +import { generateNextJSProject, generatePrismaSchema, generateMUITheme, generatePlaywrightTests, generateStorybookStories, generateUnitTests } from '@/lib/generators' import { AIService } from '@/lib/ai-service' import { toast } from 'sonner' import { @@ -90,6 +93,9 @@ function App() { const [models, setModels] = useKV('project-models', []) const [components, setComponents] = useKV('project-components', []) const [theme, setTheme] = useKV('project-theme', DEFAULT_THEME) + const [playwrightTests, setPlaywrightTests] = useKV('project-playwright-tests', []) + const [storybookStories, setStorybookStories] = useKV('project-storybook-stories', []) + const [unitTests, setUnitTests] = useKV('project-unit-tests', []) const [activeFileId, setActiveFileId] = useState((files || [])[0]?.id || null) const [activeTab, setActiveTab] = useState('code') const [exportDialogOpen, setExportDialogOpen] = useState(false) @@ -99,6 +105,9 @@ function App() { const safeModels = models || [] const safeComponents = components || [] const safeTheme = theme || DEFAULT_THEME + const safePlaywrightTests = playwrightTests || [] + const safeStorybookStories = storybookStories || [] + const safeUnitTests = unitTests || [] const handleFileChange = (fileId: string, content: string) => { setFiles((currentFiles) => @@ -124,11 +133,17 @@ function App() { const prismaSchema = generatePrismaSchema(safeModels) const themeCode = generateMUITheme(safeTheme) + const playwrightTestCode = generatePlaywrightTests(safePlaywrightTests) + const storybookFiles = generateStorybookStories(safeStorybookStories) + const unitTestFiles = generateUnitTests(safeUnitTests) const allFiles = { ...projectFiles, 'prisma/schema.prisma': prismaSchema, 'src/theme.ts': themeCode, + 'e2e/tests.spec.ts': playwrightTestCode, + ...storybookFiles, + ...unitTestFiles, ...safeFiles.reduce((acc, file) => { acc[file.path] = file.content return acc @@ -216,6 +231,18 @@ function App() { Styling + + + Playwright + + + + Storybook + + + + Unit Tests + @@ -257,6 +284,18 @@ function App() { + + + + + + + + + + + + diff --git a/src/components/PlaywrightDesigner.tsx b/src/components/PlaywrightDesigner.tsx new file mode 100644 index 0000000..4f0a947 --- /dev/null +++ b/src/components/PlaywrightDesigner.tsx @@ -0,0 +1,335 @@ +import { useState } from 'react' +import { PlaywrightTest, PlaywrightStep } from '@/types/project' +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' +import { ScrollArea } from '@/components/ui/scroll-area' +import { Textarea } from '@/components/ui/textarea' +import { Plus, Trash, Play, Sparkle } from '@phosphor-icons/react' +import { toast } from 'sonner' + +interface PlaywrightDesignerProps { + tests: PlaywrightTest[] + onTestsChange: (tests: PlaywrightTest[]) => void +} + +export function PlaywrightDesigner({ tests, onTestsChange }: PlaywrightDesignerProps) { + const [selectedTestId, setSelectedTestId] = useState(tests[0]?.id || null) + const selectedTest = tests.find(t => t.id === selectedTestId) + + const handleAddTest = () => { + const newTest: PlaywrightTest = { + id: `test-${Date.now()}`, + name: 'New Test', + description: '', + pageUrl: '/', + steps: [] + } + onTestsChange([...tests, newTest]) + setSelectedTestId(newTest.id) + } + + const handleDeleteTest = (testId: string) => { + onTestsChange(tests.filter(t => t.id !== testId)) + if (selectedTestId === testId) { + const remaining = tests.filter(t => t.id !== testId) + setSelectedTestId(remaining[0]?.id || null) + } + } + + const handleUpdateTest = (testId: string, updates: Partial) => { + onTestsChange( + tests.map(t => t.id === testId ? { ...t, ...updates } : t) + ) + } + + const handleAddStep = () => { + if (!selectedTest) return + const newStep: PlaywrightStep = { + id: `step-${Date.now()}`, + action: 'click', + selector: '', + value: '' + } + handleUpdateTest(selectedTest.id, { + steps: [...selectedTest.steps, newStep] + }) + } + + const handleUpdateStep = (stepId: string, updates: Partial) => { + if (!selectedTest) return + handleUpdateTest(selectedTest.id, { + steps: selectedTest.steps.map(s => s.id === stepId ? { ...s, ...updates } : s) + }) + } + + const handleDeleteStep = (stepId: string) => { + if (!selectedTest) return + handleUpdateTest(selectedTest.id, { + steps: selectedTest.steps.filter(s => s.id !== stepId) + }) + } + + const handleGenerateWithAI = async () => { + const description = prompt('Describe the E2E test you want to generate:') + if (!description) return + + try { + toast.info('Generating test with AI...') + const promptText = `You are a Playwright test generator. Create an E2E test based on: "${description}" + +Return a valid JSON object with a single property "test": +{ + "test": { + "id": "unique-id", + "name": "Test Name", + "description": "What this test does", + "pageUrl": "/path", + "steps": [ + { + "id": "step-id", + "action": "navigate" | "click" | "fill" | "expect" | "wait" | "select" | "check" | "uncheck", + "selector": "css selector or text", + "value": "value for fill/select actions", + "assertion": "expected value for expect action", + "timeout": 5000 + } + ] + } +} + +Create a complete test flow with appropriate selectors and assertions.` + + const response = await window.spark.llm(promptText, 'gpt-4o', true) + const parsed = JSON.parse(response) + onTestsChange([...tests, parsed.test]) + setSelectedTestId(parsed.test.id) + toast.success('Test generated successfully!') + } catch (error) { + console.error(error) + toast.error('Failed to generate test') + } + } + + return ( +
+
+
+

E2E Tests

+
+ + +
+
+ +
+ {tests.map(test => ( +
setSelectedTestId(test.id)} + > +
+
{test.name}
+
{test.pageUrl}
+
{test.steps.length} steps
+
+ +
+ ))} + {tests.length === 0 && ( +
+ No tests yet. Click + to create one. +
+ )} +
+
+
+ +
+ {selectedTest ? ( +
+
+

Test Configuration

+ +
+ + + + Test Details + + +
+ + handleUpdateTest(selectedTest.id, { name: e.target.value })} + /> +
+
+ +