mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-29 16:14:55 +00:00
Error: src/components/FeatureIdeaCloud.tsx(920,37): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/components/PlaywrightDesigner.tsx(105,37): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/components/StorybookDesigner.tsx(115,37): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/components/TemplateExplorer.tsx(40,31): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/components/TemplateExplorer.tsx(44,32): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/components/UnitTestDesigner.tsx(138,37): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/data/use-seed-data.ts(23,19): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/data/use-seed-data.ts(30,33): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/data/use-seed-data.ts(40,24): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/data/use-seed-data.ts(63,19): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/data/use-seed-data.ts(71,22): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/data/use-seed-data.ts(86,19): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/data/use-seed-data.ts(91,33): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/data/use-seed-data.ts(95,22): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/data/use-seed-templates.ts(87,22): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/data/use-seed-templates.ts(102,33): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/data/use-seed-templates.ts(104,22): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/data/use-seed-templates.ts(126,43): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/data/use-seed-templates.ts(129,24): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/data/use-seed-templates.ts(131,24): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/json-ui/use-data-sources.ts(25,40): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/json-ui/use-data-sources.ts(47,20): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/orchestration/use-actions.ts(119,31): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/use-component-tree-loader.ts(22,19): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/use-component-tree-loader.ts(27,42): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/use-component-tree-loader.ts(31,22): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/use-component-tree-loader.ts(43,24): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/use-component-tree-loader.ts(60,17): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/use-component-tree-loader.ts(65,32): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/use-component-tree-loader.ts(90,19): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/hooks/use-component-tree-loader.ts(94,20): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/project-service.ts(21,39): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/project-service.ts(61,18): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/project-service.ts(63,38): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/project-service.ts(66,20): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/project-service.ts(74,36): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/project-service.ts(83,18): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/project-service.ts(85,38): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/project-service.ts(87,18): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/protected-llm-service.ts(39,31): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/unified-storage.ts(199,17): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/unified-storage.ts(200,25): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/unified-storage.ts(204,17): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/unified-storage.ts(205,18): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/unified-storage.ts(209,17): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/unified-storage.ts(210,18): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/unified-storage.ts(214,17): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/unified-storage.ts(215,25): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/unified-storage.ts(219,17): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/unified-storage.ts(220,34): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/unified-storage.ts(221,49): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: src/lib/unified-storage.ts(409,18): error TS2339: Property 'spark' does not exist on type 'Window & typeof globalThis'. Error: Process completed with exit code 2.
338 lines
14 KiB
TypeScript
338 lines
14 KiB
TypeScript
/// <reference path="../global.d.ts" />
|
|
|
|
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<string | null>(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<PlaywrightTest>) => {
|
|
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<PlaywrightStep>) => {
|
|
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 (
|
|
<div className="h-full flex">
|
|
<div className="w-80 border-r border-border bg-card">
|
|
<div className="p-4 border-b border-border flex items-center justify-between">
|
|
<h2 className="font-semibold text-sm">E2E Tests</h2>
|
|
<div className="flex gap-1">
|
|
<Button size="sm" variant="outline" onClick={handleGenerateWithAI}>
|
|
<Sparkle size={14} weight="duotone" />
|
|
</Button>
|
|
<Button size="sm" onClick={handleAddTest}>
|
|
<Plus size={14} />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<ScrollArea className="h-[calc(100vh-200px)]">
|
|
<div className="p-2 space-y-1">
|
|
{tests.map(test => (
|
|
<div
|
|
key={test.id}
|
|
className={`p-3 rounded-md cursor-pointer flex items-start justify-between group ${
|
|
selectedTestId === test.id ? 'bg-accent text-accent-foreground' : 'hover:bg-muted'
|
|
}`}
|
|
onClick={() => setSelectedTestId(test.id)}
|
|
>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="font-medium text-sm truncate">{test.name}</div>
|
|
<div className="text-xs text-muted-foreground truncate">{test.pageUrl}</div>
|
|
<div className="text-xs text-muted-foreground">{test.steps.length} steps</div>
|
|
</div>
|
|
<Button
|
|
size="sm"
|
|
variant="ghost"
|
|
className="opacity-0 group-hover:opacity-100"
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
handleDeleteTest(test.id)
|
|
}}
|
|
>
|
|
<Trash size={14} />
|
|
</Button>
|
|
</div>
|
|
))}
|
|
{tests.length === 0 && (
|
|
<div className="p-8 text-center text-sm text-muted-foreground">
|
|
No tests yet. Click + to create one.
|
|
</div>
|
|
)}
|
|
</div>
|
|
</ScrollArea>
|
|
</div>
|
|
|
|
<div className="flex-1 p-6">
|
|
{selectedTest ? (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<h2 className="text-2xl font-bold">Test Configuration</h2>
|
|
<Button variant="outline">
|
|
<Play size={16} className="mr-2" weight="fill" />
|
|
Run Test
|
|
</Button>
|
|
</div>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Test Details</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="test-name">Test Name</Label>
|
|
<Input
|
|
id="test-name"
|
|
value={selectedTest.name}
|
|
onChange={e => handleUpdateTest(selectedTest.id, { name: e.target.value })}
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="test-description">Description</Label>
|
|
<Textarea
|
|
id="test-description"
|
|
value={selectedTest.description}
|
|
onChange={e => handleUpdateTest(selectedTest.id, { description: e.target.value })}
|
|
placeholder="What does this test verify?"
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="test-url">Page URL</Label>
|
|
<Input
|
|
id="test-url"
|
|
value={selectedTest.pageUrl}
|
|
onChange={e => handleUpdateTest(selectedTest.id, { pageUrl: e.target.value })}
|
|
placeholder="/login"
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<CardTitle>Test Steps</CardTitle>
|
|
<CardDescription>Define the actions for this test</CardDescription>
|
|
</div>
|
|
<Button size="sm" onClick={handleAddStep}>
|
|
<Plus size={14} className="mr-1" />
|
|
Add Step
|
|
</Button>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ScrollArea className="h-[400px]">
|
|
<div className="space-y-4">
|
|
{selectedTest.steps.map((step, index) => (
|
|
<Card key={step.id}>
|
|
<CardContent className="pt-4 space-y-3">
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-sm font-semibold">Step {index + 1}</span>
|
|
<Button
|
|
size="sm"
|
|
variant="ghost"
|
|
onClick={() => handleDeleteStep(step.id)}
|
|
>
|
|
<Trash size={14} />
|
|
</Button>
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<div className="space-y-2">
|
|
<Label>Action</Label>
|
|
<Select
|
|
value={step.action}
|
|
onValueChange={(value: any) => handleUpdateStep(step.id, { action: value })}
|
|
>
|
|
<SelectTrigger>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="navigate">Navigate</SelectItem>
|
|
<SelectItem value="click">Click</SelectItem>
|
|
<SelectItem value="fill">Fill</SelectItem>
|
|
<SelectItem value="expect">Expect</SelectItem>
|
|
<SelectItem value="wait">Wait</SelectItem>
|
|
<SelectItem value="select">Select</SelectItem>
|
|
<SelectItem value="check">Check</SelectItem>
|
|
<SelectItem value="uncheck">Uncheck</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
{step.action !== 'navigate' && step.action !== 'wait' && (
|
|
<div className="space-y-2">
|
|
<Label>Selector</Label>
|
|
<Input
|
|
value={step.selector || ''}
|
|
onChange={e => handleUpdateStep(step.id, { selector: e.target.value })}
|
|
placeholder="button, #login, [data-testid='submit']"
|
|
/>
|
|
</div>
|
|
)}
|
|
{(step.action === 'fill' || step.action === 'select') && (
|
|
<div className="space-y-2 col-span-2">
|
|
<Label>Value</Label>
|
|
<Input
|
|
value={step.value || ''}
|
|
onChange={e => handleUpdateStep(step.id, { value: e.target.value })}
|
|
placeholder="Text to enter"
|
|
/>
|
|
</div>
|
|
)}
|
|
{step.action === 'expect' && (
|
|
<div className="space-y-2 col-span-2">
|
|
<Label>Assertion</Label>
|
|
<Input
|
|
value={step.assertion || ''}
|
|
onChange={e => handleUpdateStep(step.id, { assertion: e.target.value })}
|
|
placeholder="toBeVisible(), toHaveText('...')"
|
|
/>
|
|
</div>
|
|
)}
|
|
{step.action === 'wait' && (
|
|
<div className="space-y-2">
|
|
<Label>Timeout (ms)</Label>
|
|
<Input
|
|
type="number"
|
|
value={step.timeout || 1000}
|
|
onChange={e => handleUpdateStep(step.id, { timeout: parseInt(e.target.value) })}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
{selectedTest.steps.length === 0 && (
|
|
<div className="py-12 text-center text-sm text-muted-foreground">
|
|
No steps yet. Click "Add Step" to create test actions.
|
|
</div>
|
|
)}
|
|
</div>
|
|
</ScrollArea>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
) : (
|
|
<div className="h-full flex items-center justify-center">
|
|
<div className="text-center">
|
|
<Play size={48} className="mx-auto mb-4 text-muted-foreground" />
|
|
<p className="text-lg font-medium mb-2">No test selected</p>
|
|
<p className="text-sm text-muted-foreground mb-4">
|
|
Create or select a test to configure
|
|
</p>
|
|
<Button onClick={handleAddTest}>
|
|
<Plus size={16} className="mr-2" />
|
|
Create Test
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|