import { describe, it, expect, beforeEach, vi } from 'vitest' import { render, screen, fireEvent, waitFor } from '@testing-library/react' import { MemoryRouter } from 'react-router-dom' import { HelmetProvider } from 'react-helmet-async' import { ContactSalesPage } from '../ContactSalesPage' import { salesApi } from '@/api/sales' import { __resetAppConfigCache, __setAppConfigCache, } from '@/hooks/useAppConfig' vi.mock('@/api/sales', () => ({ salesApi: { createLead: vi.fn(), }, })) function renderPage() { return render( , ) } function fillRequiredFields() { fireEvent.change(screen.getByTestId('cs-name'), { target: { value: 'Jane Doe' } }) fireEvent.change(screen.getByTestId('cs-email'), { target: { value: 'jane@acme.com' } }) fireEvent.change(screen.getByTestId('cs-company'), { target: { value: 'Acme MSP' } }) } describe('ContactSalesPage', () => { beforeEach(() => { __resetAppConfigCache() __setAppConfigCache({ self_serve_enabled: true, oauth_providers: [], }) vi.clearAllMocks() vi.unstubAllEnvs() }) it('submits form and shows confirmation', async () => { vi.stubEnv('VITE_CALENDLY_URL', 'https://calendly.com/resolutionflow/sales') vi.mocked(salesApi.createLead).mockResolvedValue({ id: 'fake-uuid', status: 'received', }) renderPage() fillRequiredFields() fireEvent.change(screen.getByTestId('cs-team-size'), { target: { value: '11-25' } }) fireEvent.change(screen.getByTestId('cs-message'), { target: { value: 'Looking at Enterprise pricing.' }, }) fireEvent.click(screen.getByTestId('cs-submit')) await waitFor(() => { expect(salesApi.createLead).toHaveBeenCalledTimes(1) }) const payload = vi.mocked(salesApi.createLead).mock.calls[0][0] expect(payload).toMatchObject({ name: 'Jane Doe', email: 'jane@acme.com', company: 'Acme MSP', team_size: '11-25', message: 'Looking at Enterprise pricing.', }) // Default source is landing_page (no /pricing in referrer in jsdom). expect(payload.source).toBe('landing_page') // Confirmation surface replaces the form. await waitFor(() => { expect(screen.getByTestId('contact-sales-confirmation')).toBeInTheDocument() }) expect(screen.queryByTestId('contact-sales-form')).not.toBeInTheDocument() expect(screen.getByText(/Thanks/i)).toBeInTheDocument() }) it('hides Calendly section when VITE_CALENDLY_URL unset', async () => { vi.stubEnv('VITE_CALENDLY_URL', '') vi.mocked(salesApi.createLead).mockResolvedValue({ id: 'fake-uuid', status: 'received', }) renderPage() fillRequiredFields() fireEvent.click(screen.getByTestId('cs-submit')) await waitFor(() => { expect(screen.getByTestId('contact-sales-confirmation')).toBeInTheDocument() }) expect(screen.queryByTestId('calendly-block')).not.toBeInTheDocument() expect(screen.queryByTestId('calendly-link')).not.toBeInTheDocument() }) it('disables submit button while in flight to prevent duplicate submissions', async () => { let resolveSubmit: (() => void) | null = null vi.mocked(salesApi.createLead).mockImplementation( () => new Promise((resolve) => { resolveSubmit = () => resolve({ id: 'fake-uuid', status: 'received' }) }), ) renderPage() fillRequiredFields() const submit = screen.getByTestId('cs-submit') as HTMLButtonElement fireEvent.click(submit) await waitFor(() => { expect(submit.disabled).toBe(true) }) // A second click while in flight should be a no-op. fireEvent.click(submit) expect(salesApi.createLead).toHaveBeenCalledTimes(1) resolveSubmit?.() await waitFor(() => { expect(screen.getByTestId('contact-sales-confirmation')).toBeInTheDocument() }) }) it('returns 404 when self_serve_enabled is false', () => { __resetAppConfigCache() __setAppConfigCache({ self_serve_enabled: false, oauth_providers: [], }) renderPage() expect(screen.getByTestId('contact-sales-not-found')).toBeInTheDocument() expect(screen.queryByTestId('contact-sales-form')).not.toBeInTheDocument() }) })