ux

Invalid submit shows a message

A UX test of client-side validation: open Sign up, click Submit with no name, and expect “Name is required”. Playwright and Cypress express the same user path.

SUT: js-ui · id: ux.form-validation

Cached CI results from 10/3/2026, 10:14:14 AM (ci · d7784ca)

Testing tool

Playwright · Browser UX / E2E runner · Apache-2.0

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.

The browser loads signup.html. Submit is clicked with an empty Name field. The page’s own JavaScript (not the HTML5 tooltip) reveals #name-error. The test asserts visibility and exact copy — that is the UX contract.

SUT: js-ui · samples/js-ui/public/signup.html · run npm test in examples/ux/form-validation/playwright

Code under test · samples/js-ui/public/signup.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Sign up · 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>
      <h1>Sign up</h1>
      <!-- novalidate: the UX test asserts our custom message, not the browser tooltip -->
      <form id="signup-form" novalidate>
        <label for="name">Name</label>
        <input id="name" name="name" autocomplete="name" />
        <p id="name-error" class="error" hidden>Name is required</p>
        <button type="submit">Submit</button>
      </form>
      <p id="success" class="success" hidden>Thanks, <span id="thanks-name"></span></p>
    </main>
    <script src="./app.js"></script>
  </body>
</html>
Test · examples/ux/form-validation/playwright/signup.spec.js · Apache-2.0 · run in examples/ux/form-validation/playwright: npm test
// Playwright browser test — empty submit must show our custom error, not a native tooltip
import { test, expect } from "@playwright/test";

test("empty submit shows Name is required", async ({ page }) => {
  await page.goto("./signup.html");
  // Click Submit without typing a name
  await page.getByRole("button", { name: "Submit" }).click();
  const error = page.locator("#name-error");
  await expect(error).toBeVisible();
  await expect(error).toHaveText("Name is required");
});