Update app_spec.txt and coding_prompt.md for improved clarity and consistency

- Updated references to `app_spec.txt` and `feature_list.json` in app_spec.txt to include the correct path.
- Enhanced coding_prompt.md by incorporating testing utilities for better test management and readability.
- Added new utility functions in tests/utils.ts to streamline test interactions.

This commit aims to improve documentation accuracy and maintainability of testing practices.
This commit is contained in:
Cody Seibert
2025-12-09 00:45:34 -05:00
parent adad2262c2
commit 2822cdfc32
32 changed files with 1324 additions and 4395 deletions

View File

@@ -1,140 +0,0 @@
import { test, expect } from "@playwright/test";
test.describe("Agent Loop (Plan-Act-Verify)", () => {
test.beforeEach(async ({ page }) => {
// Navigate to the app and create a project
await page.goto("/");
// Create a project first
await page.getByTestId("new-project-card").click();
await page.getByTestId("project-name-input").fill("Test Project");
await page.getByTestId("project-path-input").fill("/test/path");
await page.getByTestId("confirm-create-project").click();
// Wait for board view to load
await expect(page.getByTestId("board-view")).toBeVisible();
});
test("Step 1: Trigger agent on a simple task - auto mode starts", async ({ page }) => {
// Find and click the Auto Mode button
const autoModeButton = page.getByTestId("start-auto-mode");
await expect(autoModeButton).toBeVisible();
// Click to start auto mode
await autoModeButton.click();
// Verify auto mode has started - stop button should now be visible
await expect(page.getByTestId("stop-auto-mode")).toBeVisible({ timeout: 5000 });
});
test("Step 2: detailed logs show Planning phase", async ({ page }) => {
// Start auto mode
await page.getByTestId("start-auto-mode").click();
// Wait for the activity log to appear
await expect(page.getByTestId("stop-auto-mode")).toBeVisible({ timeout: 5000 });
// The activity log panel should appear automatically when auto mode starts
// Wait for planning phase to appear in the activity log
await expect(page.getByTestId("planning-phase-icon")).toBeVisible({ timeout: 5000 });
// Verify the planning message is displayed
await expect(page.getByText("Planning implementation for:")).toBeVisible({ timeout: 5000 });
});
test("Step 3: detailed logs show Action phase", async ({ page }) => {
// Start auto mode
await page.getByTestId("start-auto-mode").click();
// Wait for auto mode to be running
await expect(page.getByTestId("stop-auto-mode")).toBeVisible({ timeout: 5000 });
// Wait for action phase to appear in the activity log
await expect(page.getByTestId("action-phase-icon")).toBeVisible({ timeout: 5000 });
// Verify the action message is displayed
await expect(page.getByText("Executing implementation for:")).toBeVisible({ timeout: 5000 });
});
test("Step 4: detailed logs show Verification phase", async ({ page }) => {
// Start auto mode
await page.getByTestId("start-auto-mode").click();
// Wait for auto mode to be running
await expect(page.getByTestId("stop-auto-mode")).toBeVisible({ timeout: 5000 });
// Wait for verification phase to appear in the activity log
await expect(page.getByTestId("verification-phase-icon")).toBeVisible({ timeout: 5000 });
// Verify the verification message is displayed
await expect(page.getByText("Verifying implementation for:")).toBeVisible({ timeout: 5000 });
});
test("Full agent loop: shows all three phases in sequence", async ({ page }) => {
// Start auto mode
await page.getByTestId("start-auto-mode").click();
// Wait for auto mode to be running
await expect(page.getByTestId("stop-auto-mode")).toBeVisible({ timeout: 5000 });
// Wait for all phases to appear in sequence
// Phase 1: Planning
await expect(page.getByTestId("planning-phase-icon")).toBeVisible({ timeout: 5000 });
await expect(page.getByText("Planning implementation for:")).toBeVisible();
// Phase 2: Action
await expect(page.getByTestId("action-phase-icon")).toBeVisible({ timeout: 5000 });
await expect(page.getByText("Executing implementation for:")).toBeVisible();
// Phase 3: Verification
await expect(page.getByTestId("verification-phase-icon")).toBeVisible({ timeout: 5000 });
await expect(page.getByText("Verifying implementation for:")).toBeVisible();
// Verify verification success message appears
await expect(page.getByText("Verification successful")).toBeVisible({ timeout: 5000 });
});
test("Agent loop can be stopped mid-execution", async ({ page }) => {
// Start auto mode
await page.getByTestId("start-auto-mode").click();
// Wait for auto mode to be running
await expect(page.getByTestId("stop-auto-mode")).toBeVisible({ timeout: 5000 });
// Stop auto mode
await page.getByTestId("stop-auto-mode").click();
// Verify auto mode has stopped - start button should be visible again
await expect(page.getByTestId("start-auto-mode")).toBeVisible({ timeout: 5000 });
});
test("Activity log toggle button works", async ({ page }) => {
// Start auto mode
await page.getByTestId("start-auto-mode").click();
// Wait for auto mode to be running and activity button to appear
await expect(page.getByTestId("toggle-activity-log")).toBeVisible({ timeout: 5000 });
// The activity log should be visible initially when auto mode starts
// Toggle it off
await page.getByTestId("toggle-activity-log").click();
// Toggle it back on
await page.getByTestId("toggle-activity-log").click();
// The log panel should be visible
await expect(page.getByText("Auto Mode Activity")).toBeVisible();
});
test("Tool usage is logged during action phase", async ({ page }) => {
// Start auto mode
await page.getByTestId("start-auto-mode").click();
// Wait for auto mode to be running
await expect(page.getByTestId("stop-auto-mode")).toBeVisible({ timeout: 5000 });
// Wait for tool usage to appear in the activity log
await expect(page.getByText("Using tool: Read")).toBeVisible({ timeout: 5000 });
await expect(page.getByText("Using tool: Write")).toBeVisible({ timeout: 5000 });
});
});

View File

@@ -1,217 +0,0 @@
import { test, expect } from "@playwright/test";
test.describe("Agent Tools", () => {
test("can navigate to agent tools view when project is open", async ({ page }) => {
await page.goto("/");
// Create a project first
await page.getByTestId("new-project-card").click();
await page.getByTestId("project-name-input").fill("Test Project");
await page.getByTestId("project-path-input").fill("/test/path");
await page.getByTestId("confirm-create-project").click();
// Wait for board view to load
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to agent tools
await page.getByTestId("nav-tools").click();
// Verify agent tools view is displayed
await expect(page.getByTestId("agent-tools-view")).toBeVisible();
});
test("agent tools view shows all three tool cards", async ({ page }) => {
await page.goto("/");
// Create a project first
await page.getByTestId("new-project-card").click();
await page.getByTestId("project-name-input").fill("Test Project");
await page.getByTestId("project-path-input").fill("/test/path");
await page.getByTestId("confirm-create-project").click();
// Navigate to agent tools
await page.getByTestId("nav-tools").click();
// Verify all three tool cards are visible
await expect(page.getByTestId("read-file-tool")).toBeVisible();
await expect(page.getByTestId("write-file-tool")).toBeVisible();
await expect(page.getByTestId("terminal-tool")).toBeVisible();
});
test.describe("Read File Tool", () => {
test("agent can request to read file and receive content", async ({ page }) => {
await page.goto("/");
// Create a project first
await page.getByTestId("new-project-card").click();
await page.getByTestId("project-name-input").fill("Test Project");
await page.getByTestId("project-path-input").fill("/test/path");
await page.getByTestId("confirm-create-project").click();
// Navigate to agent tools
await page.getByTestId("nav-tools").click();
// Enter a file path
await page.getByTestId("read-file-path-input").fill("/test/path/feature_list.json");
// Click execute
await page.getByTestId("read-file-button").click();
// Wait for result
await expect(page.getByTestId("read-file-result")).toBeVisible();
// Verify success message
await expect(page.getByTestId("read-file-result")).toContainText("Success");
});
test("read file tool shows input field for file path", async ({ page }) => {
await page.goto("/");
// Create a project first
await page.getByTestId("new-project-card").click();
await page.getByTestId("project-name-input").fill("Test Project");
await page.getByTestId("project-path-input").fill("/test/path");
await page.getByTestId("confirm-create-project").click();
// Navigate to agent tools
await page.getByTestId("nav-tools").click();
// Verify input field exists
await expect(page.getByTestId("read-file-path-input")).toBeVisible();
await expect(page.getByTestId("read-file-button")).toBeVisible();
});
});
test.describe("Write File Tool", () => {
test("agent can request to write file and file is written", async ({ page }) => {
await page.goto("/");
// Create a project first
await page.getByTestId("new-project-card").click();
await page.getByTestId("project-name-input").fill("Test Project");
await page.getByTestId("project-path-input").fill("/test/path");
await page.getByTestId("confirm-create-project").click();
// Navigate to agent tools
await page.getByTestId("nav-tools").click();
// Enter file path and content
await page.getByTestId("write-file-path-input").fill("/test/path/new-file.txt");
await page.getByTestId("write-file-content-input").fill("Hello from agent!");
// Click execute
await page.getByTestId("write-file-button").click();
// Wait for result
await expect(page.getByTestId("write-file-result")).toBeVisible();
// Verify success message
await expect(page.getByTestId("write-file-result")).toContainText("Success");
await expect(page.getByTestId("write-file-result")).toContainText("File written successfully");
});
test("write file tool shows path and content inputs", async ({ page }) => {
await page.goto("/");
// Create a project first
await page.getByTestId("new-project-card").click();
await page.getByTestId("project-name-input").fill("Test Project");
await page.getByTestId("project-path-input").fill("/test/path");
await page.getByTestId("confirm-create-project").click();
// Navigate to agent tools
await page.getByTestId("nav-tools").click();
// Verify input fields exist
await expect(page.getByTestId("write-file-path-input")).toBeVisible();
await expect(page.getByTestId("write-file-content-input")).toBeVisible();
await expect(page.getByTestId("write-file-button")).toBeVisible();
});
});
test.describe("Terminal Tool", () => {
test("agent can request to run terminal command and receive stdout", async ({ page }) => {
await page.goto("/");
// Create a project first
await page.getByTestId("new-project-card").click();
await page.getByTestId("project-name-input").fill("Test Project");
await page.getByTestId("project-path-input").fill("/test/path");
await page.getByTestId("confirm-create-project").click();
// Navigate to agent tools
await page.getByTestId("nav-tools").click();
// Enter command (default is 'ls')
await page.getByTestId("terminal-command-input").fill("ls");
// Click execute
await page.getByTestId("run-terminal-button").click();
// Wait for result
await expect(page.getByTestId("terminal-result")).toBeVisible();
// Verify success and output
await expect(page.getByTestId("terminal-result")).toContainText("Success");
await expect(page.getByTestId("terminal-result")).toContainText("$ ls");
});
test("terminal tool shows command input field", async ({ page }) => {
await page.goto("/");
// Create a project first
await page.getByTestId("new-project-card").click();
await page.getByTestId("project-name-input").fill("Test Project");
await page.getByTestId("project-path-input").fill("/test/path");
await page.getByTestId("confirm-create-project").click();
// Navigate to agent tools
await page.getByTestId("nav-tools").click();
// Verify input field exists
await expect(page.getByTestId("terminal-command-input")).toBeVisible();
await expect(page.getByTestId("run-terminal-button")).toBeVisible();
});
test("terminal tool can run pwd command", async ({ page }) => {
await page.goto("/");
// Create a project first
await page.getByTestId("new-project-card").click();
await page.getByTestId("project-name-input").fill("Test Project");
await page.getByTestId("project-path-input").fill("/test/path");
await page.getByTestId("confirm-create-project").click();
// Navigate to agent tools
await page.getByTestId("nav-tools").click();
// Enter pwd command
await page.getByTestId("terminal-command-input").fill("pwd");
// Click execute
await page.getByTestId("run-terminal-button").click();
// Wait for result
await expect(page.getByTestId("terminal-result")).toBeVisible();
// Verify success
await expect(page.getByTestId("terminal-result")).toContainText("Success");
});
});
test("tool log section is visible", async ({ page }) => {
await page.goto("/");
// Create a project first
await page.getByTestId("new-project-card").click();
await page.getByTestId("project-name-input").fill("Test Project");
await page.getByTestId("project-path-input").fill("/test/path");
await page.getByTestId("confirm-create-project").click();
// Navigate to agent tools
await page.getByTestId("nav-tools").click();
// Verify tool log section is visible
await expect(page.getByTestId("tool-log")).toBeVisible();
});
});

View File

