story-kit: merge 112_story_add_test_coverage_for_app_tsx

This commit is contained in:
Dave
2026-02-23 22:45:59 +00:00
parent fa70cb2ced
commit 6bf523d31e

View File

@@ -1,4 +1,4 @@
import { render, screen, waitFor } from "@testing-library/react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { api } from "./api/client";
@@ -159,4 +159,187 @@ describe("App", () => {
expect(screen.getByTitle("/home/user/project2")).toBeInTheDocument();
});
});
it("shows error when path input is empty", async () => {
await renderApp();
await waitFor(() => {
expect(
screen.getByPlaceholderText(/\/path\/to\/project/i),
).toBeInTheDocument();
});
const input = screen.getByPlaceholderText(
/\/path\/to\/project/i,
) as HTMLInputElement;
await userEvent.clear(input);
const openButton = screen.getByRole("button", { name: /open project/i });
await userEvent.click(openButton);
await waitFor(() => {
expect(
screen.getByText(/Please enter a project path/i),
).toBeInTheDocument();
});
});
it("calls forgetKnownProject and removes project from list", async () => {
mockedApi.getKnownProjects.mockResolvedValue(["/home/user/project1"]);
mockedApi.forgetKnownProject.mockResolvedValue(true);
await renderApp();
await waitFor(() => {
expect(screen.getByTitle("/home/user/project1")).toBeInTheDocument();
});
const forgetButton = screen.getByRole("button", {
name: /Forget project1/i,
});
await userEvent.click(forgetButton);
await waitFor(() => {
expect(mockedApi.forgetKnownProject).toHaveBeenCalledWith(
"/home/user/project1",
);
expect(
screen.queryByTitle("/home/user/project1"),
).not.toBeInTheDocument();
});
});
it("closes project and returns to selection screen", async () => {
mockedApi.openProject.mockResolvedValue("/home/user/myproject");
mockedApi.closeProject.mockResolvedValue(true);
await renderApp();
await waitFor(() => {
expect(
screen.getByPlaceholderText(/\/path\/to\/project/i),
).toBeInTheDocument();
});
const input = screen.getByPlaceholderText(
/\/path\/to\/project/i,
) as HTMLInputElement;
await userEvent.clear(input);
await userEvent.type(input, "/home/user/myproject");
const openButton = screen.getByRole("button", { name: /open project/i });
await userEvent.click(openButton);
await waitFor(() => {
expect(mockedApi.openProject).toHaveBeenCalledWith(
"/home/user/myproject",
);
});
// Chat view should appear with close button
const closeButton = await waitFor(() => screen.getByText("✕"));
await userEvent.click(closeButton);
await waitFor(() => {
expect(mockedApi.closeProject).toHaveBeenCalled();
expect(
screen.getByPlaceholderText(/\/path\/to\/project/i),
).toBeInTheDocument();
});
});
it("handles ArrowDown and ArrowUp keyboard navigation when suggestions are visible", async () => {
mockedApi.listDirectoryAbsolute.mockResolvedValue([
{ name: "projects", kind: "dir" },
{ name: "documents", kind: "dir" },
]);
await renderApp();
// Wait for suggestions to appear after debounce
await waitFor(
() => {
expect(screen.getByText(/projects\//)).toBeInTheDocument();
},
{ timeout: 2000 },
);
const input = screen.getByPlaceholderText(/\/path\/to\/project/i);
// ArrowDown with matchList present — moves selection forward
fireEvent.keyDown(input, { key: "ArrowDown" });
// ArrowUp with matchList present — moves selection backward
fireEvent.keyDown(input, { key: "ArrowUp" });
});
it("handles Tab keyboard navigation to accept suggestion", async () => {
mockedApi.listDirectoryAbsolute.mockResolvedValue([
{ name: "myrepo", kind: "dir" },
]);
await renderApp();
await waitFor(
() => {
expect(screen.getByText(/myrepo\//)).toBeInTheDocument();
},
{ timeout: 2000 },
);
const input = screen.getByPlaceholderText(/\/path\/to\/project/i);
// Tab with matchList present — accepts the selected match
fireEvent.keyDown(input, { key: "Tab" });
});
it("handles Escape key to close suggestions", async () => {
mockedApi.listDirectoryAbsolute.mockResolvedValue([
{ name: "workspace", kind: "dir" },
]);
await renderApp();
await waitFor(
() => {
expect(screen.getByText(/workspace\//)).toBeInTheDocument();
},
{ timeout: 2000 },
);
const input = screen.getByPlaceholderText(/\/path\/to\/project/i);
// Escape closes suggestions
fireEvent.keyDown(input, { key: "Escape" });
await waitFor(() => {
expect(screen.queryByText(/workspace\//)).not.toBeInTheDocument();
});
});
it("handles Enter key to trigger project open", async () => {
mockedApi.openProject.mockResolvedValue("/home/user/myproject");
await renderApp();
await waitFor(() => {
expect(
screen.getByPlaceholderText(/\/path\/to\/project/i),
).toBeInTheDocument();
});
const input = screen.getByPlaceholderText(
/\/path\/to\/project/i,
) as HTMLInputElement;
await userEvent.clear(input);
await userEvent.type(input, "/home/user/myproject");
fireEvent.keyDown(input, { key: "Enter" });
await waitFor(() => {
expect(mockedApi.openProject).toHaveBeenCalledWith(
"/home/user/myproject",
);
});
});
});