Files
storkit/frontend/src/App.test.tsx

159 lines
4.0 KiB
TypeScript
Raw Normal View History

import { 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";
vi.mock("./api/client", () => {
const api = {
getCurrentProject: vi.fn(),
getKnownProjects: vi.fn(),
getHomeDirectory: vi.fn(),
openProject: vi.fn(),
closeProject: vi.fn(),
forgetKnownProject: vi.fn(),
listDirectoryAbsolute: vi.fn(),
getOllamaModels: vi.fn(),
getAnthropicApiKeyExists: vi.fn(),
getAnthropicModels: vi.fn(),
getModelPreference: vi.fn(),
setModelPreference: vi.fn(),
cancelChat: vi.fn(),
setAnthropicApiKey: vi.fn(),
};
class ChatWebSocket {
connect() {}
close() {}
sendChat() {}
cancel() {}
}
return { api, ChatWebSocket };
});
vi.mock("./api/workflow", () => {
return {
workflowApi: {
getAcceptance: vi.fn().mockResolvedValue({
can_accept: false,
reasons: [],
warning: null,
summary: { total: 0, passed: 0, failed: 0 },
missing_categories: [],
}),
getReviewQueueAll: vi.fn().mockResolvedValue({ stories: [] }),
recordTests: vi.fn(),
ensureAcceptance: vi.fn(),
getReviewQueue: vi.fn(),
},
};
});
const mockedApi = vi.mocked(api);
describe("App", () => {
beforeEach(() => {
vi.resetModules();
vi.clearAllMocks();
mockedApi.getKnownProjects.mockResolvedValue([]);
mockedApi.getHomeDirectory.mockResolvedValue("/home/user");
mockedApi.listDirectoryAbsolute.mockResolvedValue([]);
mockedApi.getOllamaModels.mockResolvedValue([]);
mockedApi.getAnthropicApiKeyExists.mockResolvedValue(false);
mockedApi.getAnthropicModels.mockResolvedValue([]);
mockedApi.getModelPreference.mockResolvedValue(null);
});
async function renderApp() {
const { default: App } = await import("./App");
return render(<App />);
}
it("renders the selection screen when no project is open", async () => {
await renderApp();
await waitFor(() => {
expect(
screen.getByPlaceholderText(/\/path\/to\/project/i),
).toBeInTheDocument();
});
});
it("populates path input with home directory", async () => {
mockedApi.getHomeDirectory.mockResolvedValue("/Users/dave");
await renderApp();
await waitFor(() => {
const input = screen.getByPlaceholderText(
/\/path\/to\/project/i,
) as HTMLInputElement;
expect(input.value).toBe("/Users/dave/");
});
});
it("opens project and shows chat view", 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");
const openButton = screen.getByRole("button", { name: /open project/i });
await userEvent.click(openButton);
await waitFor(() => {
expect(mockedApi.openProject).toHaveBeenCalledWith(
"/home/user/myproject",
);
});
});
it("shows error when openProject fails", async () => {
mockedApi.openProject.mockRejectedValue(new Error("Path does not exist"));
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, "/bad/path");
const openButton = screen.getByRole("button", { name: /open project/i });
await userEvent.click(openButton);
await waitFor(() => {
expect(screen.getByText(/Path does not exist/)).toBeInTheDocument();
});
});
it("shows known projects list", async () => {
mockedApi.getKnownProjects.mockResolvedValue([
"/home/user/project1",
"/home/user/project2",
]);
await renderApp();
await waitFor(() => {
expect(screen.getByTitle("/home/user/project1")).toBeInTheDocument();
expect(screen.getByTitle("/home/user/project2")).toBeInTheDocument();
});
});
});