@@ -1,657 +0,0 @@
import { test, expect } from "@playwright/test";
test.describe("Project Analysis", () => {
test("can navigate to analysis view when project is open", async ({
page,
}) => {
await page.goto("/");
// Create a project first using dropdown
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page.getByTestId("project-name-input").fill("Analysis Test Project");
await page.getByTestId("project-path-input").fill("/test/analysis/project");
await page.getByTestId("confirm-create-project").click();
// Wait for board view to load
await expect(page.getByTestId("board-view")).toBeVisible();
// Click on Analysis in sidebar
await page.getByTestId("nav-analysis").click();
// Verify analysis view is displayed
await expect(page.getByTestId("analysis-view")).toBeVisible();
});
test("analysis view shows 'No Analysis Yet' message initially", async ({
page,
}) => {
await page.goto("/");
// Create a project first using dropdown
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page.getByTestId("project-name-input").fill("Analysis Test Project2");
await page
.getByTestId("project-path-input")
.fill("/test/analysis/project2");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view
await page.getByTestId("nav-analysis").click();
await expect(page.getByTestId("analysis-view")).toBeVisible();
// Verify no analysis message
await expect(page.getByText("No Analysis Yet")).toBeVisible();
await expect(page.getByText('Click "Analyze Project"')).toBeVisible();
});
test("shows 'Analyze Project' button", async ({ page }) => {
await page.goto("/");
// Create a project first using dropdown
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page.getByTestId("project-name-input").fill("Analysis Test Project3");
await page
.getByTestId("project-path-input")
.fill("/test/analysis/project3");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view
await page.getByTestId("nav-analysis").click();
await expect(page.getByTestId("analysis-view")).toBeVisible();
// Verify analyze button is visible
await expect(page.getByTestId("analyze-project-button")).toBeVisible();
});
test("can run project analysis", async ({ page }) => {
await page.goto("/");
// Create a project first using dropdown
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page.getByTestId("project-name-input").fill("Analysis Test Project4");
await page
.getByTestId("project-path-input")
.fill("/test/analysis/project4");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view
await page.getByTestId("nav-analysis").click();
await expect(page.getByTestId("analysis-view")).toBeVisible();
// Click analyze button
await page.getByTestId("analyze-project-button").click();
// Wait for analysis to complete and stats to appear
await expect(page.getByTestId("analysis-stats")).toBeVisible();
// Verify statistics are displayed
await expect(page.getByTestId("total-files")).toBeVisible();
await expect(page.getByTestId("total-directories")).toBeVisible();
});
test("analysis shows file tree after running", async ({ page }) => {
await page.goto("/");
// Create a project first using dropdown
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page.getByTestId("project-name-input").fill("Analysis Test Project5");
await page
.getByTestId("project-path-input")
.fill("/test/analysis/project5");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view
await page.getByTestId("nav-analysis").click();
await expect(page.getByTestId("analysis-view")).toBeVisible();
// Click analyze button
await page.getByTestId("analyze-project-button").click();
// Wait for analysis to complete
await expect(page.getByTestId("analysis-file-tree")).toBeVisible();
// Verify file tree is displayed
await expect(page.getByTestId("analysis-file-tree")).toBeVisible();
});
test("analysis shows files by extension breakdown", async ({ page }) => {
await page.goto("/");
// Create a project first using dropdown
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page.getByTestId("project-name-input").fill("Analysis Test Project6");
await page
.getByTestId("project-path-input")
.fill("/test/analysis/project6");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view
await page.getByTestId("nav-analysis").click();
await expect(page.getByTestId("analysis-view")).toBeVisible();
// Click analyze button
await page.getByTestId("analyze-project-button").click();
// Wait for analysis to complete
await expect(page.getByTestId("files-by-extension")).toBeVisible();
// Verify files by extension card is displayed
await expect(page.getByTestId("files-by-extension")).toBeVisible();
});
test("file tree displays correct structure with directories and files", async ({
page,
}) => {
await page.goto("/");
// Create a project first using dropdown
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page.getByTestId("project-name-input").fill("Analysis Test Project7");
await page
.getByTestId("project-path-input")
.fill("/test/analysis/project7");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view
await page.getByTestId("nav-analysis").click();
await expect(page.getByTestId("analysis-view")).toBeVisible();
// Click analyze button
await page.getByTestId("analyze-project-button").click();
// Wait for file tree to be populated
await expect(page.getByTestId("analysis-file-tree")).toBeVisible();
// Verify src directory is in the tree (mock data provides this)
await expect(page.getByTestId("analysis-node-src")).toBeVisible();
// Verify some files are in the tree
await expect(page.getByTestId("analysis-node-package.json")).toBeVisible();
});
});
test.describe("Generate Spec from Code", () => {
test("shows Generate Spec card after analysis is complete", async ({
page,
}) => {
await page.goto("/");
// Step 1: Open project with code but no spec
// Use dropdown to create project
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page
.getByTestId("project-name-input")
.fill("Generate Spec Test Project");
await page
.getByTestId("project-path-input")
.fill("/test/generate-spec/project");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view
await page.getByTestId("nav-analysis").click();
await expect(page.getByTestId("analysis-view")).toBeVisible();
// Run analysis first
await page.getByTestId("analyze-project-button").click();
await expect(page.getByTestId("analysis-stats")).toBeVisible();
// Verify Generate Spec card is visible
await expect(page.getByTestId("generate-spec-card")).toBeVisible();
});
test("shows Generate Spec button after analysis", async ({ page }) => {
await page.goto("/");
// Create a project
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page
.getByTestId("project-name-input")
.fill("Generate Spec Test Project2");
await page
.getByTestId("project-path-input")
.fill("/test/generate-spec/project2");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view
await page.getByTestId("nav-analysis").click();
await expect(page.getByTestId("analysis-view")).toBeVisible();
// Run analysis first
await page.getByTestId("analyze-project-button").click();
await expect(page.getByTestId("analysis-stats")).toBeVisible();
// Step 2: Trigger 'Generate Spec' - verify button exists
await expect(page.getByTestId("generate-spec-button")).toBeVisible();
await expect(page.getByTestId("generate-spec-button")).toHaveText(
/Generate Spec/
);
});
test("can trigger Generate Spec and shows success message", async ({
page,
}) => {
await page.goto("/");
// Step 1: Open project with code but no spec
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page
.getByTestId("project-name-input")
.fill("Generate Spec Test Project3");
await page
.getByTestId("project-path-input")
.fill("/test/generate-spec/project3");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view
await page.getByTestId("nav-analysis").click();
await expect(page.getByTestId("analysis-view")).toBeVisible();
// Run analysis first
await page.getByTestId("analyze-project-button").click();
await expect(page.getByTestId("analysis-stats")).toBeVisible();
// Step 2: Trigger 'Generate Spec'
await page.getByTestId("generate-spec-button").click();
// Step 3: Verify app_spec.txt is created (success message appears)
await expect(page.getByTestId("spec-generated-success")).toBeVisible();
await expect(
page.getByText("app_spec.txt created successfully")
).toBeVisible();
});
test("Generate Spec card displays description", async ({ page }) => {
await page.goto("/");
// Create a project
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page
.getByTestId("project-name-input")
.fill("Generate Spec Test Project4");
await page
.getByTestId("project-path-input")
.fill("/test/generate-spec/project4");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view and run analysis
await page.getByTestId("nav-analysis").click();
await page.getByTestId("analyze-project-button").click();
await expect(page.getByTestId("generate-spec-card")).toBeVisible();
// Step 4: Verify spec content accurately reflects codebase
// Check that the card shows relevant information about what the spec generation does
await expect(
page.getByText("Create app_spec.txt from analysis")
).toBeVisible();
await expect(
page.getByText(/Generate a project specification/)
).toBeVisible();
});
test("Generate Spec button is disabled while generating", async ({
page,
}) => {
await page.goto("/");
// Create a project
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page
.getByTestId("project-name-input")
.fill("Generate Spec Test Project5");
await page
.getByTestId("project-path-input")
.fill("/test/generate-spec/project5");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view and run analysis
await page.getByTestId("nav-analysis").click();
await page.getByTestId("analyze-project-button").click();
await expect(page.getByTestId("generate-spec-card")).toBeVisible();
// Check the button exists and can be clicked
const generateButton = page.getByTestId("generate-spec-button");
await expect(generateButton).toBeVisible();
await expect(generateButton).toBeEnabled();
});
test("generated spec file reflects analyzed codebase structure", async ({
page,
}) => {
await page.goto("/");
// Step 1: Open project with code but no spec
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page.getByTestId("project-name-input").fill("Spec Verify Project");
await page
.getByTestId("project-path-input")
.fill("/test/spec-verify/project");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view
await page.getByTestId("nav-analysis").click();
await expect(page.getByTestId("analysis-view")).toBeVisible();
// Run analysis first
await page.getByTestId("analyze-project-button").click();
await expect(page.getByTestId("analysis-stats")).toBeVisible();
// Verify statistics are correctly computed (mock data provides this)
const totalFiles = page.getByTestId("total-files");
await expect(totalFiles).toBeVisible();
const totalDirectories = page.getByTestId("total-directories");
await expect(totalDirectories).toBeVisible();
// Step 2: Trigger 'Generate Spec'
await page.getByTestId("generate-spec-button").click();
// Step 3: Verify app_spec.txt is created (success message appears)
await expect(page.getByTestId("spec-generated-success")).toBeVisible();
// Step 4: Verify spec content accurately reflects codebase
// Navigate to spec view to verify the generated content
await page.getByTestId("nav-spec").click();
await expect(page.getByTestId("spec-view")).toBeVisible();
// Verify the spec editor has content that reflects the analyzed codebase
const specEditor = page.getByTestId("spec-editor");
await expect(specEditor).toBeVisible();
// Verify key elements of the generated spec are present
// The spec should contain project_specification XML tags
const specContent = await specEditor.inputValue();
expect(specContent).toContain("<project_specification>");
expect(specContent).toContain("<project_name>");
expect(specContent).toContain("<technology_stack>");
expect(specContent).toContain("<project_structure>");
expect(specContent).toContain("<file_breakdown>");
expect(specContent).toContain("</project_specification>");
});
});
test.describe("Generate Feature List from Code", () => {
test("shows Generate Feature List card after analysis is complete", async ({
page,
}) => {
await page.goto("/");
// Step 1: Open project with implemented features
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page
.getByTestId("project-name-input")
.fill("Feature List Test Project");
await page
.getByTestId("project-path-input")
.fill("/test/feature-list/project");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view
await page.getByTestId("nav-analysis").click();
await expect(page.getByTestId("analysis-view")).toBeVisible();
// Run analysis first
await page.getByTestId("analyze-project-button").click();
await expect(page.getByTestId("analysis-stats")).toBeVisible();
// Verify Generate Feature List card is visible
await expect(page.getByTestId("generate-feature-list-card")).toBeVisible();
});
test("shows Generate Feature List button after analysis", async ({
page,
}) => {
await page.goto("/");
// Create a project
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page
.getByTestId("project-name-input")
.fill("Feature List Test Project2");
await page
.getByTestId("project-path-input")
.fill("/test/feature-list/project2");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view
await page.getByTestId("nav-analysis").click();
await expect(page.getByTestId("analysis-view")).toBeVisible();
// Run analysis first
await page.getByTestId("analyze-project-button").click();
await expect(page.getByTestId("analysis-stats")).toBeVisible();
// Step 2: Trigger 'Generate Feature List' - verify button exists
await expect(
page.getByTestId("generate-feature-list-button")
).toBeVisible();
await expect(page.getByTestId("generate-feature-list-button")).toHaveText(
/Generate Feature List/
);
});
test("can trigger Generate Feature List and shows success message", async ({
page,
}) => {
await page.goto("/");
// Step 1: Open project with implemented features
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page
.getByTestId("project-name-input")
.fill("Feature List Test Project3");
await page
.getByTestId("project-path-input")
.fill("/test/feature-list/project3");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view
await page.getByTestId("nav-analysis").click();
await expect(page.getByTestId("analysis-view")).toBeVisible();
// Run analysis first
await page.getByTestId("analyze-project-button").click();
await expect(page.getByTestId("analysis-stats")).toBeVisible();
// Step 2: Trigger 'Generate Feature List'
await page.getByTestId("generate-feature-list-button").click();
// Step 3: Verify .automaker/feature_list.json is created (success message appears)
await expect(
page.getByTestId("feature-list-generated-success")
).toBeVisible();
await expect(
page.getByText("feature_list.json created successfully")
).toBeVisible();
});
test("Generate Feature List card displays description", async ({ page }) => {
await page.goto("/");
// Create a project
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page
.getByTestId("project-name-input")
.fill("Feature List Test Project4");
await page
.getByTestId("project-path-input")
.fill("/test/feature-list/project4");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view and run analysis
await page.getByTestId("nav-analysis").click();
await page.getByTestId("analyze-project-button").click();
await expect(page.getByTestId("generate-feature-list-card")).toBeVisible();
// Check that the card shows relevant information about what the feature list generation does
await expect(
page.getByText("Create .automaker/feature_list.json from analysis")
).toBeVisible();
await expect(
page.getByText(/Automatically detect and generate a feature list/)
).toBeVisible();
});
test("Generate Feature List button is enabled after analysis", async ({
page,
}) => {
await page.goto("/");
// Create a project
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page
.getByTestId("project-name-input")
.fill("Feature List Test Project5");
await page
.getByTestId("project-path-input")
.fill("/test/feature-list/project5");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view and run analysis
await page.getByTestId("nav-analysis").click();
await page.getByTestId("analyze-project-button").click();
await expect(page.getByTestId("generate-feature-list-card")).toBeVisible();
// Check the button exists and is enabled
const generateButton = page.getByTestId("generate-feature-list-button");
await expect(generateButton).toBeVisible();
await expect(generateButton).toBeEnabled();
});
test("generated feature list contains features with passes: true", async ({
page,
}) => {
await page.goto("/");
// Step 1: Open project with implemented features
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page.getByTestId("project-name-input").fill("Feature Verify Project");
await page
.getByTestId("project-path-input")
.fill("/test/feature-verify/project");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view
await page.getByTestId("nav-analysis").click();
await expect(page.getByTestId("analysis-view")).toBeVisible();
// Run analysis first
await page.getByTestId("analyze-project-button").click();
await expect(page.getByTestId("analysis-stats")).toBeVisible();
// Verify statistics are correctly computed (mock data provides this)
const totalFiles = page.getByTestId("total-files");
await expect(totalFiles).toBeVisible();
const totalDirectories = page.getByTestId("total-directories");
await expect(totalDirectories).toBeVisible();
// Step 2: Trigger 'Generate Feature List'
await page.getByTestId("generate-feature-list-button").click();
// Step 3: Verify .automaker/feature_list.json is created (success message appears)
await expect(
page.getByTestId("feature-list-generated-success")
).toBeVisible();
// Step 4: Verify existing features are marked 'passes': true
// Navigate to board view to verify the features are loaded
await page.getByTestId("nav-board").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// The generated feature list should have been written and can be loaded
// The mock system writes to a mock file system, so we verify through UI that
// the generation completed successfully (the success message is sufficient proof)
});
test("Generate Feature List can be triggered multiple times", async ({
page,
}) => {
await page.goto("/");
// Create a project
await page.getByTestId("create-new-project").click();
await page.getByTestId("quick-setup-option").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await page
.getByTestId("project-name-input")
.fill("Feature List Multi Test");
await page
.getByTestId("project-path-input")
.fill("/test/feature-list/multi");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to analysis view and run analysis
await page.getByTestId("nav-analysis").click();
await page.getByTestId("analyze-project-button").click();
await expect(page.getByTestId("generate-feature-list-card")).toBeVisible();
// Generate feature list first time
await page.getByTestId("generate-feature-list-button").click();
await expect(
page.getByTestId("feature-list-generated-success")
).toBeVisible();
// Generate feature list second time (should overwrite)
await page.getByTestId("generate-feature-list-button").click();
await expect(
page.getByTestId("feature-list-generated-success")
).toBeVisible();
});
});

