← Back

Playwright Form & Input Handling

Learn how Playwright handles text fields, passwords, checkboxes, radio buttons, dropdowns, file uploads, forms and validation with real-world automation examples.

1. What is Form & Input Field Handling?

Almost every business application contains forms. Examples include login forms, registration forms, customer creation, checkout, employee onboarding and search forms.

Text Input

Enter names, email addresses, usernames, addresses and other text values.

Password

Enter passwords securely using accessible field locators.

Dropdown

Select country, state, department, category or status.

Checkbox

Select options such as terms and conditions or permissions.

Radio Button

Select one option from a group of choices.

File Upload

Upload documents, profile images, invoices and resumes.

2. Playwright Form Handling Flow
Locate Field
→
Fill / Select
→
Submit
→
Validate Result

A professional Playwright test should model the real user workflow rather than interacting with fields using fragile CSS or XPath selectors whenever better locators are available.

3. Text Input Handling

The most common form operation is entering text. Playwright provides fill() for this purpose.

Basic Text Input
page.get_by_label("First Name").fill("John")

page.get_by_label("Last Name").fill("Smith")

page.get_by_label("Email").fill("john@example.com")
Why fill() is useful fill() clears the existing value and enters the requested text. This makes test setup straightforward and deterministic.
4. Real-Time Example — Customer Registration

Imagine a CRM application where a new customer must be registered.

Customer Form
→
Enter Details
→
Submit
→
Customer Created
Customer Registration
from playwright.sync_api import expect

page.goto("https://crm.example.test/customers/new")

page.get_by_label("First Name").fill("John")
page.get_by_label("Last Name").fill("Smith")
page.get_by_label("Email").fill("john.smith@example.com")
page.get_by_label("Phone").fill("9876543210")

page.get_by_role(
    "button",
    name="Create Customer"
).click()

expect(
    page.get_by_text("Customer created successfully")
).to_be_visible()
5. Password Field Handling

Password fields can be handled using the same accessible locator strategy.

Password Example
page.get_by_label("Password").fill("MySecret123!")

page.get_by_label(
    "Confirm Password"
).fill("MySecret123!")
Professional practice Do not hard-code real production credentials in test source code. Use environment variables, secret managers, or controlled test credentials.
6. Checkbox Handling

Use check() and uncheck() for checkbox controls.

Checkbox
page.get_by_label(
    "I agree to the Terms and Conditions"
).check()

page.get_by_label(
    "Subscribe to newsletter"
).check()

Validate Checkbox State

Assertion
from playwright.sync_api import expect

terms = page.get_by_label(
    "I agree to the Terms and Conditions"
)

terms.check()

expect(terms).to_be_checked()
7. Radio Button Handling

Radio buttons are useful when the user must select exactly one option.

Radio Button
page.get_by_label("Male").check()

page.get_by_label("Female").check()
Important Radio buttons normally represent mutually exclusive choices. Your test should verify the business result rather than only verifying that the click happened.
8. Dropdown / Select Handling

For a native HTML <select> element, Playwright provides select_option().

Select Option
page.get_by_label("Country").select_option("IN")

page.get_by_label("Department").select_option(
    label="Engineering"
)
Important distinction select_option() is for native HTML <select> elements. A custom JavaScript dropdown may behave like a button/listbox instead, in which case you should interact with its actual accessible UI.
9. Custom Dropdown Handling

Modern applications frequently use custom dropdown components. These are not always native HTML select elements.

Custom Dropdown
page.get_by_role(
    "combobox",
    name="Country"
).click()

page.get_by_role(
    "option",
    name="India"
).click()

The exact locator should match the application's accessibility structure.

10. Date Input Handling

Date fields can be handled according to the type of control used by the application.

Date Example
page.get_by_label("Date of Birth").fill(
    "1990-05-15"
)
Real-world consideration Date pickers are often custom components. If the field opens a calendar widget, interact with the calendar according to its accessible roles and labels instead of assuming it is a simple text input.
11. File Upload Handling

Playwright provides a direct API for file uploads.

File Upload
page.get_by_label("Resume").set_input_files(
    "test-data/resume.pdf"
)

Real-Time Example — Employee Onboarding

