[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]>
208 lines
8.0 KiB
TypeScript
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("");
|
|
});
|
|
});
|