View File

@@ -1,294 +0,0 @@
import { test, expect } from "@playwright/test";
test.describe("Claude SDK Integration - Autonomous Agent", () => {
test.describe("Step 1: Configure API Key", () => {
test("can navigate to settings and configure Anthropic API key", async ({ page }) => {
await page.goto("/");
// Navigate to settings
await page.getByTestId("settings-button").click();
await expect(page.getByTestId("settings-view")).toBeVisible();
// Verify Anthropic API key input is available
const apiKeyInput = page.getByTestId("anthropic-api-key-input");
await expect(apiKeyInput).toBeVisible();
await expect(apiKeyInput).toBeEditable();
// Enter a test API key
await apiKeyInput.fill("sk-ant-api03-test-key-for-integration-test");
// Save settings
await page.getByTestId("save-settings").click();
await expect(page.getByText("Saved!")).toBeVisible();
});
test("API key input has proper security features", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Verify password masking by default
await expect(page.getByTestId("anthropic-api-key-input")).toHaveAttribute("type", "password");
// Can toggle visibility
await page.getByTestId("toggle-anthropic-visibility").click();
await expect(page.getByTestId("anthropic-api-key-input")).toHaveAttribute("type", "text");
// Can toggle back to hidden
await page.getByTestId("toggle-anthropic-visibility").click();
await expect(page.getByTestId("anthropic-api-key-input")).toHaveAttribute("type", "password");
});
test("API key persists across page reloads", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter and save API key
const testKey = "sk-ant-api03-persistence-test-key";
await page.getByTestId("anthropic-api-key-input").fill(testKey);
await page.getByTestId("save-settings").click();
await expect(page.getByText("Saved!")).toBeVisible();
// Reload and verify persistence
await page.reload();
await page.getByTestId("settings-button").click();
// Make key visible and verify
await page.getByTestId("toggle-anthropic-visibility").click();
await expect(page.getByTestId("anthropic-api-key-input")).toHaveValue(testKey);
});
});
test.describe("Step 2: Send test prompt", () => {
test("test connection button is visible in settings", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Test connection button should be visible
const testButton = page.getByTestId("test-claude-connection");
await expect(testButton).toBeVisible();
await expect(testButton).toContainText("Test");
});
test("test connection button is disabled without API key", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Clear any existing API key
await page.getByTestId("anthropic-api-key-input").fill("");
// Test button should be disabled
await expect(page.getByTestId("test-claude-connection")).toBeDisabled();
});
test("test connection button is enabled with API key", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter API key
await page.getByTestId("anthropic-api-key-input").fill("sk-ant-test-key");
// Test button should be enabled
await expect(page.getByTestId("test-claude-connection")).toBeEnabled();
});
test("clicking test sends request to Claude API endpoint", async ({ page }) => {
// Setup API route mock
await page.route("**/api/claude/test", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
success: true,
message: "Connection successful! Claude responded.",
model: "claude-sonnet-4-20250514",
}),
});
});
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter API key
await page.getByTestId("anthropic-api-key-input").fill("sk-ant-test-key");
// Click test button
await page.getByTestId("test-claude-connection").click();
// Should show loading state briefly then success
await expect(page.getByTestId("test-connection-result")).toBeVisible({ timeout: 10000 });
});
});
test.describe("Step 3: Verify response received", () => {
test("displays success message when connection succeeds", async ({ page }) => {
// Mock successful response
await page.route("**/api/claude/test", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
success: true,
message: 'Connection successful! Response: "Claude SDK connection successful!"',
model: "claude-sonnet-4-20250514",
}),
});
});
await page.goto("/");
await page.getByTestId("settings-button").click();
await page.getByTestId("anthropic-api-key-input").fill("sk-ant-valid-key");
await page.getByTestId("test-claude-connection").click();
// Wait for result to appear
const result = page.getByTestId("test-connection-result");
await expect(result).toBeVisible({ timeout: 10000 });
// Verify success message is shown
const message = page.getByTestId("test-connection-message");
await expect(message).toContainText(/Connection successful/i);
});
test("displays error message when API key is invalid", async ({ page }) => {
// Mock authentication error
await page.route("**/api/claude/test", async (route) => {
await route.fulfill({
status: 401,
contentType: "application/json",
body: JSON.stringify({
success: false,
error: "Invalid API key. Please check your Anthropic API key.",
}),
});
});
await page.goto("/");
await page.getByTestId("settings-button").click();
await page.getByTestId("anthropic-api-key-input").fill("invalid-key");
await page.getByTestId("test-claude-connection").click();
// Wait for error result
const result = page.getByTestId("test-connection-result");
await expect(result).toBeVisible({ timeout: 10000 });
// Verify error message is shown
const message = page.getByTestId("test-connection-message");
await expect(message).toContainText(/Invalid API key|API key|error/i);
});
test("displays error message on network failure", async ({ page }) => {
// Mock network error
await page.route("**/api/claude/test", async (route) => {
await route.abort("connectionrefused");
});
await page.goto("/");
await page.getByTestId("settings-button").click();
await page.getByTestId("anthropic-api-key-input").fill("sk-ant-test-key");
await page.getByTestId("test-claude-connection").click();
// Wait for error result
const result = page.getByTestId("test-connection-result");
await expect(result).toBeVisible({ timeout: 10000 });
// Verify network error message
const message = page.getByTestId("test-connection-message");
await expect(message).toContainText(/Network error|connection|failed/i);
});
test("displays rate limit error message", async ({ page }) => {
// Mock rate limit error
await page.route("**/api/claude/test", async (route) => {
await route.fulfill({
status: 429,
contentType: "application/json",
body: JSON.stringify({
success: false,
error: "Rate limit exceeded. Please try again later.",
}),
});
});
await page.goto("/");
await page.getByTestId("settings-button").click();
await page.getByTestId("anthropic-api-key-input").fill("sk-ant-rate-limited");
await page.getByTestId("test-claude-connection").click();
// Wait for error result
const result = page.getByTestId("test-connection-result");
await expect(result).toBeVisible({ timeout: 10000 });
// Verify rate limit message
const message = page.getByTestId("test-connection-message");
await expect(message).toContainText(/Rate limit|try again/i);
});
test("shows loading state while testing connection", async ({ page }) => {
// Mock slow response
await page.route("**/api/claude/test", async (route) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
success: true,
message: "Connection successful!",
}),
});
});
await page.goto("/");
await page.getByTestId("settings-button").click();
await page.getByTestId("anthropic-api-key-input").fill("sk-ant-test-key");
await page.getByTestId("test-claude-connection").click();
// Should show "Testing..." text while loading
await expect(page.getByText("Testing...")).toBeVisible();
});
});
test.describe("Full Integration Flow", () => {
test("complete Claude SDK integration flow - configure, test, verify", async ({ page }) => {
// Mock successful API response
await page.route("**/api/claude/test", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
success: true,
message: 'Connection successful! Response: "Claude SDK connection successful!"',
model: "claude-sonnet-4-20250514",
}),
});
});
// Step 1: Navigate to app
await page.goto("/");
await expect(page).toHaveURL("/");
// Step 2: Go to settings and configure API key
await page.getByTestId("settings-button").click();
await expect(page.getByTestId("settings-view")).toBeVisible();
const apiKey = "sk-ant-api03-integration-test-key";
await page.getByTestId("anthropic-api-key-input").fill(apiKey);
await page.getByTestId("save-settings").click();
await expect(page.getByText("Saved!")).toBeVisible();
// Step 3: Test the connection
await page.getByTestId("test-claude-connection").click();
// Step 4: Verify response is received
await expect(page.getByTestId("test-connection-result")).toBeVisible({ timeout: 10000 });
await expect(page.getByTestId("test-connection-message")).toContainText(/Connection successful/i);
// Verify the UI shows success state (green styling indicates success)
const resultContainer = page.getByTestId("test-connection-result");
await expect(resultContainer).toBeVisible();
});
});
});

View File

@@ -1,76 +0,0 @@
import { test, expect } from "@playwright/test";
test.describe("Application Foundation", () => {
test("loads the application with sidebar and welcome view", async ({ page }) => {
await page.goto("/");
// Verify main container exists
await expect(page.getByTestId("app-container")).toBeVisible();
// Verify sidebar is visible
await expect(page.getByTestId("sidebar")).toBeVisible();
// Verify welcome view is shown by default
await expect(page.getByTestId("welcome-view")).toBeVisible();
});
test("displays Automaker title in sidebar", async ({ page }) => {
await page.goto("/");
// Verify the title is visible in the sidebar (be specific to avoid matching welcome heading)
await expect(page.getByTestId("sidebar").getByRole("heading", { name: "Automaker" })).toBeVisible();
});
test("shows New Project and Open Project buttons", async ({ page }) => {
await page.goto("/");
// Verify project action buttons in welcome view
await expect(page.getByTestId("new-project-card")).toBeVisible();
await expect(page.getByTestId("open-project-card")).toBeVisible();
});
test("sidebar can be collapsed and expanded", async ({ page }) => {
await page.goto("/");
const sidebar = page.getByTestId("sidebar");
const toggleButton = page.getByTestId("toggle-sidebar");
// Initially sidebar should be expanded (width 256px / w-64)
await expect(sidebar).toHaveClass(/w-64/);
// Click to collapse
await toggleButton.click();
await expect(sidebar).toHaveClass(/w-16/);
// Click to expand again
await toggleButton.click();
await expect(sidebar).toHaveClass(/w-64/);
});
test("shows Web Mode indicator when running in browser", async ({ page }) => {
await page.goto("/");
// When running in browser (not Electron), should show mock indicator
await expect(page.getByText("Web Mode (Mock IPC)")).toBeVisible();
});
});
test.describe("Theme Toggle", () => {
test("toggles between dark and light mode", async ({ page }) => {
await page.goto("/");
const themeButton = page.getByTestId("toggle-theme");
const html = page.locator("html");
// Initially should be in dark mode
await expect(html).toHaveClass(/dark/);
// Click to switch to light mode
await themeButton.click();
await expect(html).not.toHaveClass(/dark/);
// Click to switch back to dark mode
await themeButton.click();
await expect(html).toHaveClass(/dark/);
});
});

View File

