feat: self-serve signup Phase 2 (frontend cutover) (#162)
Some checks failed
CI / e2e (push) Has been cancelled
CI / frontend (push) Has been cancelled
CI / backend (push) Has been cancelled
Mirror to GitHub / mirror (push) Has been cancelled

Co-authored-by: Michael Chihlas <michael@resolutionflow.com>
Co-committed-by: Michael Chihlas <michael@resolutionflow.com>
This commit was merged in pull request #162.
This commit is contained in:
2026-05-07 18:42:20 +00:00
committed by chihlasm
parent f918b766b0
commit f1be3abcc5
123 changed files with 11563 additions and 559 deletions

View File

@@ -0,0 +1,146 @@
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(
<HelmetProvider>
<MemoryRouter initialEntries={['/contact-sales']}>
<ContactSalesPage />
</MemoryRouter>
</HelmetProvider>,
)
}
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()
})
})