DEV Community

Styrow.dev
Styrow.dev

Posted on Originally published at styrow.dev

How would you design a robust Playwright E2E test suite to verify client-side routing and deep linking in a complex Sing

🔥 Playwright Challenge of the Day:
How do you robustly verify complex client-side routing and deep linking in an SPA with dynamic content and varying auth?

📌 Problem Statement
Complex Single-Page Applications (SPAs) make E2E testing routing a challenge.
• Authentication Overhead: Re-logging in for every test slows down suites and increases flakiness.
• Dynamic URLs: Routes with parameters (e.g., /users/{id}/profile) require careful handling.
• Async Content Loading: SPA content loads asynchronously; tests must wait for full render.
• Data Dependencies: Setting up specific backend data via UI before each test is inefficient.
• Client-Side Redirections: Verifying internal redirects (e.g., / to /dashboard if authenticated) is crucial.

💡 Solution & Code Walkthrough
A production-grade Playwright solution combines session persistence, direct navigation, API-based state manipulation, and resilient waiting strategies.

✅ Persistent Authentication via storageState: Log in once, save the browser state (cookies, local storage), and reuse it across multiple tests. This dramatically reduces setup time.

// playwright/auth.setup.ts
import { test as setup, expect } from '@playwright/test';
const authFile = 'playwright/.auth/user.json';

setup('authenticate', async ({ page }) => {
  await page.goto('/login');
  await page.fill('#username', 'testuser');
  await page.fill('#password', 'password123');
  await page.click('button[type="submit"]');
  // Ensure a successful login indicator is visible
  await expect(page.locator('.user-profile')).toBeVisible();
  await page.context().storageState({ path: authFile });
});
Enter fullscreen mode Exit fullscreen mode

Then, configure playwright.config.ts to use authFile in your test projects to load the session state.

✅ Direct Navigation & API Seeding: Navigate directly to deep links (e.g., /products/123). For data-dependent routes, use Playwright's page.request to seed test data via API calls before navigating, bypassing slow UI setup.

✅ Resilient Assertions: Utilize page.waitForURL() for client-side route changes and locator.waitFor() or expect(locator).toBeVisible() to ensure dynamic content related to the route is fully rendered. Avoid brittle page.waitForTimeout().

🔑 Key Takeaways
• Prioritize storageState for efficient authentication management in SPAs, boosting speed.
• Leverage direct URL navigation and API requests for fast, reliable data setup.
• Employ Playwright's robust waiting mechanisms for accurate async content verification.
• Always assert the final URL or specific elements to confirm successful routing and redirects.

❓ Quick Summary Q&A
Q: Why is storageState crucial for SPA routing tests?
A: It saves and reuses login sessions, drastically speeding up tests and reducing flakiness.
Q: How to handle dynamic URLs like /users/{id}?
A: Construct the full URL with specific IDs, then navigate directly.

TAGS: playwright, e2e testing, spa, routing, authentication, automation

────────────────────────────────────────
Improve your dev skills daily. Download our app!
────────────────────────────────────────

📲 𝐅𝐑𝐄𝐄 𝐌𝐎𝐁𝐈𝐋𝐄 𝐀𝐏𝐏 — 𝟔𝟎𝟎+ 𝐒𝐃𝐄𝐓 𝐐&𝐀𝐬
Practice real-world interview scenarios offline on the free QA Automation & SDET Prep app:

🤖 𝐆𝐨𝐨𝐠𝐥𝐞 𝐏𝐥𝐚𝐲 (𝐀𝐧𝐝𝐫𝐨𝐢𝐝):
https://play.google.com/store/apps/details?id=com.app.seleniuminterviewquestions&referrer=utm_source%3Ddevto%26utm_medium%3Darticle%26utm_campaign%3Dselenium_20260926

🍎 𝐀𝐩𝐩 𝐒𝐭𝐨𝐫𝐞 (𝐢𝐎𝐒):
https://apps.apple.com/app/id6786760948?pt=128640464&ct=devto_selenium_20260926&mt=8

────────────────────────────────────────
────────────────────────────────────────

Top comments (0)