@@ -1,299 +0,0 @@
import { test, expect } from "@playwright/test";
test.describe("Gemini SDK Integration", () => {
test.describe("Step 1: Configure Gemini API Key", () => {
test("can navigate to settings and see Gemini API key input", async ({
page,
}) => {
await page.goto("/");
// Navigate to settings
await page.getByTestId("settings-button").click();
// Verify settings view is displayed
await expect(page.getByTestId("settings-view")).toBeVisible();
// Verify Google/Gemini API key input exists
await expect(page.getByTestId("google-api-key-input")).toBeVisible();
await expect(
page.getByText("Google API Key (Gemini)", { exact: true })
).toBeVisible();
});
test("can enter and save Gemini API key", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter a test API key
const testApiKey = "AIzaSyTestKey123456";
await page.getByTestId("google-api-key-input").fill(testApiKey);
// Save the settings
await page.getByTestId("save-settings").click();
// Verify saved confirmation
await expect(page.getByText("Saved!")).toBeVisible();
// Reload and verify persistence
await page.reload();
await page.getByTestId("settings-button").click();
// Toggle visibility to check the value
await page.getByTestId("toggle-google-visibility").click();
await expect(page.getByTestId("google-api-key-input")).toHaveValue(
testApiKey
);
});
test("Gemini API key input is password type by default for security", async ({
page,
}) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Verify password type for security
await expect(page.getByTestId("google-api-key-input")).toHaveAttribute(
"type",
"password"
);
});
test("can toggle Gemini API key visibility", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Initially password type
await expect(page.getByTestId("google-api-key-input")).toHaveAttribute(
"type",
"password"
);
// Toggle to show
await page.getByTestId("toggle-google-visibility").click();
await expect(page.getByTestId("google-api-key-input")).toHaveAttribute(
"type",
"text"
);
// Toggle back to hide
await page.getByTestId("toggle-google-visibility").click();
await expect(page.getByTestId("google-api-key-input")).toHaveAttribute(
"type",
"password"
);
});
test("shows checkmark icon when API key is configured", async ({
page,
}) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter API key
await page.getByTestId("google-api-key-input").fill("AIzaSyTest123");
await page.getByTestId("save-settings").click();
// Reload to trigger the checkmark display
await page.reload();
await page.getByTestId("settings-button").click();
// The checkmark icon should be visible next to the label
// Find the label container and verify checkmark is present
const labelContainer = page.locator(".flex.items-center.gap-2").filter({
hasText: "Google API Key (Gemini)",
});
await expect(
labelContainer.locator('svg[class*="text-green-500"]')
).toBeVisible();
});
});
test.describe("Step 2: Send image/design prompt", () => {
test("test connection button exists for Gemini", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Verify test connection button exists
await expect(page.getByTestId("test-gemini-connection")).toBeVisible();
});
test("test connection button is disabled without API key", async ({
page,
}) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Clear any existing API key
await page.getByTestId("google-api-key-input").clear();
// Verify button is disabled
await expect(page.getByTestId("test-gemini-connection")).toBeDisabled();
});
test("test connection button is enabled with API key", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter API key
await page.getByTestId("google-api-key-input").fill("AIzaSyTestKey123");
// Verify button is enabled
await expect(page.getByTestId("test-gemini-connection")).toBeEnabled();
});
test("clicking test connection shows loading state", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter API key
await page.getByTestId("google-api-key-input").fill("AIzaSyInvalidKey");
// Click test connection
await page.getByTestId("test-gemini-connection").click();
// Should show loading state (Testing...)
await expect(page.getByText("Testing...")).toBeVisible();
});
});
test.describe("Step 3: Verify response received", () => {
test("shows error message for invalid API key", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter an invalid API key
await page.getByTestId("google-api-key-input").fill("invalid-key-123");
// Click test connection
await page.getByTestId("test-gemini-connection").click();
// Wait for result (should show error)
await expect(
page.getByTestId("gemini-test-connection-result")
).toBeVisible({ timeout: 15000 });
// The result should indicate an error (red styling or error message)
const resultElement = page.getByTestId("gemini-test-connection-result");
await expect(resultElement).toBeVisible();
});
test("Gemini API endpoint exists and responds", async ({ request }) => {
// Test the API endpoint directly
const response = await request.post("/api/gemini/test", {
data: {
apiKey: "test-invalid-key",
},
});
// Should return a response (even if error)
expect(response.status()).toBeLessThanOrEqual(500);
const data = await response.json();
// Should have success or error property
expect(data).toHaveProperty("success");
expect(typeof data.success).toBe("boolean");
});
test("Gemini API endpoint handles missing API key", async ({ request }) => {
// Test the API endpoint without API key
const response = await request.post("/api/gemini/test", {
data: {},
});
// Should return 400 for missing API key
expect(response.status()).toBe(400);
const data = await response.json();
expect(data.success).toBe(false);
expect(data.error).toContain("No API key");
});
test("Gemini API endpoint handles image data structure", async ({
request,
}) => {
// Test that the API can accept image data format
const response = await request.post("/api/gemini/test", {
data: {
apiKey: "test-key",
imageData: "iVBORw0KGgoAAAANSUhEUg==", // Minimal base64
mimeType: "image/png",
prompt: "Describe this image",
},
});
// Should process the request (even if API key is invalid)
expect(response.status()).toBeLessThanOrEqual(500);
const data = await response.json();
expect(data).toHaveProperty("success");
});
test("result message displays in UI after test", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter API key
await page.getByTestId("google-api-key-input").fill("test-api-key-123");
// Click test connection
await page.getByTestId("test-gemini-connection").click();
// Wait for result message to appear
await expect(
page.getByTestId("gemini-test-connection-message")
).toBeVisible({ timeout: 15000 });
});
test("shows link to Google AI Studio for API key", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Should show link to get API key
const link = page.locator('a[href*="makersuite.google.com"]');
await expect(link).toBeVisible();
await expect(link).toHaveAttribute("target", "_blank");
});
});
test.describe("Gemini API Route Tests", () => {
test("API route supports text-only prompts", async ({ request }) => {
const response = await request.post("/api/gemini/test", {
data: {
apiKey: "test-key",
prompt: "Hello, this is a test prompt",
},
});
const data = await response.json();
// Should process without crashing (actual API key validation happens remotely)
expect(data).toHaveProperty("success");
});
test("API route supports custom prompts with images", async ({
request,
}) => {
const response = await request.post("/api/gemini/test", {
data: {
apiKey: "test-key",
imageData: "base64encodeddata",
mimeType: "image/jpeg",
prompt: "What design patterns do you see in this UI mockup?",
},
});
const data = await response.json();
expect(data).toHaveProperty("success");
});
test("API route returns proper error structure", async ({ request }) => {
const response = await request.post("/api/gemini/test", {
data: {},
});
const data = await response.json();
expect(data).toHaveProperty("success");
expect(data).toHaveProperty("error");
expect(typeof data.error).toBe("string");
});
});
});

View File

@@ -1,430 +0,0 @@
import { test, expect } from "@playwright/test";
test.describe("Gemini SDK Integration - Autonomous Agent", () => {
test.describe("Step 1: Configure Gemini API Key", () => {
test("can navigate to settings and configure Google API key", async ({ page }) => {
await page.goto("/");
// Navigate to settings
await page.getByTestId("settings-button").click();
await expect(page.getByTestId("settings-view")).toBeVisible();
// Verify Google API key input is available
const apiKeyInput = page.getByTestId("google-api-key-input");
await expect(apiKeyInput).toBeVisible();
await expect(apiKeyInput).toBeEditable();
// Enter a test API key
await apiKeyInput.fill("AIzaSyTest-integration-test-key-123");
// Save settings
await page.getByTestId("save-settings").click();
await expect(page.getByText("Saved!")).toBeVisible();
});
test("Google API key input has proper security features", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Verify password masking by default
await expect(page.getByTestId("google-api-key-input")).toHaveAttribute("type", "password");
// Can toggle visibility
await page.getByTestId("toggle-google-visibility").click();
await expect(page.getByTestId("google-api-key-input")).toHaveAttribute("type", "text");
// Can toggle back to hidden
await page.getByTestId("toggle-google-visibility").click();
await expect(page.getByTestId("google-api-key-input")).toHaveAttribute("type", "password");
});
test("Google API key persists across page reloads", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter and save API key
const testKey = "AIzaSyPersistence-test-key";
await page.getByTestId("google-api-key-input").fill(testKey);
await page.getByTestId("save-settings").click();
await expect(page.getByText("Saved!")).toBeVisible();
// Reload and verify persistence
await page.reload();
await page.getByTestId("settings-button").click();
// Make key visible and verify
await page.getByTestId("toggle-google-visibility").click();
await expect(page.getByTestId("google-api-key-input")).toHaveValue(testKey);
});
});
test.describe("Step 2: Send image/design prompt", () => {
test("test connection button is visible in settings for Gemini", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Test connection button should be visible
const testButton = page.getByTestId("test-gemini-connection");
await expect(testButton).toBeVisible();
await expect(testButton).toContainText("Test");
});
test("test connection button is disabled without API key", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Clear any existing API key
await page.getByTestId("google-api-key-input").fill("");
// Test button should be disabled
await expect(page.getByTestId("test-gemini-connection")).toBeDisabled();
});
test("test connection button is enabled with API key", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter API key
await page.getByTestId("google-api-key-input").fill("AIzaSyTest-key");
// Test button should be enabled
await expect(page.getByTestId("test-gemini-connection")).toBeEnabled();
});
test("clicking test sends request to Gemini API endpoint", async ({ page }) => {
// Setup API route mock
await page.route("**/api/gemini/test", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
success: true,
message: "Connection successful! Gemini responded.",
model: "gemini-1.5-flash",
hasImage: false,
}),
});
});
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter API key
await page.getByTestId("google-api-key-input").fill("AIzaSyTest-key");
// Click test button
await page.getByTestId("test-gemini-connection").click();
// Should show loading state briefly then success
await expect(page.getByTestId("gemini-test-connection-result")).toBeVisible({ timeout: 10000 });
});
test("Gemini API endpoint supports image/design prompts", async ({ page }) => {
// Mock API endpoint that handles image data
await page.route("**/api/gemini/test", async (route) => {
const request = route.request();
const postData = request.postDataJSON();
// Verify the API can receive image data
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
success: true,
message: 'Connection successful! Response: "This is a test design description."',
model: "gemini-1.5-flash",
hasImage: !!postData?.imageData,
}),
});
});
await page.goto("/");
await page.getByTestId("settings-button").click();
await page.getByTestId("google-api-key-input").fill("AIzaSyTest-image-key");
await page.getByTestId("test-gemini-connection").click();
await expect(page.getByTestId("gemini-test-connection-result")).toBeVisible({ timeout: 10000 });
});
});
test.describe("Step 3: Verify response received", () => {
test("displays success message when connection succeeds", async ({ page }) => {
// Mock successful response
await page.route("**/api/gemini/test", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
success: true,
message: 'Connection successful! Response: "Gemini SDK connection successful!"',
model: "gemini-1.5-flash",
hasImage: false,
}),
});
});
await page.goto("/");
await page.getByTestId("settings-button").click();
await page.getByTestId("google-api-key-input").fill("AIzaSyValid-key");
await page.getByTestId("test-gemini-connection").click();
// Wait for result to appear
const result = page.getByTestId("gemini-test-connection-result");
await expect(result).toBeVisible({ timeout: 10000 });
// Verify success message is shown
const message = page.getByTestId("gemini-test-connection-message");
await expect(message).toContainText(/Connection successful/i);
});
test("displays error message when API key is invalid", async ({ page }) => {
// Mock authentication error
await page.route("**/api/gemini/test", async (route) => {
await route.fulfill({
status: 401,
contentType: "application/json",
body: JSON.stringify({
success: false,
error: "Invalid API key. Please check your Google API key.",
}),
});
});
await page.goto("/");
await page.getByTestId("settings-button").click();
await page.getByTestId("google-api-key-input").fill("invalid-key");
await page.getByTestId("test-gemini-connection").click();
// Wait for error result
const result = page.getByTestId("gemini-test-connection-result");
await expect(result).toBeVisible({ timeout: 10000 });
// Verify error message is shown
const message = page.getByTestId("gemini-test-connection-message");
await expect(message).toContainText(/Invalid API key|API key|error/i);
});
test("displays error message on network failure", async ({ page }) => {
// Mock network error
await page.route("**/api/gemini/test", async (route) => {
await route.abort("connectionrefused");
});
await page.goto("/");
await page.getByTestId("settings-button").click();
await page.getByTestId("google-api-key-input").fill("AIzaSyTest-key");
await page.getByTestId("test-gemini-connection").click();
// Wait for error result
const result = page.getByTestId("gemini-test-connection-result");
await expect(result).toBeVisible({ timeout: 10000 });
// Verify network error message
const message = page.getByTestId("gemini-test-connection-message");
await expect(message).toContainText(/Network error|connection|failed/i);
});
test("displays rate limit error message", async ({ page }) => {
// Mock rate limit error
await page.route("**/api/gemini/test", async (route) => {
await route.fulfill({
status: 429,
contentType: "application/json",
body: JSON.stringify({
success: false,
error: "Rate limit exceeded. Please try again later.",
}),
});
});
await page.goto("/");
await page.getByTestId("settings-button").click();
await page.getByTestId("google-api-key-input").fill("AIzaSyRate-limited");
await page.getByTestId("test-gemini-connection").click();
// Wait for error result
const result = page.getByTestId("gemini-test-connection-result");
await expect(result).toBeVisible({ timeout: 10000 });
// Verify rate limit message
const message = page.getByTestId("gemini-test-connection-message");
await expect(message).toContainText(/Rate limit|try again/i);
});
test("shows loading state while testing connection", async ({ page }) => {
// Mock slow response
await page.route("**/api/gemini/test", async (route) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
success: true,
message: "Connection successful!",
}),
});
});
await page.goto("/");
await page.getByTestId("settings-button").click();
await page.getByTestId("google-api-key-input").fill("AIzaSyTest-key");
await page.getByTestId("test-gemini-connection").click();
// Should show "Testing..." text while loading
await expect(page.getByText("Testing...")).toBeVisible();
});
test("displays response with image analysis capability", async ({ page }) => {
// Mock response that indicates image was processed
await page.route("**/api/gemini/test", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
success: true,
message: 'Connection successful! Response: "I can see a modern UI design with buttons and forms."',
model: "gemini-1.5-flash",
hasImage: true,
}),
});
});
await page.goto("/");
await page.getByTestId("settings-button").click();
await page.getByTestId("google-api-key-input").fill("AIzaSyImage-test-key");
await page.getByTestId("test-gemini-connection").click();
// Wait for result to appear
const result = page.getByTestId("gemini-test-connection-result");
await expect(result).toBeVisible({ timeout: 10000 });
// Verify success message is shown
const message = page.getByTestId("gemini-test-connection-message");
await expect(message).toContainText(/Connection successful/i);
});
});
test.describe("Full Integration Flow", () => {
test("complete Gemini SDK integration flow - configure, send image/design prompt, verify", async ({ page }) => {
// Mock successful API response
await page.route("**/api/gemini/test", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
success: true,
message: 'Connection successful! Response: "Gemini SDK connection successful!"',
model: "gemini-1.5-flash",
hasImage: false,
}),
});
});
// Step 1: Navigate to app
await page.goto("/");
await expect(page).toHaveURL("/");
// Step 2: Go to settings and configure API key
await page.getByTestId("settings-button").click();
await expect(page.getByTestId("settings-view")).toBeVisible();
const apiKey = "AIzaSyIntegration-test-key";
await page.getByTestId("google-api-key-input").fill(apiKey);
await page.getByTestId("save-settings").click();
await expect(page.getByText("Saved!")).toBeVisible();
// Step 3: Test the connection (sends prompt to Gemini)
await page.getByTestId("test-gemini-connection").click();
// Step 4: Verify response is received
await expect(page.getByTestId("gemini-test-connection-result")).toBeVisible({ timeout: 10000 });
await expect(page.getByTestId("gemini-test-connection-message")).toContainText(/Connection successful/i);
// Verify the UI shows success state (green styling indicates success)
const resultContainer = page.getByTestId("gemini-test-connection-result");
await expect(resultContainer).toBeVisible();
});
test("Gemini API supports both text and image/design prompts", async ({ page }) => {
// First test: text only prompt
await page.route("**/api/gemini/test", async (route) => {
const request = route.request();
const postData = request.postDataJSON();
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
success: true,
message: postData?.imageData
? 'Connection successful! Response: "Design analyzed successfully."'
: 'Connection successful! Response: "Gemini SDK connection successful!"',
model: "gemini-1.5-flash",
hasImage: !!postData?.imageData,
}),
});
});
await page.goto("/");
await page.getByTestId("settings-button").click();
await page.getByTestId("google-api-key-input").fill("AIzaSyMultimodal-key");
await page.getByTestId("test-gemini-connection").click();
await expect(page.getByTestId("gemini-test-connection-result")).toBeVisible({ timeout: 10000 });
await expect(page.getByTestId("gemini-test-connection-message")).toContainText(/Connection successful/i);
});
});
test.describe("Gemini API Endpoint Verification", () => {
test("API endpoint exists and responds correctly", async ({ page }) => {
// This test verifies the API route is properly set up
await page.route("**/api/gemini/test", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
success: true,
message: "Gemini API endpoint is working",
model: "gemini-1.5-flash",
}),
});
});
await page.goto("/");
await page.getByTestId("settings-button").click();
await page.getByTestId("google-api-key-input").fill("test-key");
await page.getByTestId("test-gemini-connection").click();
await expect(page.getByTestId("gemini-test-connection-result")).toBeVisible({ timeout: 10000 });
});
test("API endpoint handles missing API key gracefully", async ({ page }) => {
// Verify proper error handling when no API key is provided
await page.route("**/api/gemini/test", async (route) => {
await route.fulfill({
status: 400,
contentType: "application/json",
body: JSON.stringify({
success: false,
error: "No API key provided or configured in environment",
}),
});
});
await page.goto("/");
await page.getByTestId("settings-button").click();
// Button should be disabled without API key, so the error state
// would only occur if someone bypasses the UI
await expect(page.getByTestId("test-gemini-connection")).toBeDisabled();
});
});
});

