intermediate19 min

Navigation and Form Interaction

What page.goto actually waits for, and the fill/select/check vocabulary Playwright provides for real, form-specific interaction.

What you'll learn

  • Explain what load state page.goto waits for by default and when to wait for a different one
  • Choose the correct form-interaction method (fill, selectOption, check) for a given input type
  • Write an assertion sequence validating both a successful and a failed form submission

Prerequisites

Explanation

await page.goto(url) navigates and, by default, waits for the load event — every resource (images, stylesheets, scripts) has finished loading. That default is frequently more waiting than a test actually needs: for a page whose interactive content is ready well before every image finishes downloading, waiting for load needlessly slows the test down. page.goto(url, { waitUntil: "domcontentloaded" }) waits only for the initial HTML to be parsed — faster, and usually sufficient, since Playwright's own auto-waiting (previous lesson) will separately wait for whatever specific element an action or assertion actually needs, regardless of which waitUntil option goto used.

Form interaction has a specific, honest vocabulary matched to each input's real behavior, and using the wrong method produces a working-looking test that doesn't actually simulate a real user: locator.fill(text) sets a text input's or textarea's value directly (fast, reliable, the right default for text entry); locator.selectOption(value) chooses an option in a <select> dropdown by value, label, or index; locator.check()/.uncheck() sets a checkbox or radio button to a specific state (idempotent — calling .check() on an already-checked box is a safe no-op, unlike .click(), which would toggle it); locator.click() remains correct for buttons and other genuinely click-driven elements, but is the wrong tool for setting a checkbox's state deliberately, precisely because a second accidental click would silently undo the first.

A complete form-interaction test validates more than just "the happy path submits" — it should assert the failure path too: submitting with invalid or missing data should produce the correct validation message, and the form should not silently succeed or navigate away when it shouldn't. await expect(page.getByText("Email is required")).toBeVisible() after submitting an empty required field is exactly as important a test as the successful-submission case, and skipping it is a common, easy-to-miss gap — a form's happy path passing tells you nothing about whether its validation actually works.

Example

Modeling waitUntil options and the fill/selectOption/check vocabulary as data, matching real Playwright method choices to real input types.

function waitUntilCost(option) {
  const relativeCost = { load: 3, domcontentloaded: 1, networkidle: 5 };
  return relativeCost[option] ?? 0;
}
console.log(waitUntilCost("domcontentloaded") < waitUntilCost("load")); // true -- generally faster

function chooseFormMethod(inputType) {
  const methodFor = {
    text: "fill",
    textarea: "fill",
    select: "selectOption",
    checkbox: "check",
    radio: "check",
    button: "click",
  };
  return methodFor[inputType] ?? "unknown";
}
console.log(chooseFormMethod("checkbox")); // "check" -- idempotent, not "click"
console.log(chooseFormMethod("text"));     // "fill"

Try it yourself

Add a 'button' entry check and confirm chooseFormMethod correctly returns 'click' for it, not 'fill'.

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Guided exercise

Guided exercise

Write chooseFormMethod(inputType) mapping 'text'/'textarea' -> 'fill', 'select' -> 'selectOption', 'checkbox'/'radio' -> 'check', 'button' -> 'click'. Return 'unknown' for anything else.

Checks: text inputs use fill · checkboxes use check, not click · select dropdowns use selectOption · unrecognized types return unknown

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Independent exercise

Independent exercise

Write validateFormSubmission(fields) where fields is an object like {email: '', password: 'abc'}. Return an array of field names that are EMPTY (falsy/empty-string), modeling which required-field validation messages a complete test should assert are visible after submitting incomplete data.

Checks: correctly identifies multiple empty fields · correctly identifies no empty fields when all are filled

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Common mistakes

  • Using .click() to toggle a checkbox instead of .check()/.uncheck() -- a second accidental click silently undoes the first; check()/uncheck() are idempotent and state the intent explicitly.
  • Always waiting for the default 'load' event when 'domcontentloaded' would be sufficient and faster -- unnecessary waiting adds up across a large suite with no correctness benefit.
  • Testing only the successful form-submission path -- a form's validation logic is exactly as important to test as its happy path, and skipping it leaves real bugs (broken required-field checks, wrong error messages) completely uncovered.

Knowledge check

Knowledge check

1. What does page.goto(url) wait for by default?
2. Why does Playwright provide .check() as a distinct method from .click() for checkboxes?
3. A form test only verifies that valid data submits successfully. What's missing?

Takeaway

Choose the waitUntil option that matches what the test actually needs (domcontentloaded is often enough), use the form-interaction method matched to each input's real semantics (fill/selectOption/check, not a blanket click), and always test the failure/validation path alongside the happy path.

Summary

page.goto's default waitUntil ('load') waits for every resource; 'domcontentloaded' is often faster and sufficient. fill/selectOption/check/click each match a specific input type's real interaction model — check()/uncheck() are idempotent, unlike click(). A complete form test validates both successful submission and validation failures.

References

Your notes

Notes save automatically.

Finished this lesson?

Mark it complete to track your progress and schedule a future review.