Implement initial project structure and features for Automaker application, including environment setup, auto mode services, and session management. Update port configurations to 3007 and add new UI components for enhanced user interaction.

This commit is contained in:
Cody Seibert
2025-12-08 21:11:00 -05:00
parent 3c8e786f29
commit 9392422d35
67 changed files with 16275 additions and 696 deletions

398
app/tests/interview.spec.ts Normal file
View File

@@ -0,0 +1,398 @@
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();
});
});