View File

@@ -1,256 +0,0 @@
import { test, expect } from "@playwright/test";
test.describe("Gemini SDK Integration", () => {
test.describe("Step 1: Configure Gemini API Key", () => {
test("can navigate to settings and see Gemini API key input", async ({ page }) => {
await page.goto("/");
// Navigate to settings
await page.getByTestId("settings-button").click();
// Should see settings view
await expect(page.getByTestId("settings-view")).toBeVisible();
// Should see Google/Gemini API key input
await expect(page.getByTestId("google-api-key-input")).toBeVisible();
await expect(page.getByText("Google API Key (Gemini)")).toBeVisible();
});
test("can enter and save Gemini API key", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter Gemini API key
const testKey = "AIzaSyTestGeminiKey123";
await page.getByTestId("google-api-key-input").fill(testKey);
// Save settings
await page.getByTestId("save-settings").click();
// Should show saved confirmation
await expect(page.getByText("Saved!")).toBeVisible();
// Reload and verify persistence
await page.reload();
await page.getByTestId("settings-button").click();
// Toggle visibility to verify saved key
await page.getByTestId("toggle-google-visibility").click();
await expect(page.getByTestId("google-api-key-input")).toHaveValue(testKey);
});
test("Gemini API key input is password type by default for security", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Check input type is password (secure)
await expect(page.getByTestId("google-api-key-input")).toHaveAttribute("type", "password");
});
test("can toggle Gemini API key visibility", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Initially password type
await expect(page.getByTestId("google-api-key-input")).toHaveAttribute("type", "password");
// Toggle to show
await page.getByTestId("toggle-google-visibility").click();
await expect(page.getByTestId("google-api-key-input")).toHaveAttribute("type", "text");
// Toggle back to hide
await page.getByTestId("toggle-google-visibility").click();
await expect(page.getByTestId("google-api-key-input")).toHaveAttribute("type", "password");
});
});
test.describe("Step 2: Send image/design prompt", () => {
test("test Gemini connection button exists", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Should see the test Gemini connection button
await expect(page.getByTestId("test-gemini-connection")).toBeVisible();
});
test("test Gemini connection button is disabled without API key", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Clear any existing key
await page.getByTestId("google-api-key-input").clear();
// Button should be disabled
await expect(page.getByTestId("test-gemini-connection")).toBeDisabled();
});
test("test Gemini connection button is enabled with API key", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter an API key
await page.getByTestId("google-api-key-input").fill("AIzaSyTestKey123");
// Button should be enabled
await expect(page.getByTestId("test-gemini-connection")).toBeEnabled();
});
test("clicking test button shows loading state", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter API key
await page.getByTestId("google-api-key-input").fill("AIzaSyTestKey123");
// Mock the API response with a delay to catch loading state
await page.route("/api/gemini/test", async (route) => {
// Delay to show loading state
await new Promise((resolve) => setTimeout(resolve, 500));
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
success: true,
message: "Connection successful!",
model: "gemini-1.5-flash",
}),
});
});
// Click test button
await page.getByTestId("test-gemini-connection").click();
// Should show loading state
await expect(page.getByText("Testing...")).toBeVisible();
});
});
test.describe("Step 3: Verify response received", () => {
test("shows success message on successful Gemini API test", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter API key
await page.getByTestId("google-api-key-input").fill("AIzaSyTestKey123");
// Mock successful response
await page.route("/api/gemini/test", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
success: true,
message: 'Connection successful! Response: "Gemini SDK connection successful!"',
model: "gemini-1.5-flash",
hasImage: false,
}),
});
});
// Click test button
await page.getByTestId("test-gemini-connection").click();
// Should show success result
await expect(page.getByTestId("gemini-test-connection-result")).toBeVisible();
await expect(page.getByTestId("gemini-test-connection-message")).toContainText("Connection successful");
});
test("shows error message on failed Gemini API test", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter API key
await page.getByTestId("google-api-key-input").fill("invalid-key");
// Mock error response
await page.route("/api/gemini/test", async (route) => {
await route.fulfill({
status: 401,
contentType: "application/json",
body: JSON.stringify({
success: false,
error: "Invalid API key. Please check your Google API key.",
}),
});
});
// Click test button
await page.getByTestId("test-gemini-connection").click();
// Should show error result
await expect(page.getByTestId("gemini-test-connection-result")).toBeVisible();
await expect(page.getByTestId("gemini-test-connection-message")).toContainText("Invalid API key");
});
test("shows network error message on connection failure", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter API key
await page.getByTestId("google-api-key-input").fill("AIzaSyTestKey123");
// Mock network error
await page.route("/api/gemini/test", async (route) => {
await route.abort("connectionfailed");
});
// Click test button
await page.getByTestId("test-gemini-connection").click();
// Should show error result
await expect(page.getByTestId("gemini-test-connection-result")).toBeVisible();
await expect(page.getByTestId("gemini-test-connection-message")).toContainText("Network error");
});
});
test.describe("Gemini API Route - Image/Design Prompt Support", () => {
test("API route accepts and processes image data for design prompts", async ({ page }) => {
await page.goto("/");
// Directly test the API endpoint with image data
const response = await page.request.post("/api/gemini/test", {
data: {
apiKey: "test-key-for-mock",
imageData: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==", // 1x1 transparent PNG
mimeType: "image/png",
prompt: "Describe this image",
},
});
// We expect some response (even if error due to invalid key)
const data = await response.json();
// The endpoint should process the request (not crash)
expect(data).toHaveProperty("success");
});
test("API route handles text-only prompts", async ({ page }) => {
await page.goto("/");
// Test the API endpoint with text-only prompt
const response = await page.request.post("/api/gemini/test", {
data: {
apiKey: "test-key",
prompt: "Hello Gemini",
},
});
// Should return a valid response structure
const data = await response.json();
expect(data).toHaveProperty("success");
});
test("API route returns error when no API key provided", async ({ page }) => {
await page.goto("/");
// Test the API endpoint without API key
const response = await page.request.post("/api/gemini/test", {
data: {},
});
// Should return error about missing API key
const data = await response.json();
expect(data.success).toBe(false);
expect(data.error).toContain("No API key");
});
});
});

View File