Employee Document Upload
page.goto(
    "https://hr.example.test/employees/new"
)

page.get_by_label("First Name").fill("John")
page.get_by_label("Last Name").fill("Smith")

page.get_by_label("Resume").set_input_files(
    "test-data/john-smith-resume.pdf"
)

page.get_by_role(
    "button",
    name="Create Employee"
).click()
12. Textarea Handling

Multi-line text fields can also use fill().

Textarea
page.get_by_label("Description").fill(
    "Customer requested a priority callback."
)
13. Clear Existing Input

Playwright's fill() replaces the existing value. This often removes the need for a separate clear operation.

Replace Existing Value
email = page.get_by_label("Email")

email.fill("old@example.com")

email.fill("new@example.com")

The second fill() replaces the existing value with the new value.

14. Input Handling — Avoid Fragile Selectors

❌ Avoid When Better Locators Exist

page.locator( "#root > div:nth-child(2) > input" ).fill("John")

This selector depends heavily on the page's DOM structure. A harmless UI change can break the test.

✅ Preferred

page.get_by_label( "First Name" ).fill("John")

This communicates what field the test is using and is generally easier to maintain.

15. Locator Strategy for Forms

A good locator strategy is one of the biggest factors in creating maintainable Playwright form tests.

Locator Typical Use Example
get_by_label() Form fields get_by_label("Email")
get_by_role() Buttons, checkboxes, radios, comboboxes get_by_role("button", name="Save")
get_by_placeholder() Inputs where placeholder is meaningful get_by_placeholder("Enter email")
get_by_test_id() Stable application test identifiers get_by_test_id("email-input")
locator() More specific CSS/XPath or complex locator needs locator("input[name='email']")
16. Form Validation

A professional test should validate not only that the form was filled, but also that the application responds correctly.

❌ Weak Validation

page.get_by_label( "Email" ).fill("invalid-email") page.get_by_role( "button", name="Submit" ).click() # No validation

✅ Strong Validation

page.get_by_label( "Email" ).fill("invalid-email") page.get_by_role( "button", name="Submit" ).click() expect( page.get_by_text( "Enter a valid email address" ) ).to_be_visible()
17. Real-Time Example — Checkout Form

Consider an e-commerce checkout form containing customer details, address, country, payment information and terms.

Customer Details
→
Address
→
Payment
→
Place Order
Checkout Automation
from playwright.sync_api import expect

page.goto(
    "https://shop.example.test/checkout"
)

# Customer details
page.get_by_label("First Name").fill("John")
page.get_by_label("Last Name").fill("Smith")
page.get_by_label("Email").fill(
    "john@example.com"
)

# Address
page.get_by_label("Address").fill(
    "123 Main Street"
)

page.get_by_label("City").fill(
    "Pune"
)

page.get_by_label("Postal Code").fill(
    "411001"
)

# Country
page.get_by_label("Country").select_option("IN")

# Terms
terms = page.get_by_label(
    "I agree to the Terms and Conditions"
)

terms.check()

expect(terms).to_be_checked()

# Submit
page.get_by_role(
    "button",
    name="Place Order"
).click()

expect(
    page.get_by_text("Order placed successfully")
).to_be_visible()
18. Form Handling with Web-First Assertions

Form automation becomes more robust when the test validates the result using web-first assertions.

Form Submission
page.get_by_label("Email").fill(
    "customer@example.com"
)

page.get_by_label("Password").fill(
    "secret"
)

page.get_by_role(
    "button",
    name="Sign In"
).click()

expect(page).to_have_url(
    r".*/dashboard"
)

expect(
    page.get_by_role(
        "heading",
        name="Dashboard"
    )
).to_be_visible()
19. Playwright vs Selenium — Form Handling

Both Playwright and Selenium can automate forms. The practical differences are mainly around API design, locator capabilities, synchronization and the surrounding test ecosystem.

Form Operation Playwright Selenium
Text input locator.fill() send_keys()
Checkbox check() click() / element interaction
Radio button check() click()
Native select select_option() Select helper
File upload set_input_files() send_keys() is commonly used with file inputs
Form assertions Web-first expect() assertions Assertions depend on the test framework and synchronization approach
Locator model Role, label, text, test id and locator APIs CSS, XPath and WebDriver locator strategies
Synchronization Strong automatic waiting around locator actions and web-first assertions Explicit waits are commonly used when application state requires synchronization
20. Selenium Form Example
Selenium Python
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()

