diff --git a/apps/web/app/(app)/companies/new/page.tsx b/apps/web/app/(app)/companies/new/page.tsx index 4e76868..def07f3 100644 --- a/apps/web/app/(app)/companies/new/page.tsx +++ b/apps/web/app/(app)/companies/new/page.tsx @@ -14,6 +14,7 @@ import { useCompanies, useCreateCompany } from "@/hooks/use-companies"; import { useCreateNotificationDestination } from "@/hooks/use-notification-destinations"; import { useDiscoverCompany } from "@/hooks/use-discovery"; import { authErrorMessage, useCurrentUser, useSystemStatus } from "@/hooks/use-auth"; +import { isLocalConvenience } from "@/lib/auth"; import { FREQUENCY_LABELS, MONITORING_FREQUENCIES, @@ -150,12 +151,17 @@ function AddCompanyWizard() { // Prefill with the account's own email once it loads - `defaultValues` // can't do this since useCurrentUser resolves after the form has already // mounted. Only sets it while the field is still untouched, so it never - // clobbers something the user already typed. + // clobbers something the user already typed. Skipped for the local-dev + // bypass account: its email is a fixed, non-deliverable placeholder + // (local@ci-agent.local - no mail relay actually serves that domain), + // not something worth prefilling - local dev just types whatever real + // address they want to test with. + const isLocalDev = systemStatus ? isLocalConvenience(systemStatus) : false; useEffect(() => { - if (currentUser?.email && !dirtyFields.notificationEmail) { + if (currentUser?.email && !isLocalDev && !dirtyFields.notificationEmail) { setValue("notificationEmail", currentUser.email); } - }, [currentUser?.email, dirtyFields.notificationEmail, setValue]); + }, [currentUser?.email, isLocalDev, dirtyFields.notificationEmail, setValue]); const frequencyType = watch("frequencyType"); diff --git a/apps/web/tests/add-company-wizard.test.tsx b/apps/web/tests/add-company-wizard.test.tsx index dee7e79..5a577f5 100644 --- a/apps/web/tests/add-company-wizard.test.tsx +++ b/apps/web/tests/add-company-wizard.test.tsx @@ -61,7 +61,7 @@ const CURRENT_USER = { auth_mode: "local", }; -function mockFetchImplementation(existingCompanies: unknown[] = []) { +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") { @@ -73,6 +73,25 @@ function mockFetchImplementation(existingCompanies: unknown[] = []) { 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" }) }); }); } @@ -163,4 +182,26 @@ describe("AddCompanyWizardPage", () => { await user.type(email, "someone-else@example.com"); expect(email.value).toBe("someone-else@example.com"); }); + + it("leaves the notification email blank for the local-dev bypass account", async () => { + // local@ci-agent.local 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(); + + 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(""); + }); });