</>

Technology

Playwright

Difficulty

Advanced

Interview Question

How do you test a JWT-authenticated application in Playwright?

Answer

Testing JWT-Authenticated Applications in Playwright

Method 1 — Set JWT in localStorage Before Navigation

TypeScript
test('access protected page with JWT', async ({ page }) => {
  const jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';

  // Set token BEFORE navigating — otherwise the page loads without auth
  await page.addInitScript(token => {
    localStorage.setItem('access_token', token);
    localStorage.setItem('user', JSON.stringify({ id: 1, role: 'admin' }));
  }, jwtToken);

  await page.goto('/dashboard');
  await expect(page).toHaveURL('/dashboard');
  await expect(page.getByRole('heading')).toContainText('Dashboard');
});

Method 2 — Set JWT as Cookie

TypeScript
test('authenticate via JWT cookie', async ({ context, page }) => {
  await context.addCookies([{
    name: 'jwt_token',
    value: 'eyJhbGciOiJIUzI1NiJ9...',
    domain: 'localhost',
    path: '/',
    httpOnly: true,
    secure: false, // true for HTTPS
    sameSite: 'Lax',
  }]);

  await page.goto('/dashboard');
  await expect(page.getByRole('heading')).toHaveText('Dashboard');
});

Method 3 — Intercept Requests and Add Auth Header

TypeScript
test('add JWT to all API calls', async ({ page }) => {
  const token = process.env.TEST_JWT_TOKEN!;

  await page.route('/api/**', async route => {
    const headers = {
      ...route.request().headers(),
      'Authorization': `Bearer ${token}`,
    };
    await route.continue({ headers });
  });

  await page.goto('/products');
  await expect(page.locator('.product-list')).toBeVisible();
});

Method 4 — Login via API to Get Real JWT

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

test.beforeAll(async ({ request }) => {
  // Login via API to get a real JWT
  const response = await request.post('/api/auth/login', {
    data: {
      email: 'test@example.com',
      password: 'password123',
    },
  });

  const { accessToken, refreshToken } = await response.json();
  process.env.ACCESS_TOKEN  = accessToken;
  process.env.REFRESH_TOKEN = refreshToken;
});

test('uses real JWT from API login', async ({ page }) => {
  await page.addInitScript(token => {
    localStorage.setItem('access_token', token);
  }, process.env.ACCESS_TOKEN);

  await page.goto('/profile');
  await expect(page.locator('.user-name')).toBeVisible();
});

Method 5 — storageState for JWT Persistence

TypeScript
// auth.setup.ts — save JWT in storageState
import { test as setup } from '@playwright/test';

setup('save auth state', async ({ page }) => {
  // Login normally via UI
  await page.goto('/login');
  await page.getByLabel('Email').fill('user@test.com');
  await page.getByLabel('Password').fill('password');
  await page.getByRole('button', { name: 'Sign in' }).click();
  await page.waitForURL('/dashboard');

  // JWT is now in localStorage — save it
  await page.context().storageState({ path: '.auth/user.json' });
});
TypeScript
// playwright.config.ts — all tests start with JWT in localStorage
projects: [
  { name: 'setup', testMatch: '*.setup.ts' },
  {
    name: 'e2e',
    use: { storageState: '.auth/user.json' }, // JWT already in storage
    dependencies: ['setup'],
  },
],

Verify JWT in Test

TypeScript
test('check token is set', async ({ page }) => {
  const token = await page.evaluate(() => localStorage.getItem('access_token'));
  expect(token).toBeTruthy();
  expect(token).toMatch(/^eyJ/); // JWT always starts with eyJ
});

Follow AutomateQA

Related Topics