90 lines
3.8 KiB
TypeScript
90 lines
3.8 KiB
TypeScript
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
import { render, screen, waitFor } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { MigrationPopup } from "./MigrationPopup";
|
|
|
|
const { getMock, postMock, getMigration, setMigration } = vi.hoisted(() => ({
|
|
getMock: vi.fn(),
|
|
postMock: vi.fn(),
|
|
getMigration: vi.fn(),
|
|
setMigration: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("../lib/api-client", () => ({
|
|
apiClient: { GET: getMock, POST: postMock },
|
|
apiErrorMessage: (e: unknown, fb = "Request failed") =>
|
|
e instanceof Error ? e.message : ((e as { message?: string })?.message ?? fb),
|
|
}));
|
|
vi.mock("../lib/bridge", () => ({ aoBridge: { appState: { getMigration, setMigration } } }));
|
|
|
|
function renderPopup() {
|
|
const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
|
render(
|
|
<QueryClientProvider client={qc}>
|
|
<MigrationPopup />
|
|
</QueryClientProvider>,
|
|
);
|
|
return qc;
|
|
}
|
|
|
|
beforeEach(() => {
|
|
getMock.mockReset();
|
|
postMock.mockReset();
|
|
getMigration.mockReset();
|
|
setMigration.mockReset();
|
|
getMigration.mockResolvedValue({ status: "pending" });
|
|
getMock.mockResolvedValue({ data: { available: true, legacyRoot: "/home/u/.agent-orchestrator" }, error: undefined });
|
|
postMock.mockResolvedValue({ data: { report: { projectsImported: 2, projectsSkipped: 1 } }, error: undefined });
|
|
setMigration.mockResolvedValue(undefined);
|
|
});
|
|
|
|
describe("MigrationPopup", () => {
|
|
it("shows when a legacy install is available and the marker is pending", async () => {
|
|
renderPopup();
|
|
expect(await screen.findByText(/Import projects from your earlier AO/i)).toBeInTheDocument();
|
|
expect(screen.getByText("/home/u/.agent-orchestrator")).toBeInTheDocument();
|
|
});
|
|
|
|
it("renders nothing when the marker is declined", async () => {
|
|
getMigration.mockResolvedValue({ status: "declined" });
|
|
renderPopup();
|
|
await waitFor(() => expect(getMigration).toHaveBeenCalled());
|
|
expect(screen.queryByText(/Import projects from your earlier AO/i)).not.toBeInTheDocument();
|
|
expect(getMock).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("Proceed imports, marks completed, and retires", async () => {
|
|
renderPopup();
|
|
await screen.findByText(/Import projects from your earlier AO/i);
|
|
await userEvent.click(screen.getByRole("button", { name: "Proceed" }));
|
|
await waitFor(() => expect(postMock).toHaveBeenCalledWith("/api/v1/import"));
|
|
expect(setMigration).toHaveBeenCalledWith(expect.objectContaining({ status: "completed" }));
|
|
await waitFor(() => expect(screen.queryByText(/Import projects from your earlier AO/i)).not.toBeInTheDocument());
|
|
});
|
|
|
|
it("Don't Migrate records declined", async () => {
|
|
renderPopup();
|
|
await screen.findByText(/Import projects from your earlier AO/i);
|
|
await userEvent.click(screen.getByRole("button", { name: "Don't Migrate" }));
|
|
expect(setMigration).toHaveBeenCalledWith(expect.objectContaining({ status: "declined" }));
|
|
});
|
|
|
|
it("Skip dismisses without writing the marker", async () => {
|
|
renderPopup();
|
|
await screen.findByText(/Import projects from your earlier AO/i);
|
|
await userEvent.click(screen.getByRole("button", { name: "Skip" }));
|
|
expect(setMigration).not.toHaveBeenCalled();
|
|
expect(screen.queryByText(/Import projects from your earlier AO/i)).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("a failed import shows the lossless reassurance and marks failed", async () => {
|
|
postMock.mockResolvedValue({ data: undefined, error: { message: "disk full" } });
|
|
renderPopup();
|
|
await screen.findByText(/Import projects from your earlier AO/i);
|
|
await userEvent.click(screen.getByRole("button", { name: "Proceed" }));
|
|
expect(await screen.findByText(/nothing is ever deleted/i)).toBeInTheDocument();
|
|
expect(setMigration).toHaveBeenCalledWith(expect.objectContaining({ status: "failed", error: "disk full" }));
|
|
});
|
|
});
|