import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' import { render, screen } from '@testing-library/react' import { MemoryRouter } from 'react-router-dom' import { TrialPill } from '../TrialPill' import { useBillingStore } from '@/store/billingStore' import type { SubscriptionState, PlanBillingState } from '@/types/billing' const FROZEN_NOW = new Date('2026-05-06T12:00:00Z') function renderPill() { return render( , ) } function setBilling(opts: { subscription: SubscriptionState | null planBilling?: PlanBillingState | null }) { useBillingStore.setState({ subscription: opts.subscription, planBilling: opts.planBilling ?? null, planLimits: {}, enabledFeatures: {}, isLoading: false, error: null, }) } function isoDaysFromNow(days: number): string { const d = new Date(FROZEN_NOW.getTime() + days * 24 * 60 * 60 * 1000) return d.toISOString() } describe('TrialPill', () => { beforeEach(() => { vi.useFakeTimers() vi.setSystemTime(FROZEN_NOW) useBillingStore.setState({ subscription: null, planBilling: null, planLimits: {}, enabledFeatures: {}, isLoading: false, error: null, }) }) afterEach(() => { vi.useRealTimers() }) it('renders Pro trial · Nd for pristine stage', () => { setBilling({ subscription: { status: 'trialing', plan: 'pro', current_period_start: FROZEN_NOW.toISOString(), current_period_end: isoDaysFromNow(12), cancel_at_period_end: false, seat_limit: 5, has_pro_entitlement: true, is_paid: false, }, }) renderPill() const pill = screen.getByTestId('trial-pill') expect(pill).toHaveTextContent(/Pro trial · 12d/) // Pristine uses info tone tokens. expect(pill.className).toContain('text-info') expect(pill.className).toContain('bg-info-dim') }) it('renders Trial expired CTA for expired stage', () => { setBilling({ subscription: { status: 'trialing', plan: 'pro', current_period_start: isoDaysFromNow(-14), current_period_end: isoDaysFromNow(-1), // already past cancel_at_period_end: false, seat_limit: 5, has_pro_entitlement: false, is_paid: false, }, }) renderPill() const pill = screen.getByTestId('trial-pill') expect(pill).toHaveTextContent(/Trial expired — pick a plan/) // Clickable: rendered as anchor/link. expect(pill.tagName).toBe('A') expect(pill.getAttribute('href')).toBe('/account/billing/select-plan') }) it('renders Complimentary Pro tag for complimentary subscription', () => { setBilling({ subscription: { status: 'complimentary', plan: 'pro', current_period_start: null, current_period_end: null, cancel_at_period_end: false, seat_limit: null, has_pro_entitlement: true, is_paid: true, }, }) renderPill() const pill = screen.getByTestId('trial-pill') expect(pill).toHaveTextContent(/Complimentary Pro/) // Friendly tag, not clickable. expect(pill.tagName).toBe('SPAN') expect(pill.className).toContain('text-accent') }) it('is hidden when subscription is null', () => { setBilling({ subscription: null }) const { container } = renderPill() expect(screen.queryByTestId('trial-pill')).not.toBeInTheDocument() expect(container.firstChild).toBeNull() }) it('past_due variant is clickable and links to /account/billing', () => { setBilling({ subscription: { status: 'past_due', plan: 'pro', current_period_start: isoDaysFromNow(-30), current_period_end: isoDaysFromNow(-2), cancel_at_period_end: false, seat_limit: 5, has_pro_entitlement: false, is_paid: true, }, }) renderPill() const pill = screen.getByTestId('trial-pill') expect(pill).toHaveTextContent(/Payment failed — update card/) expect(pill.tagName).toBe('A') expect(pill.getAttribute('href')).toBe('/account/billing') }) })