Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run Scan×
Skip to content
World desk5 min

How to Test Client-Side Routing and Deep Links with Playwright

Test routes as users reach them: by direct URL, in-app navigation, reload, and supported history actions. Assert both the URL and meaningful page content.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Build the suite around the routes and behaviors your application promises—not the internals of its router. For every important route family, test direct URL entry and in-app navigation, then verify both the resulting URL and meaningful page content. Add reload, browser-history, URL-state, access, redirect, and not-found cases when they are part of the product’s actual contract.

The supplied title ends with “complex Sing,” so it does not identify a complete product or framework name. The examples below use generic project routes; adapt them to the application’s documented routes, server setup, authentication, and supported browsers.

As an Amazon Associate I earn from qualifying purchases.

Start with the route contract

Before writing tests, make a concise inventory of what users can navigate to and what each route should do. Group routes by behavior so the suite covers distinct risks without duplicating the same assertions for every URL.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Static routes: for example, a projects index or settings page.
  • Parameterized routes: for example, a project detail page whose path contains an identifier.
  • URL-driven state: query parameters, such as a selected tab, and hashes that identify an in-page target.
  • Access and exceptional outcomes: protected pages, sign-in redirects, access-denied states, invalid parameters, unknown paths, and canonical URL rules, if the application defines them.

For each route family, record the expected entry modes, URL state, access behavior, identifying content, criticality, and browser engines the product supports. This matrix becomes the basis for choosing representative coverage.

Test direct entry separately from in-app navigation

A route may work after an in-app click but fail when a user opens the same URL in a new tab or refreshes it. Those are different paths through the system: client-side navigation exercises the running application, while direct entry and reload also depend on the server and deployment handling the nested URL.

Direct URL and reload

Use page.goto to open a representative deep link in a fresh page. Check that the browser ends at the expected canonical URL and that the route’s identifying content appears. Reload the page and check the same contract again.

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

test('direct deep link renders the intended route and survives reload', async ({ page }) => {
  await page.goto('/projects/alpha?tab=activity');
  await expect(page).toHaveURL(//projects/alpha?tab=activity$/);
  await expect(page.getByRole('heading', { name: 'Project Alpha' })).toBeVisible();

  await page.reload();
  await expect(page).toHaveURL(//projects/alpha?tab=activity$/);
  await expect(page.getByRole('heading', { name: 'Project Alpha' })).toBeVisible();
});

Configure the project’s baseURL and managed server for the actual application. Server fallback behavior is deployment- and framework-specific; do not assume that a successful in-app transition proves the server will serve a nested path directly.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Navigation through the interface

Start from a stable route, locate the navigation control by its accessible role and name, and click it. Assert the destination URL and the content that identifies the destination. The two checks catch different failures: a URL can change while the wrong view renders, or the correct-looking view can render without the expected URL.

test('in-app navigation updates the URL and renders the destination', async ({ page }) => {
  await page.goto('/projects');
  await page.getByRole('link', { name: 'Project Alpha' }).click();

  await expect(page).toHaveURL(//projects/alpha$/);
  await expect(page.getByRole('heading', { name: 'Project Alpha' })).toBeVisible();
});

When a click triggers navigation, use a retrying URL assertion or wait for the specific destination with page.waitForURL. Avoid fixed sleeps: rendering and data fetching may continue after the browser’s load event, and Playwright’s locators wait for elements to become actionable.

Cover browser history and URL state

Back and forward

Test history as a user-visible sequence: navigate to a second route, go back, and then go forward where the application supports that behavior. At each stop, assert both the URL and the visible route content.

test('back and forward restore the expected route views', async ({ page }) => {
  await page.goto('/projects');
  await expect(page.getByRole('heading', { name: 'Projects' })).toBeVisible();

  await page.getByRole('link', { name: 'Project Alpha' }).click();
  await expect(page).toHaveURL(//projects/alpha$/);
  await expect(page.getByRole('heading', { name: 'Project Alpha' })).toBeVisible();

  await page.goBack();
  await expect(page).toHaveURL(//projects$/);
  await expect(page.getByRole('heading', { name: 'Projects' })).toBeVisible();

  await page.goForward();
  await expect(page).toHaveURL(//projects/alpha$/);
  await expect(page.getByRole('heading', { name: 'Project Alpha' })).toBeVisible();
});

Do not make back-forward cache (BFCache) restoration a required Playwright assertion. Playwright documents BFCache restoration testing as unsupported and warns that it may desynchronize page state.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Queries, hashes, and canonical URLs

Include query parameters or hash fragments when the application treats them as meaningful route state. Assert that navigation preserves or changes that state as specified, and check the resulting view or target. Test trailing slashes, redirects, and canonicalization only where the product defines a policy; the expected URL should reflect that policy rather than an assumed convention.

Test exceptional and protected routes only when they are contractual

For each behavior the application promises, assert the user-visible outcome rather than router functions or other implementation details.

  • Invalid or missing parameters: verify the documented error, empty state, or redirect.
  • Protected route: verify the expected sign-in or access-denied result for the relevant authentication state.
  • Redirect: verify the final URL and identifying content at the destination.
  • Unknown path: verify the not-found experience if the application provides one.

Do not add speculative cases for behaviors that are not part of the route contract. The application’s framework, authentication flow, and deployment rules are not specified here, so their exact outcomes must come from the product itself.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Keep assertions resilient and tests isolated

Prefer accessible roles, names, and labels, such as getByRole('heading', { name: 'Project Alpha' }). Use a test ID only when user-facing semantics are insufficient and the test ID is maintained as a stable application contract. Avoid CSS classes and assertions about router internals: those can change without changing what users experience.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use web-first assertions such as expect(page).toHaveURL(...) and expect(locator).toBeVisible(). They retry while the expected condition becomes true, reducing timing assumptions. Keep each test’s browser state and data independent so failures do not depend on test order. Control third-party responses that are unrelated to routing with Playwright’s network interception and fulfillment APIs where practical.

Run against a managed server and supported browsers

Use Playwright’s webServer configuration to start the application and wait for it to become available. When practical, exercise production-built code, since development-server behavior may differ from the deployed application. The exact configuration depends on the project’s framework and start command, neither of which is identified by the title.

Choose browser projects to match the product’s support commitments. If Chromium, Firefox, and WebKit are all supported, run the critical route smoke tests across those engines. A faster continuous-integration tier can cover the highest-risk route families, with broader route and browser combinations scheduled or run on pull requests according to the team’s runtime budget.

Make failures diagnosable

Retain Playwright traces or reports for failing runs. Traces can help pinpoint the action timeline, inspect DOM snapshots, and review network requests around a routing failure. This is especially useful for distinguishing a route assertion problem from a server response, data-loading, or interaction-readiness problem.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A practical suite therefore has a small set of representative tests for each distinct route behavior, with separate coverage for direct entry, reload, client navigation, and supported history behavior. Expand that set only where the application’s URL contract, access rules, or browser support makes another case meaningful.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.