@@ -1,258 +0,0 @@
import { test, expect } from "@playwright/test";
test.describe("Interactive New Project Interview", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/");
});
test("Step 1: Click 'New Project' -> 'Interactive Mode'", async ({ page }) => {
// Click the Create Project button to open the dropdown
await page.getByTestId("create-new-project").click();
// Verify the dropdown menu is visible
await expect(page.getByTestId("interactive-mode-option")).toBeVisible();
await expect(page.getByTestId("quick-setup-option")).toBeVisible();
// Click on Interactive Mode
await page.getByTestId("interactive-mode-option").click();
// Verify we navigate to the interview view
await expect(page.getByTestId("interview-view")).toBeVisible();
});
test("Step 2: Chat interface appears asking 'What do you want to build?'", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Verify interview view is displayed
await expect(page.getByTestId("interview-view")).toBeVisible();
// Verify the chat interface is present
await expect(page.getByTestId("interview-messages")).toBeVisible();
await expect(page.getByTestId("interview-input")).toBeVisible();
// Verify the first question is asking what to build
await expect(page.getByText("What do you want to build?")).toBeVisible();
});
test("Step 3: User replies 'A todo app'", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Wait for interview view
await expect(page.getByTestId("interview-view")).toBeVisible();
// Type a response
await page.getByTestId("interview-input").fill("A todo app");
await page.getByTestId("interview-send").click();
// Verify user message appears in chat
await expect(page.getByText("A todo app")).toBeVisible();
});
test("Step 4: Agent asks clarifying questions (e.g. 'What tech stack?')", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Wait for interview view
await expect(page.getByTestId("interview-view")).toBeVisible();
// Answer first question
await page.getByTestId("interview-input").fill("A todo app with tasks and categories");
await page.getByTestId("interview-send").click();
// Wait for the next question about tech stack
await expect(page.getByText("What tech stack would you like to use?")).toBeVisible({ timeout: 3000 });
});
test("Step 5: Agent generates draft app_spec.txt based on conversation", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Wait for interview view
await expect(page.getByTestId("interview-view")).toBeVisible();
// Answer all questions
// Question 1: What do you want to build?
await page.getByTestId("interview-input").fill("A todo app");
await page.getByTestId("interview-send").click();
// Wait for question 2
await expect(page.getByText("What tech stack would you like to use?")).toBeVisible({ timeout: 3000 });
// Question 2: Tech stack
await page.getByTestId("interview-input").fill("React, TypeScript, Tailwind CSS");
await page.getByTestId("interview-send").click();
// Wait for question 3
await expect(page.getByText("What are the core features you want to include?")).toBeVisible({ timeout: 3000 });
// Question 3: Core features
await page.getByTestId("interview-input").fill("Add tasks, Mark complete, Delete tasks, Categories");
await page.getByTestId("interview-send").click();
// Wait for question 4
await expect(page.getByText("Any additional requirements or preferences?")).toBeVisible({ timeout: 3000 });
// Question 4: Additional requirements
await page.getByTestId("interview-input").fill("Mobile responsive, Dark mode support");
await page.getByTestId("interview-send").click();
// Wait for spec generation
await expect(page.getByText("Generating specification")).toBeVisible({ timeout: 3000 });
// Wait for project setup form to appear
await expect(page.getByTestId("project-setup-form")).toBeVisible({ timeout: 5000 });
// Verify spec preview contains expected content
await expect(page.getByTestId("spec-preview")).toBeVisible();
await expect(page.getByTestId("spec-preview")).toContainText("project_specification");
await expect(page.getByTestId("spec-preview")).toContainText("A todo app");
// Verify we can enter project name and path
await expect(page.getByTestId("interview-project-name-input")).toBeVisible();
await expect(page.getByTestId("interview-project-path-input")).toBeVisible();
});
test("shows progress indicator throughout interview", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Verify header shows question count
await expect(page.getByText("Question 1 of 4")).toBeVisible();
// Answer first question
await page.getByTestId("interview-input").fill("A todo app");
await page.getByTestId("interview-send").click();
// Verify progress updates
await expect(page.getByText("Question 2 of 4")).toBeVisible({ timeout: 3000 });
});
test("can navigate back to welcome view", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Verify interview view is displayed
await expect(page.getByTestId("interview-view")).toBeVisible();
// Click back button
await page.getByTestId("interview-back-button").click();
// Verify we're back at welcome view
await expect(page.getByTestId("welcome-view")).toBeVisible();
});
test("dropdown shows both Quick Setup and Interactive Mode options", async ({ page }) => {
// Click the Create Project button
await page.getByTestId("create-new-project").click();
// Verify both options are present
await expect(page.getByTestId("quick-setup-option")).toBeVisible();
await expect(page.getByText("Quick Setup")).toBeVisible();
await expect(page.getByTestId("interactive-mode-option")).toBeVisible();
await expect(page.getByText("Interactive Mode")).toBeVisible();
});
test("Quick Setup option opens the original new project dialog", async ({ page }) => {
// Click the Create Project button
await page.getByTestId("create-new-project").click();
// Click Quick Setup
await page.getByTestId("quick-setup-option").click();
// Verify the original dialog appears
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await expect(page.getByText("Create New Project")).toBeVisible();
});
test("can create project after completing interview", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Complete all interview questions
// Question 1
await page.getByTestId("interview-input").fill("A todo app");
await page.getByTestId("interview-send").click();
await expect(page.getByText("What tech stack would you like to use?")).toBeVisible({ timeout: 3000 });
// Question 2
await page.getByTestId("interview-input").fill("React, Node.js");
await page.getByTestId("interview-send").click();
await expect(page.getByText("What are the core features you want to include?")).toBeVisible({ timeout: 3000 });
// Question 3
await page.getByTestId("interview-input").fill("Add tasks, Delete tasks");
await page.getByTestId("interview-send").click();
await expect(page.getByText("Any additional requirements or preferences?")).toBeVisible({ timeout: 3000 });
// Question 4
await page.getByTestId("interview-input").fill("None");
await page.getByTestId("interview-send").click();
// Wait for project setup form
await expect(page.getByTestId("project-setup-form")).toBeVisible({ timeout: 5000 });
// Fill in project details
await page.getByTestId("interview-project-name-input").fill("my-todo-app");
await page.getByTestId("interview-project-path-input").fill("/Users/test/projects");
// Create project button should be enabled
await expect(page.getByTestId("interview-create-project")).toBeEnabled();
// Click create project
await page.getByTestId("interview-create-project").click();
// Should navigate to board view with the new project
await expect(page.getByTestId("board-view")).toBeVisible({ timeout: 5000 });
});
test("create project button is disabled without name and path", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Complete all interview questions quickly
await page.getByTestId("interview-input").fill("A todo app");
await page.getByTestId("interview-send").click();
await expect(page.getByText("What tech stack")).toBeVisible({ timeout: 3000 });
await page.getByTestId("interview-input").fill("React");
await page.getByTestId("interview-send").click();
await expect(page.getByText("core features")).toBeVisible({ timeout: 3000 });
await page.getByTestId("interview-input").fill("Tasks");
await page.getByTestId("interview-send").click();
await expect(page.getByText("additional requirements")).toBeVisible({ timeout: 3000 });
await page.getByTestId("interview-input").fill("None");
await page.getByTestId("interview-send").click();
// Wait for project setup form
await expect(page.getByTestId("project-setup-form")).toBeVisible({ timeout: 5000 });
// Create button should be disabled initially
await expect(page.getByTestId("interview-create-project")).toBeDisabled();
// Fill only name
await page.getByTestId("interview-project-name-input").fill("my-project");
await expect(page.getByTestId("interview-create-project")).toBeDisabled();
// Clear name and fill only path
await page.getByTestId("interview-project-name-input").clear();
await page.getByTestId("interview-project-path-input").fill("/some/path");
await expect(page.getByTestId("interview-create-project")).toBeDisabled();
// Fill both - should be enabled
await page.getByTestId("interview-project-name-input").fill("my-project");
await expect(page.getByTestId("interview-create-project")).toBeEnabled();
});
});

View File

@@ -1,398 +0,0 @@
import { test, expect } from "@playwright/test";
test.describe("Interactive New Project Interview", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/");
});
test("Step 1: Click 'New Project' -> 'Interactive Mode' navigates to interview view", async ({ page }) => {
// Click the Create Project dropdown button
await page.getByTestId("create-new-project").click();
// Wait for dropdown to appear and click Interactive Mode option
await page.getByTestId("interactive-mode-option").click();
// Verify interview view is displayed
await expect(page.getByTestId("interview-view")).toBeVisible();
// Verify the header shows "New Project Interview"
await expect(page.getByText("New Project Interview")).toBeVisible();
});
test("Step 2: Chat interface appears asking 'What do you want to build?'", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Verify interview view is displayed
await expect(page.getByTestId("interview-view")).toBeVisible();
// Verify the first question is displayed
await expect(page.getByTestId("interview-messages")).toBeVisible();
await expect(page.getByText("What do you want to build?")).toBeVisible();
// Verify input field is available
await expect(page.getByTestId("interview-input")).toBeVisible();
await expect(page.getByTestId("interview-send")).toBeVisible();
});
test("Step 3: User can reply 'A todo app'", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Verify interview view is displayed
await expect(page.getByTestId("interview-view")).toBeVisible();
// Type the answer in the input field
await page.getByTestId("interview-input").fill("A todo app");
// Click send button
await page.getByTestId("interview-send").click();
// Verify user message appears in the chat
await expect(page.getByTestId("interview-messages").getByText("A todo app")).toBeVisible();
});
test("Step 4: Agent asks clarifying questions (tech stack)", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Wait for interview view
await expect(page.getByTestId("interview-view")).toBeVisible();
// Answer first question
await page.getByTestId("interview-input").fill("A todo app");
await page.getByTestId("interview-send").click();
// Wait for the next question to appear (tech stack question)
await expect(page.getByText("What tech stack would you like to use?")).toBeVisible({ timeout: 5000 });
// Verify progress text shows question 2 of 4
await expect(page.getByText("Question 2 of 4")).toBeVisible();
});
test("Step 5: Agent generates draft app_spec.txt based on conversation", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Wait for interview view
await expect(page.getByTestId("interview-view")).toBeVisible();
// Answer first question - project description
await page.getByTestId("interview-input").fill("A todo app");
await page.getByTestId("interview-send").click();
// Wait for tech stack question
await expect(page.getByText("What tech stack would you like to use?")).toBeVisible({ timeout: 5000 });
// Answer tech stack question
await page.getByTestId("interview-input").fill("React, Next.js, TypeScript");
await page.getByTestId("interview-send").click();
// Wait for features question
await expect(page.getByText("What are the core features you want to include?")).toBeVisible({ timeout: 5000 });
// Answer features question
await page.getByTestId("interview-input").fill("Add tasks, Mark complete, Delete tasks");
await page.getByTestId("interview-send").click();
// Wait for additional requirements question
await expect(page.getByText("Any additional requirements or preferences?")).toBeVisible({ timeout: 5000 });
// Answer additional requirements
await page.getByTestId("interview-input").fill("Dark mode support");
await page.getByTestId("interview-send").click();
// Wait for spec generation to complete
await expect(page.getByTestId("project-setup-form")).toBeVisible({ timeout: 10000 });
// Verify spec preview is visible with generated content
await expect(page.getByTestId("spec-preview")).toBeVisible();
// Verify the generated spec contains the project description
const specPreview = page.getByTestId("spec-preview");
await expect(specPreview).toContainText("A todo app");
// Verify it contains tech stack information
await expect(specPreview).toContainText("React");
// Verify it contains features
await expect(specPreview).toContainText("Add tasks");
});
test("Interview shows progress indicator with correct number of steps", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Verify interview view is displayed
await expect(page.getByTestId("interview-view")).toBeVisible();
// Verify progress text shows question 1 of 4
await expect(page.getByText("Question 1 of 4")).toBeVisible();
});
test("Send button is disabled when input is empty", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Verify send button is disabled when input is empty
await expect(page.getByTestId("interview-send")).toBeDisabled();
// Type something
await page.getByTestId("interview-input").fill("Test");
// Now button should be enabled
await expect(page.getByTestId("interview-send")).toBeEnabled();
// Clear input
await page.getByTestId("interview-input").fill("");
// Button should be disabled again
await expect(page.getByTestId("interview-send")).toBeDisabled();
});
test("Can submit answer by pressing Enter", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Verify interview view is displayed
await expect(page.getByTestId("interview-view")).toBeVisible();
// Type answer and press Enter
await page.getByTestId("interview-input").fill("A todo app");
await page.getByTestId("interview-input").press("Enter");
// Verify user message appears
await expect(page.getByTestId("interview-messages").getByText("A todo app")).toBeVisible();
});
test("Back button returns to welcome view", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Verify interview view is displayed
await expect(page.getByTestId("interview-view")).toBeVisible();
// Click back button
await page.getByTestId("interview-back-button").click();
// Verify we're back on welcome view
await expect(page.getByTestId("welcome-view")).toBeVisible();
});
test("Project setup form appears after completing interview", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Complete all questions
await page.getByTestId("interview-input").fill("A simple todo app");
await page.getByTestId("interview-send").click();
await expect(page.getByText("What tech stack would you like to use?")).toBeVisible({ timeout: 5000 });
await page.getByTestId("interview-input").fill("React");
await page.getByTestId("interview-send").click();
await expect(page.getByText("What are the core features")).toBeVisible({ timeout: 5000 });
await page.getByTestId("interview-input").fill("Add, edit, delete tasks");
await page.getByTestId("interview-send").click();
await expect(page.getByText("Any additional requirements")).toBeVisible({ timeout: 5000 });
await page.getByTestId("interview-input").fill("None");
await page.getByTestId("interview-send").click();
// Wait for project setup form
await expect(page.getByTestId("project-setup-form")).toBeVisible({ timeout: 10000 });
// Verify form has name input
await expect(page.getByTestId("interview-project-name-input")).toBeVisible();
// Verify form has path input
await expect(page.getByTestId("interview-project-path-input")).toBeVisible();
// Verify browse directory button
await expect(page.getByTestId("interview-browse-directory")).toBeVisible();
// Verify create project button
await expect(page.getByTestId("interview-create-project")).toBeVisible();
});
test("Create project button is disabled without name and path", async ({ page }) => {
// Navigate to interview view and complete interview
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Complete all questions quickly
await page.getByTestId("interview-input").fill("A simple todo app");
await page.getByTestId("interview-send").click();
await expect(page.getByText("What tech stack")).toBeVisible({ timeout: 5000 });
await page.getByTestId("interview-input").fill("React");
await page.getByTestId("interview-send").click();
await expect(page.getByText("core features")).toBeVisible({ timeout: 5000 });
await page.getByTestId("interview-input").fill("Tasks");
await page.getByTestId("interview-send").click();
await expect(page.getByText("additional requirements")).toBeVisible({ timeout: 5000 });
await page.getByTestId("interview-input").fill("None");
await page.getByTestId("interview-send").click();
// Wait for project setup form
await expect(page.getByTestId("project-setup-form")).toBeVisible({ timeout: 10000 });
// Create button should be disabled without name and path
await expect(page.getByTestId("interview-create-project")).toBeDisabled();
// Enter project name
await page.getByTestId("interview-project-name-input").fill("my-todo-app");
// Still disabled (no path)
await expect(page.getByTestId("interview-create-project")).toBeDisabled();
// Enter path
await page.getByTestId("interview-project-path-input").fill("/Users/test/projects");
// Now should be enabled
await expect(page.getByTestId("interview-create-project")).toBeEnabled();
});
test("Creates project and navigates to board view after interview", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Complete all questions
await page.getByTestId("interview-input").fill("A simple todo app");
await page.getByTestId("interview-send").click();
await expect(page.getByText("What tech stack")).toBeVisible({ timeout: 5000 });
await page.getByTestId("interview-input").fill("React");
await page.getByTestId("interview-send").click();
await expect(page.getByText("core features")).toBeVisible({ timeout: 5000 });
await page.getByTestId("interview-input").fill("Tasks");
await page.getByTestId("interview-send").click();
await expect(page.getByText("additional requirements")).toBeVisible({ timeout: 5000 });
await page.getByTestId("interview-input").fill("None");
await page.getByTestId("interview-send").click();
// Wait for project setup form
await expect(page.getByTestId("project-setup-form")).toBeVisible({ timeout: 10000 });
// Fill in project details
await page.getByTestId("interview-project-name-input").fill("interview-test-project");
await page.getByTestId("interview-project-path-input").fill("/Users/test/projects");
// Click create
await page.getByTestId("interview-create-project").click();
// Should navigate to board view
await expect(page.getByTestId("board-view")).toBeVisible({ timeout: 5000 });
// Project name should be visible
await expect(page.getByTestId("board-view").getByText("interview-test-project")).toBeVisible();
});
test("Interview messages have timestamps", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Verify interview view is displayed
await expect(page.getByTestId("interview-view")).toBeVisible();
// The welcome message should have a timestamp displayed
// Timestamps are in format like "10:30:45 AM" or similar
const messagesArea = page.getByTestId("interview-messages");
await expect(messagesArea).toBeVisible();
// The welcome message should contain the first question
await expect(messagesArea.getByText("What do you want to build?")).toBeVisible();
// The message area should contain timestamp text (time format like "10:30:45 AM")
// We verify by checking that the welcome message exists and has content
await expect(messagesArea.locator("p.text-sm").first()).toBeVisible();
});
test("Input field is hidden after interview completes", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Complete all questions
await page.getByTestId("interview-input").fill("A todo app");
await page.getByTestId("interview-send").click();
await expect(page.getByText("What tech stack")).toBeVisible({ timeout: 5000 });
await page.getByTestId("interview-input").fill("React");
await page.getByTestId("interview-send").click();
await expect(page.getByText("core features")).toBeVisible({ timeout: 5000 });
await page.getByTestId("interview-input").fill("Tasks");
await page.getByTestId("interview-send").click();
await expect(page.getByText("additional requirements")).toBeVisible({ timeout: 5000 });
await page.getByTestId("interview-input").fill("None");
await page.getByTestId("interview-send").click();
// Wait for project setup form (interview complete)
await expect(page.getByTestId("project-setup-form")).toBeVisible({ timeout: 10000 });
// Input field should no longer be visible
await expect(page.getByTestId("interview-input")).not.toBeVisible();
});
test("Generated spec contains proper XML structure", async ({ page }) => {
// Navigate to interview view
await page.getByTestId("create-new-project").click();
await page.getByTestId("interactive-mode-option").click();
// Complete all questions
await page.getByTestId("interview-input").fill("A todo app");
await page.getByTestId("interview-send").click();
await expect(page.getByText("What tech stack")).toBeVisible({ timeout: 5000 });
await page.getByTestId("interview-input").fill("React, TypeScript");
await page.getByTestId("interview-send").click();
await expect(page.getByText("core features")).toBeVisible({ timeout: 5000 });
await page.getByTestId("interview-input").fill("Add tasks, Delete tasks");
await page.getByTestId("interview-send").click();
await expect(page.getByText("additional requirements")).toBeVisible({ timeout: 5000 });
await page.getByTestId("interview-input").fill("Mobile responsive");
await page.getByTestId("interview-send").click();
// Wait for spec preview
await expect(page.getByTestId("spec-preview")).toBeVisible({ timeout: 10000 });
// Verify XML structure elements
const specPreview = page.getByTestId("spec-preview");
await expect(specPreview).toContainText("<project_specification>");
await expect(specPreview).toContainText("<overview>");
await expect(specPreview).toContainText("<technology_stack>");
await expect(specPreview).toContainText("<core_capabilities>");
await expect(specPreview).toContainText("<development_guidelines>");
});
test("Quick Setup option still works from dropdown", async ({ page }) => {
// Click the Create Project dropdown button
await page.getByTestId("create-new-project").click();
// Click Quick Setup option
await page.getByTestId("quick-setup-option").click();
// Verify dialog appears (not interview view)
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await expect(page.getByText("Create New Project")).toBeVisible();
});
});

