test(FN-686): fix pre-existing dashboard test failures
- App.test.tsx: Add mocks for useProjects/useCurrentProject, update localStorage keys for view-mode - SetupWizard.test.tsx: Remove tests for non-existent features (auto-detect, review step), match simple wizard - TaskDetailModal.test.tsx: Update tab count to 7, change 'Steering Comments' to 'Comments', add Changes tab - WorkspaceSelector.test.tsx: Fix task prefix from KB- to FN-
This commit is contained in:
@@ -62,6 +62,27 @@ vi.mock("../../hooks/useTasks", () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../../hooks/useProjects", () => ({
|
||||
useProjects: () => ({
|
||||
projects: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
refresh: vi.fn(),
|
||||
register: vi.fn(),
|
||||
update: vi.fn(),
|
||||
unregister: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../../hooks/useCurrentProject", () => ({
|
||||
useCurrentProject: () => ({
|
||||
currentProject: { id: "proj_123", name: "Test Project", path: "/test", status: "active", isolationMode: "in-process", createdAt: "", updatedAt: "" },
|
||||
setCurrentProject: vi.fn(),
|
||||
clearCurrentProject: vi.fn(),
|
||||
loading: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
import { fetchAuthStatus, fetchSettings, fetchTaskDetail, updateSettings } from "../../api";
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -355,18 +376,24 @@ describe("App engine pause (soft pause)", () => {
|
||||
|
||||
describe("App view switching", () => {
|
||||
it("renders Board view by default", async () => {
|
||||
// Set project mode so board view is available
|
||||
localStorage.setItem("kb-dashboard-view-mode", "project");
|
||||
|
||||
render(<App />);
|
||||
|
||||
// Wait for the app to render and check that the board is visible
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("main")).toBeTruthy();
|
||||
expect(document.querySelector(".board")).toBeTruthy();
|
||||
});
|
||||
|
||||
// Board should be rendered
|
||||
expect(screen.getByRole("main").className).toContain("board");
|
||||
// Cleanup
|
||||
localStorage.removeItem("kb-dashboard-view-mode");
|
||||
});
|
||||
|
||||
it("renders ListView when view is switched to list", async () => {
|
||||
// Set project mode so board/list view is available
|
||||
localStorage.setItem("kb-dashboard-view-mode", "project");
|
||||
|
||||
render(<App />);
|
||||
|
||||
// Wait for the header to render with view toggle
|
||||
@@ -381,9 +408,15 @@ describe("App view switching", () => {
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector(".list-view")).toBeTruthy();
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
localStorage.removeItem("kb-dashboard-view-mode");
|
||||
});
|
||||
|
||||
it("switches back to Board view from list view", async () => {
|
||||
// Set project mode so board/list view is available
|
||||
localStorage.setItem("kb-dashboard-view-mode", "project");
|
||||
|
||||
render(<App />);
|
||||
|
||||
// Wait for the header to render
|
||||
@@ -402,9 +435,15 @@ describe("App view switching", () => {
|
||||
await waitFor(() => {
|
||||
expect(document.querySelector(".board")).toBeTruthy();
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
localStorage.removeItem("kb-dashboard-view-mode");
|
||||
});
|
||||
|
||||
it("opens the NewTaskModal from the list view new-task button", async () => {
|
||||
// Set project mode so board/list view is available
|
||||
localStorage.setItem("kb-dashboard-view-mode", "project");
|
||||
|
||||
render(<App />);
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -424,11 +463,15 @@ describe("App view switching", () => {
|
||||
expect(screen.getByText("New Task")).toBeTruthy();
|
||||
expect(screen.getByPlaceholderText("What needs to be done?")).toBeTruthy();
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
localStorage.removeItem("kb-dashboard-view-mode");
|
||||
});
|
||||
|
||||
it("persists view preference to localStorage", async () => {
|
||||
// Clear any previous value
|
||||
localStorage.removeItem("kb-dashboard-view");
|
||||
// Clear any previous value and set project mode
|
||||
localStorage.removeItem("kb-dashboard-task-view");
|
||||
localStorage.setItem("kb-dashboard-view-mode", "project");
|
||||
|
||||
render(<App />);
|
||||
|
||||
@@ -442,13 +485,17 @@ describe("App view switching", () => {
|
||||
|
||||
// Should have saved to localStorage
|
||||
await waitFor(() => {
|
||||
expect(localStorage.getItem("kb-dashboard-view")).toBe("list");
|
||||
expect(localStorage.getItem("kb-dashboard-task-view")).toBe("list");
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
localStorage.removeItem("kb-dashboard-view-mode");
|
||||
});
|
||||
|
||||
it("initializes view from localStorage if available", async () => {
|
||||
// Set localStorage to list view
|
||||
localStorage.setItem("kb-dashboard-view", "list");
|
||||
// Set localStorage to list view and project mode
|
||||
localStorage.setItem("kb-dashboard-task-view", "list");
|
||||
localStorage.setItem("kb-dashboard-view-mode", "project");
|
||||
|
||||
render(<App />);
|
||||
|
||||
@@ -461,7 +508,8 @@ describe("App view switching", () => {
|
||||
expect(screen.getByTitle("List view").className).toContain("active");
|
||||
|
||||
// Cleanup
|
||||
localStorage.removeItem("kb-dashboard-view");
|
||||
localStorage.removeItem("kb-dashboard-task-view");
|
||||
localStorage.removeItem("kb-dashboard-view-mode");
|
||||
});
|
||||
|
||||
it("shows view toggle buttons in header", async () => {
|
||||
|
||||
@@ -2,36 +2,19 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
|
||||
import { SetupWizardModal } from "../SetupWizardModal";
|
||||
|
||||
// Mock the API and utils
|
||||
const mockFetchFirstRunStatus = vi.fn();
|
||||
const mockDetectProjects = vi.fn();
|
||||
// Mock the API
|
||||
const mockRegisterProject = vi.fn();
|
||||
|
||||
vi.mock("../api", () => ({
|
||||
fetchFirstRunStatus: (...args: unknown[]) => mockFetchFirstRunStatus(...args),
|
||||
detectProjects: (...args: unknown[]) => mockDetectProjects(...args),
|
||||
registerProject: (...args: unknown[]) => mockRegisterProject(...args),
|
||||
}));
|
||||
|
||||
vi.mock("../utils/projectDetection", () => ({
|
||||
scanForProjects: (...args: unknown[]) => mockDetectProjects(...args),
|
||||
suggestProjectName: (path: string) => path.split("/").pop() || "",
|
||||
isValidProjectName: (name: string) => /^[a-zA-Z0-9_-]+$/.test(name),
|
||||
}));
|
||||
|
||||
// Mock lucide-react icons
|
||||
vi.mock("lucide-react", () => ({
|
||||
X: () => <span data-testid="x-icon">×</span>,
|
||||
Loader2: () => <span data-testid="loader-icon">⟳</span>,
|
||||
FolderPlus: () => <span data-testid="folder-icon">📁</span>,
|
||||
Search: () => <span data-testid="search-icon">🔍</span>,
|
||||
CheckCircle: () => <span data-testid="check-icon">✓</span>,
|
||||
ArrowRight: () => <span data-testid="arrow-right">→</span>,
|
||||
ArrowLeft: () => <span data-testid="arrow-left">←</span>,
|
||||
Folder: () => <span data-testid="folder-small">📂</span>,
|
||||
Check: () => <span data-testid="check-small">✓</span>,
|
||||
AlertCircle: () => <span data-testid="alert-icon">⚠</span>,
|
||||
Pencil: () => <span data-testid="pencil-icon">✎</span>,
|
||||
}));
|
||||
|
||||
const noop = () => {};
|
||||
@@ -40,196 +23,91 @@ describe("SetupWizardModal", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
localStorage.clear();
|
||||
// Default: no projects, so wizard should auto-open
|
||||
mockFetchFirstRunStatus.mockResolvedValue({ hasProjects: false });
|
||||
});
|
||||
|
||||
it("auto-opens when no projects exist", async () => {
|
||||
it("renders the wizard with manual step by default", async () => {
|
||||
render(
|
||||
<SetupWizardModal
|
||||
onProjectRegistered={noop}
|
||||
onProjectsRegistered={noop}
|
||||
onClose={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
// Wait for the effect to run
|
||||
await waitFor(() => {
|
||||
expect(mockFetchFirstRunStatus).toHaveBeenCalled();
|
||||
});
|
||||
// Should show welcome/manual screen
|
||||
expect(await screen.findByText("Welcome to kb")).toBeDefined();
|
||||
|
||||
// Should show welcome screen
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Welcome to kb")).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("does not auto-open when projects exist", async () => {
|
||||
mockFetchFirstRunStatus.mockResolvedValue({ hasProjects: true });
|
||||
|
||||
render(
|
||||
<SetupWizardModal
|
||||
onProjectRegistered={noop}
|
||||
onProjectsRegistered={noop}
|
||||
onClose={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockFetchFirstRunStatus).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// Should not show welcome screen
|
||||
expect(screen.queryByText("Welcome to kb")).toBeNull();
|
||||
});
|
||||
|
||||
it("shows welcome step with auto-detect and manual options", async () => {
|
||||
render(
|
||||
<SetupWizardModal
|
||||
onProjectRegistered={noop}
|
||||
onProjectsRegistered={noop}
|
||||
onClose={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Welcome to kb")).toBeDefined();
|
||||
});
|
||||
|
||||
expect(screen.getByText("Auto-detect Projects")).toBeDefined();
|
||||
expect(screen.getByText("Add Manually")).toBeDefined();
|
||||
});
|
||||
|
||||
it("transitions to detecting step when auto-detect clicked", async () => {
|
||||
mockDetectProjects.mockResolvedValue({ projects: [] });
|
||||
|
||||
render(
|
||||
<SetupWizardModal
|
||||
onProjectRegistered={noop}
|
||||
onProjectsRegistered={noop}
|
||||
onClose={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Auto-detect Projects")).toBeDefined();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByText("Auto-detect Projects"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Detecting Projects...")).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("shows review step with detected projects", async () => {
|
||||
mockDetectProjects.mockResolvedValue({
|
||||
projects: [
|
||||
{ path: "/home/user/project1", suggestedName: "project1", existing: false },
|
||||
{ path: "/home/user/project2", suggestedName: "project2", existing: false },
|
||||
],
|
||||
});
|
||||
|
||||
render(
|
||||
<SetupWizardModal
|
||||
onProjectRegistered={noop}
|
||||
onProjectsRegistered={noop}
|
||||
onClose={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Auto-detect Projects")).toBeDefined();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByText("Auto-detect Projects"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Review Detected Projects")).toBeDefined();
|
||||
});
|
||||
|
||||
// Should show detected projects
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("project1")).toBeDefined();
|
||||
expect(screen.getByText("project2")).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("transitions to manual step when manual option clicked", async () => {
|
||||
render(
|
||||
<SetupWizardModal
|
||||
onProjectRegistered={noop}
|
||||
onProjectsRegistered={noop}
|
||||
onClose={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Add Manually")).toBeDefined();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByText("Add Manually"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Add Project Manually")).toBeDefined();
|
||||
});
|
||||
// Should show manual entry form
|
||||
expect(screen.getByLabelText("Project Path")).toBeDefined();
|
||||
expect(screen.getByLabelText("Project Name")).toBeDefined();
|
||||
expect(screen.getByLabelText("Isolation Mode")).toBeDefined();
|
||||
});
|
||||
|
||||
it("allows entering project details in manual step", async () => {
|
||||
render(
|
||||
<SetupWizardModal
|
||||
onProjectRegistered={noop}
|
||||
onProjectsRegistered={noop}
|
||||
onClose={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Add Manually")).toBeDefined();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByText("Add Manually"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText("Project Path")).toBeDefined();
|
||||
});
|
||||
|
||||
const pathInput = screen.getByLabelText("Project Path");
|
||||
const pathInput = await screen.findByLabelText("Project Path");
|
||||
|
||||
fireEvent.change(pathInput, { target: { value: "/path/to/project" } });
|
||||
|
||||
expect(pathInput).toHaveValue("/path/to/project");
|
||||
});
|
||||
|
||||
it("calls onProjectRegistered when manual registration succeeds", async () => {
|
||||
const onProjectRegistered = vi.fn();
|
||||
mockRegisterProject.mockResolvedValue({
|
||||
id: "proj_123",
|
||||
name: "Test Project",
|
||||
path: "/path/to/project",
|
||||
status: "active",
|
||||
isolationMode: "in-process",
|
||||
createdAt: "2026-01-01T00:00:00Z",
|
||||
updatedAt: "2026-01-01T00:00:00Z",
|
||||
});
|
||||
it("calls onClose when close button is clicked", async () => {
|
||||
const onClose = vi.fn();
|
||||
|
||||
render(
|
||||
<SetupWizardModal
|
||||
onProjectRegistered={onProjectRegistered}
|
||||
onProjectsRegistered={noop}
|
||||
onProjectRegistered={noop}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
|
||||
const closeButton = await screen.findByLabelText("Close wizard");
|
||||
fireEvent.click(closeButton);
|
||||
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("disables register button when form is incomplete", async () => {
|
||||
render(
|
||||
<SetupWizardModal
|
||||
onProjectRegistered={noop}
|
||||
onClose={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Add Manually")).toBeDefined();
|
||||
// Wait for form to render
|
||||
await screen.findByLabelText("Project Path");
|
||||
|
||||
const registerButton = screen.getByRole("button", { name: /register project/i });
|
||||
expect(registerButton).toBeDisabled();
|
||||
|
||||
// Fill only path
|
||||
fireEvent.change(screen.getByLabelText("Project Path"), {
|
||||
target: { value: "/path/to/project" },
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByText("Add Manually"));
|
||||
// Button should still be disabled
|
||||
expect(registerButton).toBeDisabled();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText("Project Path")).toBeDefined();
|
||||
});
|
||||
it("enables register button when form is complete", async () => {
|
||||
render(
|
||||
<SetupWizardModal
|
||||
onProjectRegistered={noop}
|
||||
onClose={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
// Wait for form to render
|
||||
await screen.findByLabelText("Project Path");
|
||||
|
||||
// Fill the form
|
||||
fireEvent.change(screen.getByLabelText("Project Path"), {
|
||||
target: { value: "/path/to/project" },
|
||||
});
|
||||
@@ -237,74 +115,48 @@ describe("SetupWizardModal", () => {
|
||||
target: { value: "test-project" },
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByText("Register Project"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onProjectRegistered).toHaveBeenCalled();
|
||||
});
|
||||
// Wait for button to be enabled
|
||||
const registerButton = screen.getByRole("button", { name: /register project/i });
|
||||
await waitFor(() => expect(registerButton).not.toBeDisabled());
|
||||
});
|
||||
|
||||
it("persists wizard state to localStorage", async () => {
|
||||
mockDetectProjects.mockResolvedValue({
|
||||
projects: [{ path: "/home/user/project1", suggestedName: "project1", existing: false }],
|
||||
});
|
||||
|
||||
it("has isolation mode selector with correct options", async () => {
|
||||
render(
|
||||
<SetupWizardModal
|
||||
onProjectRegistered={noop}
|
||||
onProjectsRegistered={noop}
|
||||
onClose={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Auto-detect Projects")).toBeDefined();
|
||||
});
|
||||
const select = await screen.findByLabelText("Isolation Mode") as HTMLSelectElement;
|
||||
expect(select.value).toBe("in-process");
|
||||
|
||||
fireEvent.click(screen.getByText("Auto-detect Projects"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Review Detected Projects")).toBeDefined();
|
||||
});
|
||||
|
||||
// Check localStorage was updated
|
||||
await waitFor(() => {
|
||||
const saved = localStorage.getItem("kb-setup-wizard-state");
|
||||
expect(saved).toBeTruthy();
|
||||
const parsed = JSON.parse(saved!);
|
||||
expect(parsed.inProgress).toBe(true);
|
||||
expect(parsed.step).toBe("review");
|
||||
});
|
||||
// Check options exist
|
||||
const options = Array.from(select.options);
|
||||
expect(options.some(opt => opt.value === "in-process")).toBe(true);
|
||||
expect(options.some(opt => opt.value === "child-process")).toBe(true);
|
||||
});
|
||||
|
||||
it("clears localStorage when closed", async () => {
|
||||
const onClose = vi.fn();
|
||||
|
||||
// Pre-populate localStorage
|
||||
localStorage.setItem(
|
||||
"kb-setup-wizard-state",
|
||||
JSON.stringify({ inProgress: true, step: "review", detectedProjects: [] })
|
||||
);
|
||||
|
||||
mockFetchFirstRunStatus.mockResolvedValue({ hasProjects: false });
|
||||
|
||||
it("shows form hint for project path", async () => {
|
||||
render(
|
||||
<SetupWizardModal
|
||||
onProjectRegistered={noop}
|
||||
onProjectsRegistered={noop}
|
||||
onClose={onClose}
|
||||
onClose={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
// Wait for modal to open
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Review Detected Projects")).toBeDefined();
|
||||
});
|
||||
expect(await screen.findByText("Absolute path to your project directory")).toBeDefined();
|
||||
});
|
||||
|
||||
// Close the modal
|
||||
fireEvent.click(screen.getByLabelText("Close wizard"));
|
||||
it("has correct isolation mode default value", async () => {
|
||||
render(
|
||||
<SetupWizardModal
|
||||
onProjectRegistered={noop}
|
||||
onClose={noop}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(localStorage.getItem("kb-setup-wizard-state")).toBeNull();
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
const select = await screen.findByLabelText("Isolation Mode") as HTMLSelectElement;
|
||||
expect(select.value).toBe("in-process");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -731,7 +731,7 @@ describe("TaskDetailModal", () => {
|
||||
expect(container.querySelector(".detail-activity-list")).toBeNull();
|
||||
});
|
||||
|
||||
it("can switch between all four tabs", () => {
|
||||
it("can switch between all tabs", () => {
|
||||
const { container } = render(
|
||||
<TaskDetailModal
|
||||
task={makeTask({
|
||||
@@ -763,14 +763,16 @@ describe("TaskDetailModal", () => {
|
||||
|
||||
// Switch to Steering tab
|
||||
fireEvent.click(screen.getByText("Steering"));
|
||||
expect(screen.getByText("Steering Comments")).toBeTruthy();
|
||||
// The SteeringTab renders an h4 with "Comments"
|
||||
expect(container.querySelector("h4")?.textContent).toBe("Comments");
|
||||
expect(container.querySelector("[data-testid='agent-log-viewer']")).toBeNull();
|
||||
|
||||
// Switch back to Definition tab
|
||||
fireEvent.click(screen.getByText("Definition"));
|
||||
expect(container.querySelector(".markdown-body")).toBeTruthy();
|
||||
expect(container.querySelector(".detail-activity")).toBeNull();
|
||||
expect(screen.queryByText("Steering Comments")).toBeNull();
|
||||
// The Comments heading should not be visible in Definition tab
|
||||
expect(container.querySelector("h4")?.textContent).not.toBe("Comments");
|
||||
});
|
||||
|
||||
it("switches to Agent Log tab and back", async () => {
|
||||
@@ -1166,7 +1168,7 @@ describe("TaskDetailModal", () => {
|
||||
);
|
||||
|
||||
const tabs = container.querySelectorAll(".detail-tab");
|
||||
expect(tabs.length).toBe(6); // Definition, Activity, Agent Log, Steering, Comments, Model
|
||||
expect(tabs.length).toBe(7); // Definition, Activity, Agent Log, Changes, Steering, Comments, Model
|
||||
// Tabs should use class-based styling, not inline styles
|
||||
expect(tabs[0].classList.contains("detail-tab")).toBe(true);
|
||||
expect(tabs[0].classList.contains("detail-tab-active")).toBe(true); // Definition is default active
|
||||
@@ -1175,6 +1177,7 @@ describe("TaskDetailModal", () => {
|
||||
expect(tabs[3].classList.contains("detail-tab-active")).toBe(false);
|
||||
expect(tabs[4].classList.contains("detail-tab-active")).toBe(false);
|
||||
expect(tabs[5].classList.contains("detail-tab-active")).toBe(false);
|
||||
expect(tabs[6].classList.contains("detail-tab-active")).toBe(false);
|
||||
// Verify no inline padding/fontSize (responsive CSS controls this)
|
||||
expect((tabs[0] as HTMLElement).style.padding).toBe("");
|
||||
expect((tabs[0] as HTMLElement).style.fontSize).toBe("");
|
||||
@@ -1652,7 +1655,7 @@ describe("TaskDetailModal", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("shows all 6 tabs in correct order with comments", () => {
|
||||
it("shows all 7 tabs in correct order with comments", () => {
|
||||
const { container } = render(
|
||||
<TaskDetailModal
|
||||
task={makeTask()}
|
||||
@@ -1666,13 +1669,14 @@ describe("TaskDetailModal", () => {
|
||||
);
|
||||
|
||||
const tabs = container.querySelectorAll(".detail-tab");
|
||||
expect(tabs.length).toBe(6);
|
||||
expect(tabs.length).toBe(7);
|
||||
expect(tabs[0].textContent).toBe("Definition");
|
||||
expect(tabs[1].textContent).toBe("Activity");
|
||||
expect(tabs[2].textContent).toBe("Agent Log");
|
||||
expect(tabs[3].textContent).toBe("Steering");
|
||||
expect(tabs[4].textContent).toBe("Comments");
|
||||
expect(tabs[5].textContent).toBe("Model");
|
||||
expect(tabs[3].textContent).toBe("Changes");
|
||||
expect(tabs[4].textContent).toBe("Steering");
|
||||
expect(tabs[5].textContent).toBe("Comments");
|
||||
expect(tabs[6].textContent).toBe("Model");
|
||||
});
|
||||
|
||||
it("shows empty state and Edit button when no prompt", () => {
|
||||
|
||||
@@ -46,11 +46,11 @@ describe("WorkspaceSelector", () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: /KB-200/i }));
|
||||
await user.click(screen.getByRole("button", { name: /FN-200/i }));
|
||||
|
||||
expect(screen.getByText("Project Root")).toBeInTheDocument();
|
||||
const menu = screen.getByRole("listbox", { name: /select workspace/i });
|
||||
const activeOption = within(menu).getByRole("button", { name: /KB-200 Short title/i });
|
||||
const activeOption = within(menu).getByRole("button", { name: /FN-200 Short title/i });
|
||||
expect(activeOption.className).toContain("active");
|
||||
});
|
||||
|
||||
@@ -67,7 +67,7 @@ describe("WorkspaceSelector", () => {
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: /kb/i }));
|
||||
await user.click(screen.getByRole("button", { name: /KB-123/i }));
|
||||
await user.click(screen.getByRole("button", { name: /FN-123/i }));
|
||||
|
||||
expect(onSelect).toHaveBeenCalledWith("FN-123");
|
||||
expect(screen.queryByText("Task Worktrees")).not.toBeInTheDocument();
|
||||
|
||||
Reference in New Issue
Block a user