Playwright launches a real browser (Chromium here), gives you a page object, and uses locators plus expect() to assert what the user sees. Traces and auto-waiting make flaky clicks easier to debug than raw WebDriver.
webServer starts samples/js-ui. Each test opens a page, interacts through roles and CSS, and asserts URL or visible text. The SUT is HTML/CSS/JS in the browser — not an imported function.
Testing architecture
UX / E2E tests drive a real browser against samples/js-ui. The test is a user path: click a link or submit a form, then assert what appears on screen or in the URL. Failures point at markup, CSS, or client JS — not at a unit-tested helper alone.
A real Chromium (Playwright) or Electron/Chrome (Cypress) loads samples/js-ui over HTTP. The test clicks the About link like a user, then asserts the URL contains about.html and the h1 is “About this counter”. No unit-style imports — the browser is the client.
SUT: js-ui · samples/js-ui/public/index.html · run npm test in examples/ux/navigate-and-assert/playwright
Code under test · samples/js-ui/public/index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Counter · js-ui</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<header>
<!-- Primary nav: UX tests click these links and assert the next URL/heading -->
<a href="./index.html">Home</a>
<a href="./about.html">About</a>
<a href="./signup.html">Sign up</a>
</header>
<main>
<h1>Counter</h1>
<p>A tiny UI used as the system under test for UX examples.</p>
<p>Count: <output id="count">0</output></p>
<button type="button" id="increment">Increment</button>
</main>
<script src="./app.js"></script>
</body>
</html>
Test · examples/ux/navigate-and-assert/playwright/navigate.spec.js · Apache-2.0
· run in examples/ux/navigate-and-assert/playwright: npm test
// Playwright browser test — real Chromium, not APIRequest
import { test, expect } from "@playwright/test";
test("clicking About shows the about heading", async ({ page }) => {
// Load the home page served by samples/js-ui
await page.goto("./index.html");
// Click the same nav control a user would
await page.getByRole("link", { name: "About" }).click();
// URL and heading are the observable UX contract
await expect(page).toHaveURL(/about\.html/);
await expect(page.getByRole("heading", { name: "About this counter" })).toBeVisible();
});
Playwright starts samples/js-ui via webServer, then drives Chromium.
Cypress runs tests in (or beside) the browser with a chainable API: cy.visit, cy.click, cy.should. The open-source runner is enough for these demos; we do not use Cypress Cloud.
start-server-and-test boots js-ui, then cypress run visits the page, clicks, and asserts. Commands retry until the assertion passes or times out — that retry is Cypress’s default stability model.
Testing architecture
UX / E2E tests drive a real browser against samples/js-ui. The test is a user path: click a link or submit a form, then assert what appears on screen or in the URL. Failures point at markup, CSS, or client JS — not at a unit-tested helper alone.
A real Chromium (Playwright) or Electron/Chrome (Cypress) loads samples/js-ui over HTTP. The test clicks the About link like a user, then asserts the URL contains about.html and the h1 is “About this counter”. No unit-style imports — the browser is the client.
SUT: js-ui · samples/js-ui/public/about.html · run npm test in examples/ux/navigate-and-assert/cypress
Code under test · samples/js-ui/public/about.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>About · js-ui</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<header>
<a href="./index.html">Home</a>
<a href="./about.html">About</a>
<a href="./signup.html">Sign up</a>
</header>
<main>
<!-- Target heading for ux.navigate-and-assert -->
<h1>About this counter</h1>
<p>This page exists so a UI test can click a link and check the destination.</p>
</main>
</body>
</html>
Test · examples/ux/navigate-and-assert/cypress/navigate.cy.js · MIT
· run in examples/ux/navigate-and-assert/cypress: npm test
// Cypress browser test — same click → URL/heading contract as Playwright
describe("navigation", () => {
it("clicking About shows the about heading", () => {
// Load the home page served by samples/js-ui
cy.visit("/index.html");
// Click the same nav control a user would
cy.contains("a", "About").click();
// URL and heading are the observable UX contract
cy.url().should("include", "about.html");
cy.get("h1").should("have.text", "About this counter");
});
});
start-server-and-test boots js-ui, then cypress run (headed: false).