Change description field to textarea in Add New Feature modal

The description field in the Add New Feature modal is now a textarea instead of
an input, allowing users to enter multi-line feature descriptions more easily.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Cody Seibert
2025-12-08 22:53:33 -05:00
parent 9392422d35
commit 7bfc489efa
23 changed files with 1319 additions and 1382 deletions

View File

@@ -1,7 +1,9 @@
import { test, expect } from "@playwright/test";
test.describe("Project Analysis", () => {
test("can navigate to analysis view when project is open", async ({ page }) => {
test("can navigate to analysis view when project is open", async ({
page,
}) => {
await page.goto("/");
// Create a project first using dropdown
@@ -22,7 +24,9 @@ test.describe("Project Analysis", () => {
await expect(page.getByTestId("analysis-view")).toBeVisible();
});
test("analysis view shows 'No Analysis Yet' message initially", async ({ page }) => {
test("analysis view shows 'No Analysis Yet' message initially", async ({
page,
}) => {
await page.goto("/");
// Create a project first using dropdown
@@ -30,7 +34,9 @@ test.describe("Project Analysis", () => {
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("project-path-input")
.fill("/test/analysis/project2");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
@@ -51,7 +57,9 @@ test.describe("Project Analysis", () => {
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("project-path-input")
.fill("/test/analysis/project3");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
@@ -71,7 +79,9 @@ test.describe("Project Analysis", () => {
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("project-path-input")
.fill("/test/analysis/project4");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
@@ -98,7 +108,9 @@ test.describe("Project Analysis", () => {
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("project-path-input")
.fill("/test/analysis/project5");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
@@ -124,7 +136,9 @@ test.describe("Project Analysis", () => {
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("project-path-input")
.fill("/test/analysis/project6");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
@@ -142,7 +156,9 @@ test.describe("Project Analysis", () => {
await expect(page.getByTestId("files-by-extension")).toBeVisible();
});
test("file tree displays correct structure with directories and files", async ({ page }) => {
test("file tree displays correct structure with directories and files", async ({
page,
}) => {
await page.goto("/");
// Create a project first using dropdown
@@ -150,7 +166,9 @@ test.describe("Project Analysis", () => {
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("project-path-input")
.fill("/test/analysis/project7");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
@@ -173,7 +191,9 @@ test.describe("Project Analysis", () => {
});
test.describe("Generate Spec from Code", () => {
test("shows Generate Spec card after analysis is complete", async ({ page }) => {
test("shows Generate Spec card after analysis is complete", async ({
page,
}) => {
await page.goto("/");
// Step 1: Open project with code but no spec
@@ -181,8 +201,12 @@ test.describe("Generate Spec from Code", () => {
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("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();
@@ -205,8 +229,12 @@ test.describe("Generate Spec from Code", () => {
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("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();
@@ -220,18 +248,26 @@ test.describe("Generate Spec from Code", () => {
// 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/);
await expect(page.getByTestId("generate-spec-button")).toHaveText(
/Generate Spec/
);
});
test("can trigger Generate Spec and shows success message", async ({ page }) => {
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("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();
@@ -248,7 +284,9 @@ test.describe("Generate Spec from Code", () => {
// 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();
await expect(
page.getByText("app_spec.txt created successfully")
).toBeVisible();
});
test("Generate Spec card displays description", async ({ page }) => {
@@ -258,8 +296,12 @@ test.describe("Generate Spec from Code", () => {
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("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();
@@ -270,19 +312,29 @@ test.describe("Generate Spec from Code", () => {
// 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();
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 }) => {
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("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();
@@ -297,7 +349,9 @@ test.describe("Generate Spec from Code", () => {
await expect(generateButton).toBeEnabled();
});
test("generated spec file reflects analyzed codebase structure", async ({ page }) => {
test("generated spec file reflects analyzed codebase structure", async ({
page,
}) => {
await page.goto("/");
// Step 1: Open project with code but no spec
@@ -305,7 +359,9 @@ test.describe("Generate Spec from Code", () => {
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("project-path-input")
.fill("/test/spec-verify/project");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
@@ -352,15 +408,21 @@ test.describe("Generate Spec from Code", () => {
});
test.describe("Generate Feature List from Code", () => {
test("shows Generate Feature List card after analysis is complete", async ({ page }) => {
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("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();
@@ -376,15 +438,21 @@ test.describe("Generate Feature List from Code", () => {
await expect(page.getByTestId("generate-feature-list-card")).toBeVisible();
});
test("shows Generate Feature List button after analysis", async ({ page }) => {
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("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();
@@ -397,19 +465,29 @@ test.describe("Generate Feature List from Code", () => {
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/);
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 }) => {
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("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();
@@ -424,9 +502,13 @@ test.describe("Generate Feature List from Code", () => {
// Step 2: Trigger 'Generate Feature List'
await page.getByTestId("generate-feature-list-button").click();
// Step 3: Verify 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();
// 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 }) => {
@@ -436,8 +518,12 @@ test.describe("Generate Feature List from Code", () => {
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("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();
@@ -447,19 +533,29 @@ test.describe("Generate Feature List from Code", () => {
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 feature_list.json from analysis")).toBeVisible();
await expect(page.getByText(/Automatically detect and generate a feature list/)).toBeVisible();
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 }) => {
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("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();
@@ -474,7 +570,9 @@ test.describe("Generate Feature List from Code", () => {
await expect(generateButton).toBeEnabled();
});
test("generated feature list contains features with passes: true", async ({ page }) => {
test("generated feature list contains features with passes: true", async ({
page,
}) => {
await page.goto("/");
// Step 1: Open project with implemented features
@@ -482,7 +580,9 @@ test.describe("Generate Feature List from Code", () => {
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("project-path-input")
.fill("/test/feature-verify/project");
await page.getByTestId("confirm-create-project").click();
await expect(page.getByTestId("board-view")).toBeVisible();
@@ -504,8 +604,10 @@ test.describe("Generate Feature List from Code", () => {
// Step 2: Trigger 'Generate Feature List'
await page.getByTestId("generate-feature-list-button").click();
// Step 3: Verify feature_list.json is created (success message appears)
await expect(page.getByTestId("feature-list-generated-success")).toBeVisible();
// 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
@@ -517,15 +619,21 @@ test.describe("Generate Feature List from Code", () => {
// the generation completed successfully (the success message is sufficient proof)
});
test("Generate Feature List can be triggered multiple times", async ({ page }) => {
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("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();
@@ -536,10 +644,14 @@ test.describe("Generate Feature List from Code", () => {
// Generate feature list first time
await page.getByTestId("generate-feature-list-button").click();
await expect(page.getByTestId("feature-list-generated-success")).toBeVisible();
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();
await expect(
page.getByTestId("feature-list-generated-success")
).toBeVisible();
});
});

View File

@@ -10,35 +10,22 @@ test.describe("Kanban Board", () => {
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,
}));
localStorage.setItem(
"automaker-storage",
JSON.stringify({
state: {
projects: [mockProject],
currentProject: mockProject,
currentView: "board",
sidebarOpen: true,
theme: "dark",
},
version: 0,
})
);
});
}
test("renders Kanban columns when project is open", async ({ page }) => {
await setupMockProject(page);
await page.goto("/");
// Should show the board view
await expect(page.getByTestId("board-view")).toBeVisible();
// Check all columns are visible
await expect(page.getByTestId("kanban-column-backlog")).toBeVisible();
await expect(page.getByTestId("kanban-column-planned")).toBeVisible();
await expect(page.getByTestId("kanban-column-in_progress")).toBeVisible();
await expect(page.getByTestId("kanban-column-review")).toBeVisible();
await expect(page.getByTestId("kanban-column-verified")).toBeVisible();
await expect(page.getByTestId("kanban-column-failed")).toBeVisible();
});
test("shows Add Feature button", async ({ page }) => {
await setupMockProject(page);
await page.goto("/");
@@ -71,7 +58,9 @@ test.describe("Kanban Board", () => {
// 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-description-input")
.fill("Test Feature Description");
await page.getByTestId("feature-step-0-input").fill("Step 1: First step");
// Submit the form
@@ -88,7 +77,9 @@ test.describe("Kanban Board", () => {
await expect(page.getByTestId("refresh-board")).toBeVisible();
});
test("loads cards from feature_list.json and displays them in correct columns", async ({ page }) => {
test("loads cards from .automaker/feature_list.json and displays them in correct columns", async ({
page,
}) => {
await setupMockProject(page);
await page.goto("/");
@@ -105,7 +96,9 @@ test.describe("Kanban Board", () => {
await expect(backlogColumn.getByText("Sample Feature")).toBeVisible();
});
test("features with passes:true appear in verified column", async ({ page }) => {
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("/");
@@ -116,11 +109,17 @@ test.describe("Kanban Board", () => {
// 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("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();
await expect(
page
.getByTestId("kanban-column-backlog")
.getByText("Verified Test Feature")
).toBeVisible();
});
test("can edit feature card details", async ({ page }) => {
@@ -131,19 +130,25 @@ test.describe("Kanban Board", () => {
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();
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-"]');
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");
await page
.getByTestId("edit-feature-description")
.fill("Updated Feature Description");
// Save the changes
await page.getByTestId("confirm-edit-feature").click();
@@ -163,15 +168,21 @@ test.describe("Kanban Board", () => {
await expect(page.getByTestId("board-view")).toBeVisible();
// Wait for features to load
await expect(page.getByTestId("kanban-column-backlog").getByText("Sample Feature")).toBeVisible();
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-"]');
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-description")).toHaveValue(
"Sample Feature"
);
await expect(page.getByTestId("edit-feature-category")).toHaveValue("Core");
});
@@ -189,7 +200,9 @@ test.describe("Kanban Board", () => {
await expect(backlogColumn.getByText("Sample Feature")).toBeVisible();
// Find the drag handle specifically
const dragHandle = backlogColumn.locator('[data-testid^="drag-handle-feature-0-"]');
const dragHandle = backlogColumn.locator(
'[data-testid^="drag-handle-feature-0-"]'
);
await expect(dragHandle).toBeVisible();
// Get drag handle and target positions
@@ -217,55 +230,9 @@ test.describe("Kanban Board", () => {
await expect(backlogColumn.getByText("Sample Feature")).not.toBeVisible();
});
test("drag and drop updates feature status and triggers file save", 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 plannedColumn = page.getByTestId("kanban-column-planned");
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 (Planned is adjacent to Backlog)
const handleBox = await dragHandle.boundingBox();
const targetBox = await plannedColumn.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 Planned column
await expect(plannedColumn.getByText("Sample Feature")).toBeVisible();
// Verify card is no longer in Backlog
await expect(backlogColumn.getByText("Sample Feature")).not.toBeVisible();
// The feature moving to Planned means the feature_list.json would be updated
// with the new status. Since status changed from backlog, passes would remain false
// This confirms the state update and file save workflow works.
const plannedCard = plannedColumn.locator('[data-testid^="kanban-card-feature-0-"]');
await expect(plannedCard).toBeVisible();
});
test("displays delete button (trash icon) on feature card", async ({ page }) => {
test("displays delete button (trash icon) on feature card", async ({
page,
}) => {
await setupMockProject(page);
await page.goto("/");
@@ -277,7 +244,9 @@ test.describe("Kanban Board", () => {
await expect(backlogColumn.getByText("Sample Feature")).toBeVisible();
// Find the delete button on the card
const deleteButton = backlogColumn.locator('[data-testid^="delete-feature-feature-0-"]');
const deleteButton = backlogColumn.locator(
'[data-testid^="delete-feature-feature-0-"]'
);
await expect(deleteButton).toBeVisible();
});
@@ -293,7 +262,9 @@ test.describe("Kanban Board", () => {
await expect(backlogColumn.getByText("Sample Feature")).toBeVisible();
// Find and click the delete button
const deleteButton = backlogColumn.locator('[data-testid^="delete-feature-feature-0-"]');
const deleteButton = backlogColumn.locator(
'[data-testid^="delete-feature-feature-0-"]'
);
await deleteButton.click();
// Verify the feature is removed from the board
@@ -310,7 +281,9 @@ test.describe("Kanban Board", () => {
// 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("feature-description-input")
.fill("Feature to Delete");
await page.getByTestId("confirm-add-feature").click();
// Wait for the new feature to appear in backlog
@@ -318,11 +291,15 @@ test.describe("Kanban Board", () => {
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();
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();
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

@@ -0,0 +1,104 @@
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,7 +1,9 @@
import { test, expect } from "@playwright/test";
test.describe("New Project Workflow", () => {
test("opens new project dialog when clicking Create Project", async ({ page }) => {
test("opens new project dialog when clicking Create Project", async ({
page,
}) => {
await page.goto("/");
// Click the New Project card
@@ -42,7 +44,9 @@ test.describe("New Project Workflow", () => {
// Enter project name
await page.getByTestId("project-name-input").fill("my-test-project");
await expect(page.getByTestId("project-name-input")).toHaveValue("my-test-project");
await expect(page.getByTestId("project-name-input")).toHaveValue(
"my-test-project"
);
});
test("can close dialog with cancel button", async ({ page }) => {
@@ -57,7 +61,9 @@ test.describe("New Project Workflow", () => {
await expect(page.getByTestId("new-project-dialog")).not.toBeVisible();
});
test("create button enables when name and path are entered", async ({ page }) => {
test("create button enables when name and path are entered", async ({
page,
}) => {
await page.goto("/");
// Open dialog
@@ -100,7 +106,9 @@ test.describe("New Project Workflow", () => {
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();
await expect(
page.getByTestId("board-view").getByText("test-new-project")
).toBeVisible();
// Kanban columns should be visible
await expect(page.getByText("Backlog")).toBeVisible();
@@ -108,7 +116,9 @@ test.describe("New Project Workflow", () => {
await expect(page.getByText("Verified")).toBeVisible();
});
test("created project appears in recent projects on welcome view", async ({ page }) => {
test("created project appears in recent projects on welcome view", async ({
page,
}) => {
await page.goto("/");
// Create a project
@@ -125,13 +135,21 @@ test.describe("New Project Workflow", () => {
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();
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 }) => {
test("clicking Open Project triggers directory selection", async ({
page,
}) => {
await page.goto("/");
// In web mode, clicking Open Project card will show a prompt dialog
@@ -139,7 +157,9 @@ test.describe("Open Project Workflow", () => {
await expect(page.getByTestId("open-project-card")).toBeVisible();
});
test("opens existing project and navigates to board view", async ({ page }) => {
test("opens existing project and navigates to board view", async ({
page,
}) => {
await page.goto("/");
// Mock the window.prompt response
@@ -154,10 +174,14 @@ test.describe("Open Project Workflow", () => {
await expect(page.getByTestId("board-view")).toBeVisible();
// Project name should be derived from path
await expect(page.getByTestId("board-view").getByText("existing-project")).toBeVisible();
await expect(
page.getByTestId("board-view").getByText("existing-project")
).toBeVisible();
});
test("opened project loads into dashboard with features", async ({ page }) => {
test("opened project loads into dashboard with features", async ({
page,
}) => {
await page.goto("/");
// Mock the window.prompt response
@@ -171,9 +195,11 @@ test.describe("Open Project Workflow", () => {
// Should show board view
await expect(page.getByTestId("board-view")).toBeVisible();
// Should have loaded features from the mock feature_list.json
// 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();
await expect(
page.getByTestId("kanban-column-backlog").getByText("Sample Feature")
).toBeVisible();
});
test("can click on recent project to reopen it", async ({ page }) => {
@@ -192,14 +218,20 @@ test.describe("Open Project Workflow", () => {
await page.goto("/");
// Wait for recent projects to appear
await expect(page.getByRole("heading", { name: "Recent Projects" })).toBeVisible();
await expect(
page.getByRole("heading", { name: "Recent Projects" })
).toBeVisible();
// Click on the recent project
const recentProjectCard = page.getByText("reopenable-project", { exact: true }).first();
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();
await expect(
page.getByTestId("board-view").getByText("reopenable-project")
).toBeVisible();
});
});