Verification emails linked to /verify-email?token=..., but no such route existed in main.tsx and nothing in the frontend ever called POST /api/customers/verify-email. The SPA catch-all served index.html, no route matched, and the page rendered blank -- so the token was never redeemed and accounts stayed unverified forever. The gap was invisible until SMTP was configured, because no verification email had ever actually been delivered. Adds VerifyEmail.tsx (verifying / verified / failed states), a verifyEmail call in customerApi, and the route. The request is pinned to a single firing via a ref: the endpoint deletes the token on success, so StrictMode's double effect invocation in dev would otherwise overwrite the success state with "invalid or expired token". Verified end to end against a local stack: a real token flips customers.email_verified to true and is consumed from customer_tokens. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
19 lines
956 B
TypeScript
19 lines
956 B
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
// The /verify-email route was missing entirely, so the link in every verification
|
|
// email rendered a blank page. These cover the route existing and reporting an
|
|
// outcome; a happy-path test would need a real token out of the database.
|
|
test.describe('Email verification', () => {
|
|
test('reports an invalid token instead of rendering nothing', async ({ page }) => {
|
|
await page.goto('/verify-email?token=not-a-real-token');
|
|
await expect(page.getByRole('heading', { name: 'Email verification' })).toBeVisible();
|
|
await expect(page.getByText('invalid or expired token')).toBeVisible();
|
|
});
|
|
|
|
test('reports a link with no token at all', async ({ page }) => {
|
|
await page.goto('/verify-email');
|
|
await expect(page.getByRole('heading', { name: 'Email verification' })).toBeVisible();
|
|
await expect(page.getByText('This link is missing its verification token.')).toBeVisible();
|
|
});
|
|
});
|