Was always blank by default. Now prefilled from useCurrentUser() once it loads (defaultValues can't do this since the query resolves after mount), gated on the field not being dirty yet so it never clobbers something the user already typed - still fully editable either way. Co-Authored-By: Claude Sonnet 5 <[email protected]>
167 lines
6.3 KiB
TypeScript
167 lines
6.3 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,
|
|
},
|
|
];
|
|
|
|
const CURRENT_USER = {
|
|
id: "user-1",
|
|
email: "[email protected]",
|
|
display_name: "Team Person",
|
|
timezone: "America/New_York",
|
|
is_active: true,
|
|
is_admin: false,
|
|
auth_mode: "local",
|
|
};
|
|
|
|
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 });
|
|
}
|
|
if (path === "/api/v1/auth/me") {
|
|
return Promise.resolve({ ok: true, status: 200, json: async () => CURRENT_USER });
|
|
}
|
|
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();
|
|
});
|
|
|
|
it("pre-fills the notification email with the account's own email, still 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();
|
|
});
|
|
|
|
await user.click(screen.getByRole("button", { name: /^continue$/i })); // Review -> Schedule
|
|
await user.click(screen.getByRole("button", { name: /^continue$/i })); // Schedule -> Notifications
|
|
|
|
const email = (await screen.findByLabelText("Notification email")) as HTMLInputElement;
|
|
await waitFor(() => expect(email.value).toBe("[email protected]"));
|
|
|
|
// Still editable - prefilling must not lock the field.
|
|
await user.clear(email);
|
|
await user.type(email, "[email protected]");
|
|
expect(email.value).toBe("[email protected]");
|
|
});
|
|
});
|