- src/hooks/query-keys.ts - tests/e2e/registration.spec.ts - tests/e2e/subscription.spec.ts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
349 lines
13 KiB
TypeScript
349 lines
13 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import { Page } from '@playwright/test';
|
|
|
|
test.describe('Subscription Flow', () => {
|
|
let page: Page;
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
page = page;
|
|
});
|
|
|
|
test('should display pricing page', async () => {
|
|
await page.goto('/pricing');
|
|
|
|
// Check page title
|
|
await expect(page).toHaveTitle(/Pricing/);
|
|
|
|
// Check pricing plans
|
|
await expect(page.locator('[data-testid="pricing-plans"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="plan-basic"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="plan-pro"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="plan-enterprise"]')).toBeVisible();
|
|
});
|
|
|
|
test('should show plan details', async () => {
|
|
await page.goto('/pricing');
|
|
|
|
// Click on Pro plan
|
|
await page.locator('[data-testid="plan-pro"]').click();
|
|
|
|
// Check modal opens with details
|
|
await expect(page.locator('[data-testid="plan-modal"]')).toBeVisible();
|
|
await expect(page.locator('text=Pro Plan Features')).toBeVisible();
|
|
|
|
// Check features list
|
|
await expect(page.locator('[data-testid="feature-list"]')).toBeVisible();
|
|
await expect(page.locator('text=Unlimited users')).toBeVisible();
|
|
await expect(page.locator('text=100GB storage')).toBeVisible();
|
|
await expect(page.locator('text=Priority support')).toBeVisible();
|
|
});
|
|
|
|
test('should select plan and proceed to checkout', async () => {
|
|
await page.goto('/pricing');
|
|
|
|
// Select Pro plan
|
|
await page.locator('[data-testid="plan-pro"] [data-testid="select-plan"]').click();
|
|
|
|
// Should redirect to checkout
|
|
await expect(page).toHaveURL(/checkout/);
|
|
|
|
// Check checkout page elements
|
|
await expect(page.locator('[data-testid="checkout-form"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="plan-summary"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="payment-method"]')).toBeVisible();
|
|
});
|
|
|
|
test('should fill payment form', async () => {
|
|
// Mock user is logged in
|
|
await page.goto('/checkout?plan=pro');
|
|
|
|
// Fill card details
|
|
await page.locator('[data-testid="card-number"]').fill('4242424242424242');
|
|
await page.locator('[data-testid="card-expiry"]').fill('12/25');
|
|
await page.locator('[data-testid="card-cvc"]').fill('123');
|
|
await page.locator('[data-testid="card-name"]').fill('John Doe');
|
|
|
|
// Fill billing address
|
|
await page.locator('[data-testid="billing-address"]').fill('123 Main St');
|
|
await page.locator('[data-testid="billing-city"]').fill('New York');
|
|
await page.locator('[data-testid="billing-zip"]').fill('10001');
|
|
await page.locator('[data-testid="billing-country"]').selectOption('US');
|
|
|
|
// Accept terms
|
|
await page.locator('[data-testid="accept-terms"]').check();
|
|
|
|
// Enable auto-renew
|
|
await page.locator('[data-testid="auto-renew"]').check();
|
|
});
|
|
|
|
test('should validate payment form', async () => {
|
|
await page.goto('/checkout?plan=pro');
|
|
|
|
// Submit empty form
|
|
await page.locator('[data-testid="submit-payment"]').click();
|
|
|
|
// Check validation errors
|
|
await expect(page.locator('text=Card number is required')).toBeVisible();
|
|
await expect(page.locator('text=Expiry date is required')).toBeVisible();
|
|
await expect(page.locator('text=CVC is required')).toBeVisible();
|
|
await expect(page.locator('text=Name on card is required')).toBeVisible();
|
|
});
|
|
|
|
test('should validate card number format', async () => {
|
|
await page.goto('/checkout?plan=pro');
|
|
|
|
// Enter invalid card number
|
|
await page.locator('[data-testid="card-number"]').fill('123456');
|
|
await page.locator('[data-testid="submit-payment"]').click();
|
|
|
|
await expect(page.locator('text=Please enter a valid card number')).toBeVisible();
|
|
});
|
|
|
|
test('should process subscription successfully', async () => {
|
|
// Mock Stripe success response
|
|
await page.route('/api/billing/subscribe', route => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: true,
|
|
subscription: {
|
|
id: 'sub_123',
|
|
status: 'active',
|
|
plan: 'pro',
|
|
current_period_end: '2026-02-20',
|
|
},
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.goto('/checkout?plan=pro');
|
|
|
|
// Fill valid payment details
|
|
await page.locator('[data-testid="card-number"]').fill('4242424242424242');
|
|
await page.locator('[data-testid="card-expiry"]').fill('12/25');
|
|
await page.locator('[data-testid="card-cvc"]').fill('123');
|
|
await page.locator('[data-testid="card-name"]').fill('John Doe');
|
|
await page.locator('[data-testid="accept-terms"]').check();
|
|
|
|
// Submit payment
|
|
await page.locator('[data-testid="submit-payment"]').click();
|
|
|
|
// Should show success message
|
|
await expect(page.locator('[data-testid="success-message"]')).toBeVisible();
|
|
await expect(page.locator('text=Subscription successful!')).toBeVisible();
|
|
|
|
// Should redirect to dashboard
|
|
await expect(page).toHaveURL(/dashboard/);
|
|
});
|
|
|
|
test('should handle payment failure', async () => {
|
|
// Mock Stripe error response
|
|
await page.route('/api/billing/subscribe', route => {
|
|
route.fulfill({
|
|
status: 400,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
error: {
|
|
message: 'Your card was declined.',
|
|
code: 'card_declined',
|
|
},
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.goto('/checkout?plan=pro');
|
|
|
|
// Fill payment details
|
|
await page.locator('[data-testid="card-number"]').fill('4000000000000002');
|
|
await page.locator('[data-testid="card-expiry"]').fill('12/25');
|
|
await page.locator('[data-testid="card-cvc"]').fill('123');
|
|
await page.locator('[data-testid="card-name"]').fill('John Doe');
|
|
await page.locator('[data-testid="accept-terms"]').check();
|
|
|
|
// Submit payment
|
|
await page.locator('[data-testid="submit-payment"]').click();
|
|
|
|
// Should show error message
|
|
await expect(page.locator('[data-testid="error-message"]')).toBeVisible();
|
|
await expect(page.locator('text=Your card was declined')).toBeVisible();
|
|
});
|
|
|
|
test('should support multiple payment methods', async () => {
|
|
await page.goto('/checkout?plan=pro');
|
|
|
|
// Check available payment methods
|
|
await expect(page.locator('[data-testid="payment-card"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="payment-paypal"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="payment-bank-transfer"]')).toBeVisible();
|
|
|
|
// Select PayPal
|
|
await page.locator('[data-testid="payment-paypal"]').click();
|
|
|
|
// Should show PayPal form
|
|
await expect(page.locator('[data-testid="paypal-form"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="paypal-email"]')).toBeVisible();
|
|
});
|
|
|
|
test('should apply discount code', async () => {
|
|
await page.goto('/checkout?plan=pro');
|
|
|
|
// Enter discount code
|
|
await page.locator('[data-testid="discount-code"]').fill('SAVE20');
|
|
await page.locator('[data-testid="apply-discount"]').click();
|
|
|
|
// Should show discount applied
|
|
await expect(page.locator('[data-testid="discount-applied"]')).toBeVisible();
|
|
await expect(page.locator('text=20% discount applied')).toBeVisible();
|
|
|
|
// Check updated price
|
|
await expect(page.locator('[data-testid="final-price"]')).toContainText('$79.20');
|
|
});
|
|
|
|
test('should handle invalid discount code', async () => {
|
|
await page.goto('/checkout?plan=pro');
|
|
|
|
// Enter invalid discount code
|
|
await page.locator('[data-testid="discount-code"]').fill('INVALID');
|
|
await page.locator('[data-testid="apply-discount"]').click();
|
|
|
|
// Should show error
|
|
await expect(page.locator('[data-testid="discount-error"]')).toBeVisible();
|
|
await expect(page.locator('text=Invalid discount code')).toBeVisible();
|
|
});
|
|
|
|
test('should show subscription management', async () => {
|
|
// Mock authenticated user with active subscription
|
|
await page.goto('/settings/billing');
|
|
|
|
// Check subscription details
|
|
await expect(page.locator('[data-testid="current-plan"]')).toBeVisible();
|
|
await expect(page.locator('text=Pro Plan')).toBeVisible();
|
|
await expect(page.locator('[data-testid="subscription-status"]')).toContainText('Active');
|
|
await expect(page.locator('[data-testid="next-billing"]')).toBeVisible();
|
|
|
|
// Check management options
|
|
await expect(page.locator('[data-testid="update-payment"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="cancel-subscription"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="download-invoices"]')).toBeVisible();
|
|
});
|
|
|
|
test('should handle subscription upgrade', async () => {
|
|
await page.goto('/settings/billing');
|
|
|
|
// Click upgrade button
|
|
await page.locator('[data-testid="upgrade-plan"]').click();
|
|
|
|
// Should show upgrade options
|
|
await expect(page.locator('[data-testid="upgrade-modal"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="plan-enterprise"]')).toBeVisible();
|
|
|
|
// Select Enterprise plan
|
|
await page.locator('[data-testid="select-enterprise"]').click();
|
|
|
|
// Should show prorated billing info
|
|
await expect(page.locator('[data-testid="prorated-amount"]')).toBeVisible();
|
|
await expect(page.locator('text=Prorated amount due today')).toBeVisible();
|
|
});
|
|
|
|
test('should handle subscription cancellation', async () => {
|
|
await page.goto('/settings/billing');
|
|
|
|
// Click cancel subscription
|
|
await page.locator('[data-testid="cancel-subscription"]').click();
|
|
|
|
// Should show cancellation modal
|
|
await expect(page.locator('[data-testid="cancel-modal"]')).toBeVisible();
|
|
await expect(page.locator('text=Are you sure you want to cancel?')).toBeVisible();
|
|
|
|
// Select cancellation reason
|
|
await page.locator('[data-testid="cancellation-reason"]').selectOption('too_expensive');
|
|
|
|
// Add feedback
|
|
await page.locator('[data-testid="cancellation-feedback"]').fill('Found a cheaper alternative');
|
|
|
|
// Confirm cancellation
|
|
await page.locator('[data-testid="confirm-cancellation"]').click();
|
|
|
|
// Should show confirmation
|
|
await expect(page.locator('[data-testid="cancellation-success"]')).toBeVisible();
|
|
await expect(page.locator('text=Subscription will be cancelled at the end of the billing period')).toBeVisible();
|
|
});
|
|
|
|
test('should handle subscription reactivation', async () => {
|
|
// Mock cancelled subscription
|
|
await page.goto('/settings/billing');
|
|
|
|
// Check reactivation option is available
|
|
await expect(page.locator('[data-testid="reactivate-subscription"]')).toBeVisible();
|
|
|
|
// Click reactivate
|
|
await page.locator('[data-testid="reactivate-subscription"]').click();
|
|
|
|
// Should show reactivation modal
|
|
await expect(page.locator('[data-testid="reactivate-modal"]')).toBeVisible();
|
|
await expect(page.locator('text=Reactivate your subscription')).toBeVisible();
|
|
|
|
// Confirm reactivation
|
|
await page.locator('[data-testid="confirm-reactivation"]').click();
|
|
|
|
// Should process reactivation
|
|
await expect(page.locator('[data-testid="reactivation-success"]')).toBeVisible();
|
|
});
|
|
|
|
test('should display invoice history', async () => {
|
|
await page.goto('/settings/billing');
|
|
|
|
// Click invoices tab
|
|
await page.locator('[data-testid="invoices-tab"]').click();
|
|
|
|
// Check invoice list
|
|
await expect(page.locator('[data-testid="invoice-list"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="invoice-item"]')).toHaveCount(3);
|
|
|
|
// Check invoice details
|
|
await expect(page.locator('[data-testid="invoice-date"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="invoice-amount"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="invoice-status"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="download-invoice"]')).toBeVisible();
|
|
});
|
|
|
|
test('should download invoice', async () => {
|
|
// Mock download
|
|
const downloadPromise = page.waitForEvent('download');
|
|
|
|
await page.goto('/settings/billing');
|
|
await page.locator('[data-testid="invoices-tab"]').click();
|
|
|
|
// Click download button
|
|
await page.locator('[data-testid="download-invoice"]').first().click();
|
|
|
|
// Wait for download
|
|
const download = await downloadPromise;
|
|
expect(download.suggestedFilename()).toMatch(/invoice-\d+\.pdf/);
|
|
});
|
|
|
|
test('should update payment method', async () => {
|
|
await page.goto('/settings/billing');
|
|
|
|
// Click update payment method
|
|
await page.locator('[data-testid="update-payment"]').click();
|
|
|
|
// Should show payment form
|
|
await expect(page.locator('[data-testid="payment-form"]')).toBeVisible();
|
|
|
|
// Fill new card details
|
|
await page.locator('[data-testid="card-number"]').fill('5555555555554444');
|
|
await page.locator('[data-testid="card-expiry"]').fill('10/26');
|
|
await page.locator('[data-testid="card-cvc"]').fill('456');
|
|
await page.locator('[data-testid="card-name"]').fill('Jane Smith');
|
|
|
|
// Submit update
|
|
await page.locator('[data-testid="update-payment-submit"]').click();
|
|
|
|
// Should show success message
|
|
await expect(page.locator('[data-testid="update-success"]')).toBeVisible();
|
|
await expect(page.locator('text=Payment method updated successfully')).toBeVisible();
|
|
});
|
|
});
|