FastAPI + Celery + Next.js + Postgres/Redis app with company monitoring, source collection, LLM-based change analysis, enrichment, and account security (Turnstile, escalating lockout, email verification).
132 lines
5.0 KiB
TypeScript
132 lines
5.0 KiB
TypeScript
import { screen, waitFor } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import AddCompanyWizardPage from "@/app/(app)/companies/new/page";
|
|
import { renderWithQueryClient } from "./test-utils";
|
|
|
|
vi.mock("next/navigation", () => ({
|
|
useRouter: () => ({ push: vi.fn(), replace: vi.fn() }),
|
|
useSearchParams: () => new URLSearchParams(),
|
|
}));
|
|
|
|
const DISCOVERED_PROFILE = {
|
|
name: "Acme Widgets",
|
|
official_website: "https://acmewidgets.com",
|
|
description: "Acme Widgets designs and sells handcrafted mechanical widgets.",
|
|
monitoring_focus: null,
|
|
industry: null,
|
|
country: null,
|
|
region: null,
|
|
headquarters: "Austin, Texas",
|
|
aliases: ["Acme Industries"],
|
|
competitors: [],
|
|
public_identifiers: {},
|
|
potential_sources: [
|
|
{ source_type: "website", name: "Acme Widgets - Website", base_url: "https://acmewidgets.com" },
|
|
],
|
|
sources_consulted: ["https://acmewidgets.com"],
|
|
};
|
|
|
|
const EXISTING_COMPANIES = [
|
|
{
|
|
id: "existing-1",
|
|
name: "Acme Widgets",
|
|
slug: "acme-widgets",
|
|
official_website: null,
|
|
description: null,
|
|
monitoring_focus: null,
|
|
industry: null,
|
|
country: null,
|
|
region: null,
|
|
headquarters: null,
|
|
public_identifiers: {},
|
|
status: "active",
|
|
created_at: "2026-01-01T00:00:00Z",
|
|
updated_at: "2026-01-01T00:00:00Z",
|
|
aliases: [],
|
|
competitors: [],
|
|
monitor_configuration: null,
|
|
report_count: 0,
|
|
unresolved_alert_count: 0,
|
|
},
|
|
];
|
|
|
|
function mockFetchImplementation(existingCompanies: unknown[] = []) {
|
|
return vi.fn().mockImplementation((url: string, init?: RequestInit) => {
|
|
const path = url.replace("http://localhost:8000", "");
|
|
if (path === "/api/v1/companies/discover" && init?.method === "POST") {
|
|
return Promise.resolve({ ok: true, status: 200, json: async () => DISCOVERED_PROFILE });
|
|
}
|
|
if (path === "/api/v1/companies" && (!init?.method || init.method === "GET")) {
|
|
return Promise.resolve({ ok: true, status: 200, json: async () => existingCompanies });
|
|
}
|
|
return Promise.resolve({ ok: false, status: 404, json: async () => ({ detail: "not found" }) });
|
|
});
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.stubGlobal("fetch", mockFetchImplementation());
|
|
});
|
|
|
|
describe("AddCompanyWizardPage", () => {
|
|
it("only requires a company name on the Discover step", async () => {
|
|
const user = userEvent.setup();
|
|
renderWithQueryClient(<AddCompanyWizardPage />);
|
|
|
|
await user.click(screen.getByRole("button", { name: /discover company/i }));
|
|
|
|
expect(await screen.findByText(/company name is required/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it("discovers a profile and pre-fills the Review step, editable", async () => {
|
|
const user = userEvent.setup();
|
|
renderWithQueryClient(<AddCompanyWizardPage />);
|
|
|
|
await user.type(screen.getByLabelText("Company name"), "Acme Widgets");
|
|
await user.click(screen.getByRole("button", { name: /discover company/i }));
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText(/here's what we found for/i)).toBeInTheDocument();
|
|
});
|
|
|
|
const website = screen.getByLabelText("Official website") as HTMLInputElement;
|
|
expect(website.value).toBe("https://acmewidgets.com");
|
|
const headquarters = screen.getByLabelText("Headquarters") as HTMLInputElement;
|
|
expect(headquarters.value).toBe("Austin, Texas");
|
|
const description = screen.getByLabelText("Description") as HTMLTextAreaElement;
|
|
expect(description.value).toBe(
|
|
"Acme Widgets designs and sells handcrafted mechanical widgets.",
|
|
);
|
|
const aliases = screen.getByLabelText(/known aliases/i) as HTMLInputElement;
|
|
expect(aliases.value).toBe("Acme Industries");
|
|
|
|
expect(screen.getByText(/we'll start monitoring 1 source/i)).toBeInTheDocument();
|
|
|
|
// Still editable - the whole point of the review step.
|
|
await user.clear(headquarters);
|
|
await user.type(headquarters, "Denver, Colorado");
|
|
expect(headquarters.value).toBe("Denver, Colorado");
|
|
});
|
|
|
|
it("warns about a likely duplicate before discovering, and proceeds after Continue anyway", async () => {
|
|
vi.stubGlobal("fetch", mockFetchImplementation(EXISTING_COMPANIES));
|
|
const user = userEvent.setup();
|
|
renderWithQueryClient(<AddCompanyWizardPage />);
|
|
|
|
await user.type(screen.getByLabelText("Company name"), "Acme Widgets");
|
|
await user.click(screen.getByRole("button", { name: /discover company/i }));
|
|
|
|
expect(await screen.findByText(/you might already be monitoring/i)).toBeInTheDocument();
|
|
// Discovery must not have fired yet - still on the Discover step.
|
|
expect(screen.queryByText(/here's what we found for/i)).not.toBeInTheDocument();
|
|
|
|
await user.click(screen.getByRole("button", { name: /continue anyway/i }));
|
|
await user.click(screen.getByRole("button", { name: /discover company/i }));
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText(/here's what we found for/i)).toBeInTheDocument();
|
|
});
|
|
expect(screen.queryByText(/you might already be monitoring/i)).not.toBeInTheDocument();
|
|
});
|
|
});
|