Skip to content

Protected Routes Testing

Protected routes should reject unauthenticated users and enforce roles. Playwright verifies guards redirect correctly and authorized users reach content.

Guard Test Scenarios

Logged-out user visiting /admin should redirect to login or see 403. Authenticated user with wrong role should see unauthorized message. Authorized user sees content.

Use empty storageState for unauthenticated cases; role-specific storageState for authorization matrix.

test('guest redirected from admin', async ({ page }) => {
  await page.context().clearCookies();
  await page.goto('/admin');
  await expect(page).toHaveURL(/\/login/);
});

test('admin can access', async ({ page }) => {
  // uses admin storageState from project config
  await page.goto('/admin');
  await expect(page.getByRole('heading', { name: 'Admin' })).toBeVisible();
});

Separate projects for admin vs user storageState simplify role tests.

Role Matrix Testing

projects: [
  { name: 'admin', use: { storageState: '.auth/admin.json' } },
  { name: 'user', use: { storageState: '.auth/user.json' } },
]
  • Same spec runs twice under different projects for role matrix.
  • Assert URL, visible forbidden message, or HTTP 403 page content.
  • Deep link: goto protected URL directly, not only via nav.
  • Client-side routers may need expect(page).toHaveURL retry.

Protected Route Checks

What to assert.

Case Expected
No auth Redirect to login
Wrong role 403 or unauthorized component
Correct role Protected content visible
Deep link Guard applies on direct URL
Post-logout Back button doesn't restore session
API + UI API 403 matches UI message

Parameterized Role Tests

for (const { role, state, canAccess } of cases) {
  test(`${role} access`, async ({ browser }) => {
    const ctx = await browser.newContext({ storageState: state });
    const page = await ctx.newPage();
    await page.goto('/admin');
    // assert canAccess
  });
}

Testing Both UI Redirect and API 401

Browser tests assert redirect to login. API tests with request.get('/api/admin') assert 401/403 without involving the UI — faster contract verification.

Common Mistakes

  • Only testing navigation path, not direct URL entry.
  • Admin storageState on tests that should start logged out.
  • Not testing client-side route guards after SPA load.
  • Ignoring API authorization — only checking UI hide/show.

Key Takeaways

  • Test unauthenticated, wrong-role, and authorized access.
  • Use storageState projects for role matrices.
  • Deep link to protected URLs every time.
  • Align UI guards with API status expectations.

Pro Tip

Run the same protected-route spec under admin and user projects — doubles coverage with zero duplicate code.