Ctrl K
ComunidadGuías…

tests/cancel-plan-modal.unit.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// @vitest-environment happy-dom

import { afterEach, describe, expect, it, vi } from 'vitest'
import { createApp, h, nextTick } from 'vue'
import CancelPlanModal from '../src/components/billing/CancelPlanModal.vue'

const mocks = vi.hoisted(() => ({
  getCancelSaveOffer: vi.fn(),
  cancelSubscriptionWithReason: vi.fn(),
  acceptCancelSaveOffer: vi.fn(),
}))

vi.mock('vue-i18n', () => ({ useI18n: () => ({ t: (key: string) => key }) }))
vi.mock('vue-sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } }))
vi.mock('../src/services/stripe', () => ({
  CANCEL_REASONS: ['too_expensive', 'other'],
  getCancelSaveOffer: mocks.getCancelSaveOffer,
  cancelSubscriptionWithReason: mocks.cancelSubscriptionWithReason,
  acceptCancelSaveOffer: mocks.acceptCancelSaveOffer,
  formatStripeAmount: (amount: number) => `$${amount / 100}`,
}))

const offer = {
  id: 'offer_1',
  percentOff: 50,
  durationMonths: 3,
  monthlyAmount: 2900,
  discountedAmount: 1450,
  currency: 'usd',
}

function mountModal(onClose = vi.fn()) {
  const onCanceled = vi.fn()
  const onSaved = vi.fn()
  const root = document.createElement('div')
  document.body.appendChild(root)
  const app = createApp({
    render: () => h(CancelPlanModal, {
      orgId: 'org_test',
      planName: 'Solo',
      onClose,
      onCanceled,
      onSaved,
    }),
  })
  app.mount(root)
  return { app, root, onClose, onCanceled, onSaved }
}

function shell() {
  return document.body
}

async function pickReason() {
  const input = shell().querySelector('[data-test="cancel-reason-too_expensive"] input') as HTMLInputElement
  input.click()
  await nextTick()
}

async function submitReason() {
  const button = shell().querySelector('[data-test="cancel-plan-confirm"]') as HTMLButtonElement
  button.click()
  await nextTick()
}

describe('CancelPlanModal loss reminder', () => {
  afterEach(() => {
    document.body.replaceChildren()
    vi.clearAllMocks()
  })

  it('shows the loss step after the reason form is submitted', async () => {
    mountModal()
    await pickReason()
    await submitReason()
    expect(shell().querySelector('[data-test="cancel-loss-reminder"]')).toBeTruthy()
    const focusTarget = shell().querySelector('[data-test="cancel-loss-reminder"] [tabindex="-1"]')
    await vi.waitFor(() => expect(document.activeElement).toBe(focusTarget))
    expect(shell().querySelector('[data-test="cancel-plan-form"]')).toBeNull()
    expect(mocks.getCancelSaveOffer).not.toHaveBeenCalled()
  })

  it('closes when keep plan is clicked on the loss step', async () => {
    const onClose = vi.fn()
    mountModal(onClose)
    await pickReason()
    await submitReason()
    ;(shell().querySelector('[data-test="cancel-loss-keep-plan"]') as HTMLButtonElement).click()
    expect(onClose).toHaveBeenCalledOnce()
  })

  it('loads the offer after continue on the loss step', async () => {
    mocks.getCancelSaveOffer.mockResolvedValue({ offer })
    mountModal()
    await pickReason()
    await submitReason()
    ;(shell().querySelector('[data-test="cancel-loss-continue"]') as HTMLButtonElement).click()
    await vi.waitFor(() => expect(shell().querySelector('[data-test="cancel-save-offer"]')).toBeTruthy())
    expect(mocks.getCancelSaveOffer).toHaveBeenCalledWith('org_test', 'too_expensive')
    expect(mocks.cancelSubscriptionWithReason).not.toHaveBeenCalled()
  })

  it('cancels when continue is clicked and no offer is available', async () => {
    mocks.getCancelSaveOffer.mockResolvedValue({ offer: null })
    mocks.cancelSubscriptionWithReason.mockResolvedValue({ subscription: null })
    const onCanceled = vi.fn()
    const root = document.createElement('div')
    document.body.appendChild(root)
    const app = createApp({
      render: () => h(CancelPlanModal, {
        orgId: 'org_test',
        onCanceled,
      }),
    })
    app.mount(root)
    await pickReason()
    await submitReason()
    ;(shell().querySelector('[data-test="cancel-loss-continue"]') as HTMLButtonElement).click()
    await vi.waitFor(() => expect(onCanceled).toHaveBeenCalledOnce())
    expect(mocks.cancelSubscriptionWithReason).toHaveBeenCalledWith('org_test', 'too_expensive', '', undefined)
  })

  it('still cancels when declining an eligible offer', async () => {
    mocks.getCancelSaveOffer.mockResolvedValue({ offer })
    mocks.cancelSubscriptionWithReason.mockResolvedValue({ subscription: { cancelAtPeriodEnd: true } })
    const onCanceled = vi.fn()
    const root = document.createElement('div')
    document.body.appendChild(root)
    const app = createApp({
      render: () => h(CancelPlanModal, {
        orgId: 'org_test',
        onCanceled,
      }),
    })
    app.mount(root)
    await pickReason()
    await submitReason()
    ;(shell().querySelector('[data-test="cancel-loss-continue"]') as HTMLButtonElement).click()
    await vi.waitFor(() => expect(shell().querySelector('[data-test="cancel-save-offer"]')).toBeTruthy())
    ;(shell().querySelector('[data-test="cancel-offer-decline"]') as HTMLButtonElement).click()
    await vi.waitFor(() => expect(onCanceled).toHaveBeenCalledOnce())
    expect(mocks.cancelSubscriptionWithReason).toHaveBeenCalledWith('org_test', 'too_expensive', '', offer.id)
  })
})