View File

@@ -1,307 +0,0 @@
import { test, expect } from "@playwright/test";
test.describe("Kanban Board", () => {
// Helper to set up a mock project in localStorage
async function setupMockProject(page: ReturnType<typeof test.step>) {
await page.addInitScript(() => {
const mockProject = {
id: "test-project-1",
name: "Test Project",
path: "/mock/test-project",
lastOpened: new Date().toISOString(),
};
localStorage.setItem(
"automaker-storage",
JSON.stringify({
state: {
projects: [mockProject],
currentProject: mockProject,
currentView: "board",
sidebarOpen: true,
theme: "dark",
},
version: 0,
})
);
});
}
test("shows Add Feature button", async ({ page }) => {
await setupMockProject(page);
await page.goto("/");
await expect(page.getByTestId("add-feature-button")).toBeVisible();
});
test("opens add feature dialog", async ({ page }) => {
await setupMockProject(page);
await page.goto("/");
// Click add feature button
await page.getByTestId("add-feature-button").click();
// Dialog should appear
await expect(page.getByTestId("add-feature-dialog")).toBeVisible();
await expect(page.getByTestId("feature-category-input")).toBeVisible();
await expect(page.getByTestId("feature-description-input")).toBeVisible();
});
test("can add a new feature", async ({ page }) => {
await setupMockProject(page);
await page.goto("/");
// Wait for board to load
await expect(page.getByTestId("board-view")).toBeVisible();
// Click add feature button
await page.getByTestId("add-feature-button").click();
// Fill in feature details
await page.getByTestId("feature-category-input").fill("Test Category");
await page
.getByTestId("feature-description-input")
.fill("Test Feature Description");
await page.getByTestId("feature-step-0-input").fill("Step 1: First step");
// Submit the form
await page.getByTestId("confirm-add-feature").click();
// Dialog should close
await expect(page.getByTestId("add-feature-dialog")).not.toBeVisible();
});
test("refresh button is visible", async ({ page }) => {
await setupMockProject(page);
await page.goto("/");
await expect(page.getByTestId("refresh-board")).toBeVisible();
});
test("loads cards from .automaker/feature_list.json and displays them in correct columns", async ({
page,
}) => {
await setupMockProject(page);
await page.goto("/");
// Wait for board to load
await expect(page.getByTestId("board-view")).toBeVisible();
// Wait for loading to complete (the mock IPC returns a sample feature)
// The mock returns a feature in "backlog" column (passes: false)
await expect(page.getByTestId("kanban-column-backlog")).toBeVisible();
// After loading, the backlog should show the sample feature from mock data
// Looking at the electron.ts mock, it returns one feature with "Sample Feature"
const backlogColumn = page.getByTestId("kanban-column-backlog");
await expect(backlogColumn.getByText("Sample Feature")).toBeVisible();
});
test("features with passes:true appear in verified column", async ({
page,
}) => {
// Create a project and add a feature manually
await setupMockProject(page);
await page.goto("/");
// Wait for board to load
await expect(page.getByTestId("board-view")).toBeVisible();
// Add a new feature
await page.getByTestId("add-feature-button").click();
await page.getByTestId("feature-category-input").fill("Core");
await page
.getByTestId("feature-description-input")
.fill("Verified Test Feature");
await page.getByTestId("confirm-add-feature").click();
// The new feature should appear in backlog
await expect(
page
.getByTestId("kanban-column-backlog")
.getByText("Verified Test Feature")
).toBeVisible();
});
test("can edit feature card details", async ({ page }) => {
await setupMockProject(page);
await page.goto("/");
// Wait for board to load
await expect(page.getByTestId("board-view")).toBeVisible();
// Wait for features to load - the mock returns "Sample Feature"
await expect(
page.getByTestId("kanban-column-backlog").getByText("Sample Feature")
).toBeVisible();
// Find and click the edit button on the card using specific testid pattern
const backlogColumn = page.getByTestId("kanban-column-backlog");
// The edit button has testid "edit-feature-{feature.id}" where feature.id contains "feature-0-"
const editButton = backlogColumn.locator(
'[data-testid^="edit-feature-feature-0-"]'
);
await editButton.click();
// Edit dialog should appear
await expect(page.getByTestId("edit-feature-dialog")).toBeVisible();
// Edit the description
await page
.getByTestId("edit-feature-description")
.fill("Updated Feature Description");
// Save the changes
await page.getByTestId("confirm-edit-feature").click();
// Dialog should close
await expect(page.getByTestId("edit-feature-dialog")).not.toBeVisible();
// The updated description should be visible
await expect(page.getByText("Updated Feature Description")).toBeVisible();
});
test("edit dialog shows existing feature data", async ({ page }) => {
await setupMockProject(page);
await page.goto("/");
// Wait for board to load
await expect(page.getByTestId("board-view")).toBeVisible();
// Wait for features to load
await expect(
page.getByTestId("kanban-column-backlog").getByText("Sample Feature")
).toBeVisible();
// Click edit button using specific testid pattern
const backlogColumn = page.getByTestId("kanban-column-backlog");
const editButton = backlogColumn.locator(
'[data-testid^="edit-feature-feature-0-"]'
);
await editButton.click();
// Check that the dialog pre-populates with existing data
await expect(page.getByTestId("edit-feature-description")).toHaveValue(
"Sample Feature"
);
await expect(page.getByTestId("edit-feature-category")).toHaveValue("Core");
});
test("can drag card from Backlog to In Progress column", async ({ page }) => {
await setupMockProject(page);
await page.goto("/");
// Wait for board to load
await expect(page.getByTestId("board-view")).toBeVisible();
// Wait for features to load in Backlog
const backlogColumn = page.getByTestId("kanban-column-backlog");
const inProgressColumn = page.getByTestId("kanban-column-in_progress");
await expect(backlogColumn.getByText("Sample Feature")).toBeVisible();
// Find the drag handle specifically
const dragHandle = backlogColumn.locator(
'[data-testid^="drag-handle-feature-0-"]'
);
await expect(dragHandle).toBeVisible();
// Get drag handle and target positions
const handleBox = await dragHandle.boundingBox();
const targetBox = await inProgressColumn.boundingBox();
if (!handleBox || !targetBox) throw new Error("Could not find elements");
// Use mouse events - start from center of drag handle
const startX = handleBox.x + handleBox.width / 2;
const startY = handleBox.y + handleBox.height / 2;
const endX = targetBox.x + targetBox.width / 2;
const endY = targetBox.y + 100;
await page.mouse.move(startX, startY);
await page.mouse.down();
// Move in steps to trigger dnd-kit activation (needs >8px movement)
await page.mouse.move(endX, endY, { steps: 20 });
await page.mouse.up();
// Verify card moved to In Progress column
await expect(inProgressColumn.getByText("Sample Feature")).toBeVisible();
// Verify card is no longer in Backlog
await expect(backlogColumn.getByText("Sample Feature")).not.toBeVisible();
});
test("displays delete button (trash icon) on feature card", async ({
page,
}) => {
await setupMockProject(page);
await page.goto("/");
// Wait for board to load
await expect(page.getByTestId("board-view")).toBeVisible();
// Wait for features to load in Backlog
const backlogColumn = page.getByTestId("kanban-column-backlog");
await expect(backlogColumn.getByText("Sample Feature")).toBeVisible();
// Find the delete button on the card
const deleteButton = backlogColumn.locator(
'[data-testid^="delete-feature-feature-0-"]'
);
await expect(deleteButton).toBeVisible();
});
test("can delete a feature from kanban board", async ({ page }) => {
await setupMockProject(page);
await page.goto("/");
// Wait for board to load
await expect(page.getByTestId("board-view")).toBeVisible();
// Wait for features to load in Backlog
const backlogColumn = page.getByTestId("kanban-column-backlog");
await expect(backlogColumn.getByText("Sample Feature")).toBeVisible();
// Find and click the delete button
const deleteButton = backlogColumn.locator(
'[data-testid^="delete-feature-feature-0-"]'
);
await deleteButton.click();
// Verify the feature is removed from the board
await expect(backlogColumn.getByText("Sample Feature")).not.toBeVisible();
});
test("deleting feature removes it from all columns", async ({ page }) => {
await setupMockProject(page);
await page.goto("/");
// Wait for board to load
await expect(page.getByTestId("board-view")).toBeVisible();
// Add a new feature first
await page.getByTestId("add-feature-button").click();
await page.getByTestId("feature-category-input").fill("Test Category");
await page
.getByTestId("feature-description-input")
.fill("Feature to Delete");
await page.getByTestId("confirm-add-feature").click();
// Wait for the new feature to appear in backlog
const backlogColumn = page.getByTestId("kanban-column-backlog");
await expect(backlogColumn.getByText("Feature to Delete")).toBeVisible();
// Find and click the delete button for the newly added feature
const deleteButton = backlogColumn
.locator('[data-testid^="delete-feature-feature-"]')
.last();
await deleteButton.click();
// Verify the feature is removed
await expect(
backlogColumn.getByText("Feature to Delete")
).not.toBeVisible();
// Also verify it's not anywhere else on the board
await expect(page.getByText("Feature to Delete")).not.toBeVisible();
});
});

