Files
CIAgent/apps/web/tests/add-company-wizard.test.tsx
sakshamandClaude Sonnet 5 6343ea19db Don't prefill notification email for the local-dev bypass account
[email protected] is a fixed, non-deliverable placeholder (no mail
relay serves that domain, and Mailpit was removed a while back) - prefilling
it was just noise the local dev would always need to clear. Real accounts
still get their own email prefilled as before.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-05 21:28:35 -04:00

208 lines
8.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,
},
];
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[] = [], isLocalhost = false) {
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 });
}
if (path === "/api/v1/system/status") {
return Promise.resolve({
ok: true,
status: 200,
json: async () => ({
app_env: "development",
auth_mode: "local",
llm_provider: "mock",
search_provider: "mock",
sms_enabled: false,
sms_provider: "twilio",
ninjapear_configured: false,
ninjapear_credit_balance: null,
ninjapear_estimated_credits_per_company: null,
is_localhost: isLocalhost,
components: [],
}),
});
}
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]");
});
it("leaves the notification email blank for the local-dev bypass account", async () => {
// [email protected] isn't a real, deliverable address (no mail relay
// serves that domain) - prefilling it would just be noise to clear.
vi.stubGlobal("fetch", mockFetchImplementation([], true));
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;
// Give any async prefill effect a chance to run before asserting it didn't.
await new Promise((resolve) => setTimeout(resolve, 50));
expect(email.value).toBe("");
});
});