// @vitest-environment jsdom // // The billing page. We mock `@voltro/client` (the live subscription + the three // billing actions) and assert: the current plan renders, and the plan-change // flow quotes the provider's proration BEFORE it can be confirmed (preview → // confirm), not a locally-estimated figure. import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' import { act, createElement, type ReactNode } from 'react' import { createRoot, type Root } from 'react-dom/client' import { I18nProvider } from '@voltro/i18n' import enCatalog from '../../../locales/en' const subData = vi.fn<() => { data: unknown }>() const previewRun = vi.fn<(input: unknown) => Promise>() vi.mock('@voltro/client', () => ({ useSubscription: () => subData(), useAction: (_api: string, tag: string) => ({ run: tag === 'billing.previewChange' ? previewRun : vi.fn().mockResolvedValue({ url: 'https://provider/portal' }), pending: false, }), })) const { default: Billing, meta, renderMode } = await import('./page') ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true let container: HTMLDivElement let root: Root const render = (node: ReactNode): void => { container = document.createElement('div') document.body.appendChild(container) act(() => { root = createRoot(container) root.render( createElement(I18nProvider, { locale: 'en', messages: enCatalog, defaultLocale: 'en', children: node }), ) }) } beforeEach(() => { subData.mockReset() subData.mockReturnValue({ data: { subscription: { plan: 'free', status: 'active', quantity: 1, currentPeriodEnd: null, cancelAt: null } } }) previewRun.mockReset() previewRun.mockResolvedValue({ plan: 'pro', quantity: 1, prorationMinor: 900, currency: 'usd' }) }) afterEach(() => { if (root) act(() => root.unmount()) container?.remove() document.body.innerHTML = '' }) describe('billing — config', () => { test('is an SSR page titled Billing (so the parent gate runs per request)', () => { expect(renderMode).toBe('ssr') expect(meta({ locale: 'en' }).title).toBe('Billing') }) }) describe('billing — render + change flow', () => { test('shows the current plan', () => { render(createElement(Billing)) expect(container.textContent).toContain('free') expect(container.textContent).toContain('Current plan') }) test('previewing the upgrade quotes the provider proration before confirm appears', async () => { render(createElement(Billing)) // Before preview: the button is "Preview upgrade", not "Confirm". expect(container.textContent).toContain('Preview upgrade') expect(container.textContent).not.toContain('Confirm upgrade') const previewBtn = Array.from(container.querySelectorAll('button')).find((b) => b.textContent === 'Preview upgrade')! await act(async () => { previewBtn.dispatchEvent(new MouseEvent('click', { bubbles: true })) await Promise.resolve(); await Promise.resolve() }) expect(previewRun).toHaveBeenCalledWith({ plan: 'pro' }) // The quote is shown and now the confirm button is available. expect(container.textContent).toContain('settles at about') expect(container.textContent).toContain('Confirm upgrade') }) })