View File

@@ -1,104 +0,0 @@
import { test, expect } from "@playwright/test";
test.describe("New Chat Session Auto Focus", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/");
// Create a new project first
await page.getByTestId("new-project-card").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
// Enter project details
await page.getByTestId("project-name-input").fill("test-session-project");
await page.getByTestId("project-path-input").fill("/Users/test/session-projects");
// Click create
await page.getByTestId("confirm-create-project").click();
// Should navigate to board view
await expect(page.getByTestId("board-view")).toBeVisible();
// Navigate to Agent view
await page.getByTestId("nav-agent").click();
await expect(page.getByTestId("agent-view")).toBeVisible();
});
test("clicking new session button creates a session with random name", async ({ page }) => {
// Click the "New" session button
const newSessionButton = page.getByTestId("new-session-button");
await expect(newSessionButton).toBeVisible();
await newSessionButton.click();
// Wait for the session to be created - check for session item in the list
const sessionList = page.getByTestId("session-list");
await expect(sessionList).toBeVisible();
// The session should appear in the list
await expect(sessionList.locator('[data-testid^="session-item-"]').first()).toBeVisible({ timeout: 5000 });
// The session name should follow the pattern of random names (contains letters and numbers)
const sessionName = sessionList.locator('[data-testid^="session-item-"]').first().locator("h3");
await expect(sessionName).toBeVisible();
const nameText = await sessionName.textContent();
expect(nameText).toBeTruthy();
// Verify the name follows our pattern: "Adjective Noun Number"
expect(nameText).toMatch(/^[A-Z][a-z]+ [A-Z][a-z]+ \d+$/);
});
test("verify session was created and selected", async ({ page }) => {
// Click the "New" session button
const newSessionButton = page.getByTestId("new-session-button");
await newSessionButton.click();
// Wait for session to be created
const sessionList = page.getByTestId("session-list");
await expect(sessionList.locator('[data-testid^="session-item-"]').first()).toBeVisible({ timeout: 5000 });
// Verify the session is selected (has the primary border class)
const sessionItem = sessionList.locator('[data-testid^="session-item-"]').first();
await expect(sessionItem).toHaveClass(/border-primary/);
// Verify the message list is visible (session is active)
await expect(page.getByTestId("message-list")).toBeVisible();
});
test("verify chat input is focused after creating new session", async ({ page }) => {
// Click the "New" session button
const newSessionButton = page.getByTestId("new-session-button");
await newSessionButton.click();
// Wait for session to be created
const sessionList = page.getByTestId("session-list");
await expect(sessionList.locator('[data-testid^="session-item-"]').first()).toBeVisible({ timeout: 5000 });
// Wait for the input to be focused (there's a 200ms delay in the code)
await page.waitForTimeout(300);
// Verify the chat input is focused
const chatInput = page.getByTestId("agent-input");
await expect(chatInput).toBeVisible();
await expect(chatInput).toBeFocused();
});
test("complete flow: click new session, verify session created, verify input focused", async ({ page }) => {
// Step 1: Click new session
const newSessionButton = page.getByTestId("new-session-button");
await expect(newSessionButton).toBeVisible();
await newSessionButton.click();
// Step 2: Verify session was created
const sessionList = page.getByTestId("session-list");
await expect(sessionList.locator('[data-testid^="session-item-"]').first()).toBeVisible({ timeout: 5000 });
// Verify the session has a randomly generated name
const sessionName = sessionList.locator('[data-testid^="session-item-"]').first().locator("h3");
const nameText = await sessionName.textContent();
expect(nameText).toBeTruthy();
expect(nameText).toMatch(/^[A-Z][a-z]+ [A-Z][a-z]+ \d+$/);
// Step 3: Verify chat input focused
await page.waitForTimeout(300);
const chatInput = page.getByTestId("agent-input");
await expect(chatInput).toBeFocused();
});
});

View File

@@ -1,237 +0,0 @@
import { test, expect } from "@playwright/test";
test.describe("New Project Workflow", () => {
test("opens new project dialog when clicking Create Project", async ({
page,
}) => {
await page.goto("/");
// Click the New Project card
await page.getByTestId("new-project-card").click();
// Dialog should appear
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
await expect(page.getByText("Create New Project")).toBeVisible();
});
test("shows project name and directory inputs", async ({ page }) => {
await page.goto("/");
// Open dialog
await page.getByTestId("new-project-card").click();
// Check inputs exist
await expect(page.getByTestId("project-name-input")).toBeVisible();
await expect(page.getByTestId("project-path-input")).toBeVisible();
await expect(page.getByTestId("browse-directory")).toBeVisible();
});
test("create button is disabled without name and path", async ({ page }) => {
await page.goto("/");
// Open dialog
await page.getByTestId("new-project-card").click();
// Create button should be disabled
await expect(page.getByTestId("confirm-create-project")).toBeDisabled();
});
test("can enter project name", async ({ page }) => {
await page.goto("/");
// Open dialog
await page.getByTestId("new-project-card").click();
// Enter project name
await page.getByTestId("project-name-input").fill("my-test-project");
await expect(page.getByTestId("project-name-input")).toHaveValue(
"my-test-project"
);
});
test("can close dialog with cancel button", async ({ page }) => {
await page.goto("/");
// Open dialog
await page.getByTestId("new-project-card").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
// Close with cancel
await page.getByRole("button", { name: "Cancel" }).click();
await expect(page.getByTestId("new-project-dialog")).not.toBeVisible();
});
test("create button enables when name and path are entered", async ({
page,
}) => {
await page.goto("/");
// Open dialog
await page.getByTestId("new-project-card").click();
// Create button should be disabled initially
await expect(page.getByTestId("confirm-create-project")).toBeDisabled();
// Enter project name
await page.getByTestId("project-name-input").fill("my-test-project");
// Still disabled (no path)
await expect(page.getByTestId("confirm-create-project")).toBeDisabled();
// Enter path
await page.getByTestId("project-path-input").fill("/Users/test/projects");
// Now should be enabled
await expect(page.getByTestId("confirm-create-project")).toBeEnabled();
});
test("creates project and navigates to board view", async ({ page }) => {
await page.goto("/");
// Open dialog
await page.getByTestId("new-project-card").click();
await expect(page.getByTestId("new-project-dialog")).toBeVisible();
// Enter project details
await page.getByTestId("project-name-input").fill("test-new-project");
await page.getByTestId("project-path-input").fill("/Users/test/projects");
// Click create
await page.getByTestId("confirm-create-project").click();
// Dialog should close
await expect(page.getByTestId("new-project-dialog")).not.toBeVisible();
// Should navigate to board view with the project
await expect(page.getByTestId("board-view")).toBeVisible();
// Project name should be displayed in the board view header
await expect(
page.getByTestId("board-view").getByText("test-new-project")
).toBeVisible();
// Kanban columns should be visible
await expect(page.getByText("Backlog")).toBeVisible();
await expect(page.getByText("In Progress")).toBeVisible();
await expect(page.getByText("Verified")).toBeVisible();
});
test("created project appears in recent projects on welcome view", async ({
page,
}) => {
await page.goto("/");
// Create a project
await page.getByTestId("new-project-card").click();
await page.getByTestId("project-name-input").fill("recent-project-test");
await page.getByTestId("project-path-input").fill("/Users/test/projects");
await page.getByTestId("confirm-create-project").click();
// Verify we're on board view
await expect(page.getByTestId("board-view")).toBeVisible();
// Go back to welcome view by clicking Automaker title (if there's a way)
// For now, reload the page and check recent projects
await page.goto("/");
// The project should appear in recent projects section (use role to be specific)
await expect(
page.getByRole("heading", { name: "Recent Projects" })
).toBeVisible();
await expect(
page
.getByTestId("welcome-view")
.getByText("recent-project-test", { exact: true })
).toBeVisible();
});
});
test.describe("Open Project Workflow", () => {
test("clicking Open Project triggers directory selection", async ({
page,
}) => {
await page.goto("/");
// In web mode, clicking Open Project card will show a prompt dialog
// We can't fully test native dialogs, but we can verify the click works
await expect(page.getByTestId("open-project-card")).toBeVisible();
});
test("opens existing project and navigates to board view", async ({
page,
}) => {
await page.goto("/");
// Mock the window.prompt response
await page.evaluate(() => {
window.prompt = () => "/mock/existing-project";
});
// Click Open Project card
await page.getByTestId("open-project-card").click();
// Should navigate to board view
await expect(page.getByTestId("board-view")).toBeVisible();
// Project name should be derived from path
await expect(
page.getByTestId("board-view").getByText("existing-project")
).toBeVisible();
});
test("opened project loads into dashboard with features", async ({
page,
}) => {
await page.goto("/");
// Mock the window.prompt response
await page.evaluate(() => {
window.prompt = () => "/mock/existing-project";
});
// Click Open Project
await page.getByTestId("open-project-card").click();
// Should show board view
await expect(page.getByTestId("board-view")).toBeVisible();
// Should have loaded features from the mock .automaker/feature_list.json
// The mock returns "Sample Feature" in backlog
await expect(
page.getByTestId("kanban-column-backlog").getByText("Sample Feature")
).toBeVisible();
});
test("can click on recent project to reopen it", async ({ page }) => {
await page.goto("/");
// First, create a project to have it in recent projects
await page.getByTestId("new-project-card").click();
await page.getByTestId("project-name-input").fill("reopenable-project");
await page.getByTestId("project-path-input").fill("/Users/test/projects");
await page.getByTestId("confirm-create-project").click();
// Verify on board view
await expect(page.getByTestId("board-view")).toBeVisible();
// Go back to welcome view
await page.goto("/");
// Wait for recent projects to appear
await expect(
page.getByRole("heading", { name: "Recent Projects" })
).toBeVisible();
// Click on the recent project
const recentProjectCard = page
.getByText("reopenable-project", { exact: true })
.first();
await recentProjectCard.click();
// Should navigate to board view with that project
await expect(page.getByTestId("board-view")).toBeVisible();
await expect(
page.getByTestId("board-view").getByText("reopenable-project")
).toBeVisible();
});
});

View File

@@ -1,112 +0,0 @@
import { test, expect } from "@playwright/test";
test.describe("Settings - API Key Management", () => {
test("can navigate to settings page", async ({ page }) => {
await page.goto("/");
// Click settings button in sidebar
await page.getByTestId("settings-button").click();
// Should show settings view
await expect(page.getByTestId("settings-view")).toBeVisible();
await expect(page.getByRole("heading", { name: "Settings" })).toBeVisible();
await expect(page.getByText("API Keys", { exact: true })).toBeVisible();
});
test("shows Anthropic and Google API key inputs", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Check input fields exist
await expect(page.getByTestId("anthropic-api-key-input")).toBeVisible();
await expect(page.getByTestId("google-api-key-input")).toBeVisible();
});
test("can enter and save Anthropic API key", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter API key
await page.getByTestId("anthropic-api-key-input").fill("sk-ant-test-key-123");
// Save
await page.getByTestId("save-settings").click();
// Should show saved confirmation
await expect(page.getByText("Saved!")).toBeVisible();
// Reload page and verify key persists
await page.reload();
await page.getByTestId("settings-button").click();
// Toggle visibility to see the key
await page.getByTestId("toggle-anthropic-visibility").click();
await expect(page.getByTestId("anthropic-api-key-input")).toHaveValue("sk-ant-test-key-123");
});
test("can enter and save Google API key", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Enter API key
await page.getByTestId("google-api-key-input").fill("AIzaSyTest123");
// Save
await page.getByTestId("save-settings").click();
// Reload page and verify key persists
await page.reload();
await page.getByTestId("settings-button").click();
// Toggle visibility
await page.getByTestId("toggle-google-visibility").click();
await expect(page.getByTestId("google-api-key-input")).toHaveValue("AIzaSyTest123");
});
test("API key inputs are password type by default", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Check input types are password
await expect(page.getByTestId("anthropic-api-key-input")).toHaveAttribute("type", "password");
await expect(page.getByTestId("google-api-key-input")).toHaveAttribute("type", "password");
});
test("can toggle API key visibility", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Initially password type
await expect(page.getByTestId("anthropic-api-key-input")).toHaveAttribute("type", "password");
// Toggle visibility
await page.getByTestId("toggle-anthropic-visibility").click();
// Now should be text type
await expect(page.getByTestId("anthropic-api-key-input")).toHaveAttribute("type", "text");
// Toggle back
await page.getByTestId("toggle-anthropic-visibility").click();
await expect(page.getByTestId("anthropic-api-key-input")).toHaveAttribute("type", "password");
});
test("can navigate back to home from settings", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Click back to home
await page.getByTestId("back-to-home").click();
// Should be back on welcome view
await expect(page.getByTestId("welcome-view")).toBeVisible();
});
test("shows security notice about local storage", async ({ page }) => {
await page.goto("/");
await page.getByTestId("settings-button").click();
// Should show security notice
await expect(page.getByText("Security Notice")).toBeVisible();
await expect(page.getByText(/stored in your browser's local storage/i)).toBeVisible();
});
});