driver.get(
    "https://example.test/register"
)

driver.find_element(
    By.ID,
    "firstName"
).send_keys("John")

driver.find_element(
    By.ID,
    "lastName"
).send_keys("Smith")

driver.find_element(
    By.ID,
    "email"
).send_keys("john@example.com")

driver.find_element(
    By.ID,
    "password"
).send_keys("secret")

driver.find_element(
    By.ID,
    "register"
).click()

driver.quit()
21. Same Form Using Playwright
Playwright Python
from playwright.sync_api import expect

page.goto(
    "https://example.test/register"
)

page.get_by_label(
    "First Name"
).fill("John")

page.get_by_label(
    "Last Name"
).fill("Smith")

page.get_by_label(
    "Email"
).fill("john@example.com")

page.get_by_label(
    "Password"
).fill("secret")

page.get_by_role(
    "button",
    name="Register"
).click()

expect(
    page.get_by_text(
        "Registration successful"
    )
).to_be_visible()
Practical benefit The Playwright example expresses the user's interaction directly: find the field by its accessible name, fill it, submit the form, and verify the resulting business state.
22. Why Playwright Can Be Beneficial for Form Automation

1. Accessible Locators

Locators such as get_by_label() and get_by_role() make form automation easier to understand.

2. Automatic Waiting

Playwright waits for relevant actionability conditions before many locator interactions.

3. Web-First Assertions

Form results can be validated using retrying assertions.

4. File Upload API

File input handling can be performed directly with set_input_files().

5. Modern Web Support

Playwright is designed around modern browser automation scenarios including dynamic applications.

6. Less Timing Code

A well-designed Playwright test can avoid many manual timing workarounds.

23. Avoid Hard Waits in Form Automation

❌ Avoid

page.get_by_label( "Email" ).fill("john@example.com") page.get_by_role( "button", name="Save" ).click() page.wait_for_timeout(3000) assert "success" in page.content()

✅ Preferred

page.get_by_label( "Email" ).fill("john@example.com") page.get_by_role( "button", name="Save" ).click() expect( page.get_by_text( "Customer saved successfully" ) ).to_be_visible()
Professional rule Do not guess how long the form submission needs. Wait for the actual UI or application state that proves the operation completed.
24. Complete Professional Form Example
Customer Creation Test
from playwright.sync_api import Page, expect


def test_create_customer(page: Page):

    # Arrange
    page.goto(
        "https://crm.example.test/customers/new"
    )

    # Fill customer details
    page.get_by_label(
        "First Name"
    ).fill("John")

    page.get_by_label(
        "Last Name"
    ).fill("Smith")

    page.get_by_label(
        "Email"
    ).fill("john.smith@example.com")

    page.get_by_label(
        "Phone"
    ).fill("9876543210")

    # Address
    page.get_by_label(
        "Address"
    ).fill("123 Main Street")

    page.get_by_label(
        "City"
    ).fill("Pune")

    page.get_by_label(
        "Country"
    ).select_option("IN")

    # Terms
    terms = page.get_by_label(
        "I agree to the Terms and Conditions"
    )

    terms.check()

    expect(terms).to_be_checked()

    # Submit
    page.get_by_role(
        "button",
        name="Create Customer"
    ).click()

    # Validate business result
    expect(
        page.get_by_text(
            "Customer created successfully"
        )
    ).to_be_visible()

    expect(page).to_have_url(
        r".*/customers/.*"
    )
25. Form Handling Best Practices
26. Final Mental Model
Locate
→
Fill / Select
→
Submit
→
Wait for State
→
Assert Business Result
Remember A robust Playwright form test is not simply:

Find → Type → Click

It is:

Locate with a stable locator → Enter realistic data → Submit → Validate the actual application result

This is where Playwright can provide a practical advantage for modern form automation: its locator model, actionability checks, and web-first assertions allow the test to focus more on the business workflow and less on manually managing browser timing.