skills/playwright-pro/templates/onboarding/email-verification.md

5.0 KiB

Email Verification Template

Tests email verification link, resend flow, and expired token handling.

Prerequisites

  • Registered but unverified account: {{unverifiedEmail}}
  • Valid token: {{verificationToken}}
  • Expired token: {{expiredVerificationToken}}
  • App running at {{baseUrl}}

TypeScript

import { test, expect } from '@playwright/test';

test.describe('Email Verification', () => {
  // Happy path: valid verification link
  test('verifies email with valid token', async ({ page }) => {
    await page.goto('{{baseUrl}}/verify-email?token={{verificationToken}}');
    await expect(page.getByRole('heading', { name: /email verified|verified/i })).toBeVisible();
    await expect(page.getByRole('link', { name: /continue|go to dashboard/i })).toBeVisible();
  });

  // Happy path: continues to app after verification
  test('redirects to dashboard after clicking continue', async ({ page }) => {
    await page.goto('{{baseUrl}}/verify-email?token={{verificationToken}}');
    await page.getByRole('link', { name: /continue|go to dashboard/i }).click();
    await expect(page).toHaveURL('{{baseUrl}}/dashboard');
  });

  // Happy path: resend verification email
  test('resends verification email', async ({ page }) => {
    await page.goto('{{baseUrl}}/verify-email/resend');
    await page.getByRole('textbox', { name: /email/i }).fill('{{unverifiedEmail}}');
    await page.getByRole('button', { name: /resend/i }).click();
    await expect(page.getByRole('alert')).toContainText(/sent|check your email/i);
  });

  // Happy path: verification prompt on login for unverified user
  test('shows verification prompt when unverified user logs in', async ({ page }) => {
    await page.goto('{{baseUrl}}/login');
    await page.getByRole('textbox', { name: /email/i }).fill('{{unverifiedEmail}}');
    await page.getByRole('textbox', { name: /password/i }).fill('{{password}}');
    await page.getByRole('button', { name: /sign in/i }).click();
    await expect(page.getByText(/verify.*email|check.*inbox/i)).toBeVisible();
    await expect(page.getByRole('button', { name: /resend.*verification/i })).toBeVisible();
  });

  // Error case: expired token
  test('shows error for expired verification token', async ({ page }) => {
    await page.goto('{{baseUrl}}/verify-email?token={{expiredVerificationToken}}');
    await expect(page.getByRole('heading', { name: /link.*expired|verification.*failed/i })).toBeVisible();
    await expect(page.getByRole('link', { name: /resend|request new/i })).toBeVisible();
  });

  // Error case: invalid token
  test('shows error for invalid verification token', async ({ page }) => {
    await page.goto('{{baseUrl}}/verify-email?token=invalid-token-xyz');
    await expect(page.getByRole('heading', { name: /invalid|failed/i })).toBeVisible();
  });

  // Edge case: already verified user hitting link
  test('shows already verified message for used token', async ({ page }) => {
    await page.goto('{{baseUrl}}/verify-email?token={{usedVerificationToken}}');
    await expect(page.getByText(/already verified|email.*confirmed/i)).toBeVisible();
    await expect(page.getByRole('link', { name: /sign in/i })).toBeVisible();
  });
});

JavaScript

const { test, expect } = require('@playwright/test');

test.describe('Email Verification', () => {
  test('verifies email with valid token', async ({ page }) => {
    await page.goto('{{baseUrl}}/verify-email?token={{verificationToken}}');
    await expect(page.getByRole('heading', { name: /email verified/i })).toBeVisible();
  });

  test('shows error for expired token', async ({ page }) => {
    await page.goto('{{baseUrl}}/verify-email?token={{expiredVerificationToken}}');
    await expect(page.getByRole('heading', { name: /link.*expired/i })).toBeVisible();
    await expect(page.getByRole('link', { name: /resend|request new/i })).toBeVisible();
  });

  test('resends verification email', async ({ page }) => {
    await page.goto('{{baseUrl}}/verify-email/resend');
    await page.getByRole('textbox', { name: /email/i }).fill('{{unverifiedEmail}}');
    await page.getByRole('button', { name: /resend/i }).click();
    await expect(page.getByRole('alert')).toContainText(/sent/i);
  });

  test('shows verification prompt on login for unverified user', async ({ page }) => {
    await page.goto('{{baseUrl}}/login');
    await page.getByRole('textbox', { name: /email/i }).fill('{{unverifiedEmail}}');
    await page.getByRole('textbox', { name: /password/i }).fill('{{password}}');
    await page.getByRole('button', { name: /sign in/i }).click();
    await expect(page.getByText(/verify.*email/i)).toBeVisible();
  });
});

Variants

Variant Description
Valid token Email verified heading + continue link
Continue CTA Navigates to dashboard
Resend Sends new email, success alert
Login prompt Unverified login shows resend button
Expired token Error heading + resend link
Invalid token Generic error heading
Already verified "Already verified" with login link