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.

Playwright automates a form by treating it like a user: open the page, find each control through its accessible label or role, fill or select values, submit, and await an assertion about the result. The following pattern is reliable for registration, login, checkout, search and data-entry tests because locator actions include actionability checks and web-first assertions wait for the UI to reach the expected state.

The complete workflow

  1. Navigate to the form with page.goto().
  2. Locate controls with getByLabel(), getByRole(), or another explicit locator contract.
  3. Enter text with locator.fill().
  4. Set checkboxes, radios, dates, selects and files with the control-specific locator method.
  5. Click the submit control.
  6. Await a web-first assertion such as toHaveText(), toHaveURL(), toBeVisible() or toBeChecked().

Use a controlled staging site and deterministic data when a test creates or changes records. Keep each test isolated in its own browser context and avoid making third-party sites part of the test’s success criteria.

A runnable registration-form test

This TypeScript example uses the Playwright test runner. Replace the URL, labels and expected status text with the contracts exposed by your application.

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

test('submits a registration form', async ({ page }) => {
  await page.goto('https://example.test/register');

  await page.getByLabel('Full name').fill('Ada Lovelace');
  await page.getByLabel('Email').fill('[email protected]');
  await page.getByLabel('Plan').selectOption({ label: 'Standard' });
  await page.getByLabel('Agree to terms').check();

  await page.getByRole('button', { name: 'Create account' }).click();
  await expect(page.getByRole('status')).toHaveText(/created/i);
});

fill() focuses the field and dispatches an input event. The locator-based style makes the target and action explicit; current Playwright guidance discourages page-level page.fill() and page.selectOption() in favor of locator methods.

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

Choose locators that describe the user contract

Labels for form fields

page.getByLabel('Email') is resilient when the label is correctly associated with its input. It also documents what the test means. Prefer the visible label over a generated class name or a long CSS path.

Roles for buttons and status messages

Use getByRole('button', { name: 'Create account' }) for a submit button and getByRole('status') for a live success message. Role and accessible-name locators test the same interface a keyboard or assistive-technology user receives.

When a locator needs refinement

If a page contains two “Email” fields, scope the locator to a form or dialog with locator(), then use getByLabel() inside that region. A CSS or test-id locator can be appropriate when no meaningful accessible contract exists, but keep it short and owned by the application rather than tied to presentation details.

Fill text, date and contenteditable controls

fill() supports standard inputs, textareas and [contenteditable] regions. Date, time and local-datetime inputs must receive the format required by the control, for example an ISO-style date value accepted by the page:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.getByLabel('Start date').fill('2026-10-15');
await page.getByLabel('Notes').fill('Ship after approval.');

Do not simulate individual keystrokes unless the application specifically requires keyboard events. For a masked field that reacts to each key, use pressSequentially() only when the component’s behavior makes it necessary, then assert the rendered value.

Checkboxes and radio buttons

Use check() for a checkbox or radio, uncheck() to clear a checkbox, and setChecked() when the desired state is computed.

Rank #2
TypeScript Programming Language - Software Engineer & Coder T-Shirt
  • TypeScript implements a superset of syntax for strictly typed development, facilitating deep static analysis and enhanced development environment integration. The compiler translates source into standard script formats, ensuring parity across any runtime.
  • TypeScript is ideal for front-end developers, full-stack engineers, and software architects who build large-scale web applications. It serves those looking to improve code excellence, reduce bugs through static checking, and maintain complex projects more.
  • Lightweight, Classic fit, Double-needle sleeve and bottom hem
const terms = page.getByLabel('Agree to terms');
await terms.check();
await expect(terms).toBeChecked();

await page.getByLabel('Business account').check();
await page.getByLabel('Receive product updates').uncheck();

The state assertion is useful when being checked is itself the requirement, not merely a step before submission.

Native select elements and custom comboboxes

Native HTML select

For a real <select>, use selectOption() by value, label or option object. Pass an array for a multiple select.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.getByLabel('Plan').selectOption({ label: 'Standard' });
await page.getByLabel('Regions').selectOption(['us-east', 'eu-west']);

Custom dropdown widgets

A styled component may be a button, combobox or listbox rather than a native select. Open it through its visible role, choose the exposed option, and assert the displayed selection:

await page.getByRole('combobox', { name: 'Plan' }).click();
await page.getByRole('option', { name: 'Standard' }).click();
await expect(page.getByRole('combobox', { name: 'Plan' }))
  .toHaveText('Standard');

The exact roles depend on the component’s accessibility implementation. Inspect the rendered roles instead of assuming that a CSS selector such as .dropdown-item represents an option.

Upload one or more files

Target an input[type=file] and call setInputFiles(). It accepts a path, multiple paths, a directory where supported, or an in-memory file object.

await page.getByLabel('Profile photo').setInputFiles('tests/fixtures/avatar.png');
await page.getByLabel('Attachments').setInputFiles([
  'tests/fixtures/invoice.pdf',
  'tests/fixtures/receipt.pdf'
]);

// Clear the file selection
await page.getByLabel('Attachments').setInputFiles([]);

Keep fixture files in the test project and assert any filename or upload-status text that the application shows. A browser cannot bypass server-side file-size, type or malware rules; test those validations with suitable fixtures.

Free tools Windows power users keep installed

One-click scans. No signup required.

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

Submit and verify the outcome

Click the button only after the required controls are set. Playwright automatically waits for actionability before performing an action, so fixed sleeps are usually unnecessary. Replace arbitrary delays with an assertion that describes the post-submit contract.

await page.getByRole('button', { name: 'Create account' }).click();
await expect(page.getByRole('status')).toHaveText(/created/i);
// Or, for a redirect:
await expect(page).toHaveURL(//welcome/);

Use toBeVisible() for a confirmation panel, toHaveText() for a message, toHaveURL() for navigation, and toBeChecked() for a persisted checkbox state. These assertions retry until the configured timeout and produce a useful failure when the expected state never appears. A one-time isVisible() check followed by an immediate assertion can race the application.

Login forms and authenticated state

For a login flow, fill username and password by label, click the sign-in control, then verify an authenticated landmark or URL.

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

test('signs in', async ({ page }) => {
  await page.goto('https://example.test/login');
  await page.getByLabel('Username').fill(process.env.TEST_USER!);
  await page.getByLabel('Password').fill(process.env.TEST_PASSWORD!);
  await page.getByRole('button', { name: 'Sign in' }).click();
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});

For a suite with many authenticated tests, save the signed-in state once and reuse it rather than repeating login in every test. Treat that state file as a credential: protect it from source control and from logs. Create isolated contexts for tests that mutate data. Depending on the application, authentication may involve cookies, local storage, IndexedDB or passkeys; preserve the mechanism the application actually uses.

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

Synchronization, isolation and data design

Prefer state-based waiting

Actions wait for visibility, stability, event reception and enabled state. After an action, wait for the resulting UI or navigation with an assertion. A fixed waitForTimeout() makes tests slower when the page is fast and flaky when the page is slow.

Control your data

Use unique, disposable records or reset fixtures between tests. A test that depends on a previously created account can fail for reasons unrelated to the form. Staging endpoints, stubbed email delivery and deterministic server responses make validation repeatable.

Keep third parties outside the critical path

Playwright’s best-practice guidance favors testing behavior you control. If a form embeds a payment, identity or marketing provider, test your integration boundary with a controlled substitute and maintain a smaller, separately monitored check for the provider itself.

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

Common failures and precise fixes

“Locator resolved to zero elements”

The label or role does not match the rendered accessible tree, the form is inside an iframe, or the control appears only after another action. Inspect the page, correct the accessible name, wait for the preceding state with an assertion, or use frameLocator() for a genuine iframe.

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

Strict-mode violation

More than one element matches. Scope the locator to the relevant form or dialog, or make the accessible name unique. Avoid solving this with an arbitrary nth() unless order is part of the contract.

“Element is not a select”

selectOption() works only with a native select. Operate the custom combobox through its button, listbox and option roles instead.

Checkbox cannot be checked

The label may target a hidden input while a custom element handles the interaction, or an overlay may block it. Locate the exposed checkbox role, ensure the component is enabled, and assert toBeChecked() after the action.

Upload does nothing

Confirm that the locator targets an input of type file, use a path that exists in the test environment, and check application-side type or size validation. Clear a prior selection with an empty array before retrying.

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

Submit assertion times out

Capture the actual validation message, network response and URL. The form may reject the data, require a hidden field, submit inside an iframe, or show a toast with a different role. Assert the documented success state rather than assuming that a click means success.

Tests pass locally but fail in CI

Use stable fixture data, avoid shared accounts, increase the assertion timeout only for a known slow environment, and retain traces or screenshots on failure. Do not mask a race with a global sleep.

Or skip the browser setup

If your goal is a clean page image rather than interaction testing, ScreenshotNeo returns a screenshot or PDF from one request. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the page verdict and billing result in headers. Its MCP server provides take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo documentation for the 63 capture options, including full-page lazy-image loading, CSS-selector element capture, device and retina settings, PDF layout, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, geolocation, resizing, TTL caching, signed image links, asynchronous webhooks, bulk capture and usage reporting. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000.

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

Create a free ScreenshotNeo account to start with 1,000 screenshots a month and no card.

Frequently Asked Questions

Can Playwright submit a form without clicking a button?

Yes. You can trigger the page’s submit behavior with the application’s keyboard or DOM contract, but clicking the user-facing submit control is generally clearer and exercises validation and accessibility behavior together.

How do I test a form that opens a new page after submission?

Wait for the popup or new page while performing the action, then assert its URL or visible result. Keep the wait tied to the click so the test cannot miss a fast-created page.

Should I use API requests instead of browser form automation?

Use browser automation when you need to verify the rendered, user-visible workflow. Use direct API calls for setup, teardown or service-level tests when the UI is not part of the behavior under test.

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

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.