Skip to main content

Submitting Forms with Playwright

Playwright Guide: Submitting Forms

Automating form submission is pivotal for web scraping and browser testing scenarios. Playwright provides flexible methods to interact with forms and input elements.

In this guide, we'll cover everything you need to know to master form submission with Playwright, from basic form interactions to handling dynamic inputs and form validation.

If you prefer to follow along with a video then check out the video tutorial version here:


TLDR: Submitting Forms with Playwright

Using locator.fill is the easiest way to fill out form fields in playwright. Here is a quick example to fill and submit a login form that has inputs with a "User Name" and "Password" label:

// Fill username text input
await page.getByLabel("User Name").fill("John");

// Fill password text input
await page.getByLabel("Password").fill("secret-password");

// Click sign in button
await page.getByRole("button", { name: "Sign in" }).click();

The key steps are:

  1. Find the elements using locators.
  2. Populate input values using locator.fill()
  3. Trigger form submission by clicking the button

With those steps in mind, we can see in the above code, we are finding our text inputs by label. Then we use fill to set the value of the input and finally we find the submission button by the button role with the Sign in text and click it.


Understanding HTML Forms

The HTML <form> element allows you to capture user input. It contains interactive controls like:

  • <input> - Text fields, checkboxes, radio buttons
    • There are varying input types. Some examples are text, password, email, date, checkbox, radio. For a comprehensive list of input types, read the MDN Documentation.
  • <select> - Drop down select menus
  • <button> - Buttons to submit

Within the form element, The action attribute specifies the URL to submit the data to and the method attribute specifies the HTTP method that will be used for submission.

Here is an example of an HTML form:

<form action="/submit" method="post">
<label for="fname">First name:</label><br />
<input type="text" id="fname" name="fname" /><br />
<label for="lname">Last name:</label><br />
<input type="text" id="lname" name="lname" /><br />
<label for="email">Email:</label><br />
<input type="email" id="email" name="email" /><br />
<input type="submit" value="Submit" />
</form>

You can see in the above HTML, we have the form element with the action and method attributes as discussed. From this we can infer this form will be POSTing data to the /submit endpoint. Continuing with the HTML, we can see a series of labels and inputs within the form element. The labels have no impact on the submission. The inputs do though. You can see each input specifies a type and a name attribute. These are important for the form as they specify the type of the input (i.e email, phone, date, text) and the name of the field when the form submits.

Here are some common types of form input types that you may encounter when writing automation scripts for form submission:

CategoryInput TypeDescription
Basic Input TypestextSingle-line text input.
passwordSecure password input.
searchSearch query input.
telTelephone number input.
urlURL entry with protocol validation.
emailEmail address input.
Numeric Input TypesnumberNumeric input with restrictions.
rangeSlider for selecting numeric values within a specified range.
Date and Time TypesdateCalendar-based date input.
timeInput for specifying a specific time.
datetime-localCombined date and time input in a localized format.
monthSelection of a specific month.
weekSelection of a specific week.
Selection Input TypescheckboxBinary option that can be checked or unchecked.
radioSelection of a single option from a group.
File Input TypefileAllows users to upload files.
Button TypesbuttonClickable button without a predefined action.
submitTriggers the form submission process.
resetResets form fields to default values.
Miscellaneous TypescolorSelection of a color from a palette.
hiddenStores data on the client-side without display.
imageFunctions as a submit button with an image representation.

Submitting Forms with Playwright

Playwright offers a range of methods that can be effectively combined to automate form submissions. Submitting forms using Playwright involves interacting with web elements such as text fields, inputs, buttons, dropdowns, and checkboxes.

Before delving into these methods, let's first explore how to inspect a form on a website using the Chrome or Firefox DevTools.

Inspecting Forms Using DevTools

Inspecting forms involves identifying the HTML elements that constitute the form fields, buttons, and other elements necessary for interaction.

We can use Developer Tools in Chrome/Firefox to inspect forms:

Inspect form elements

This helps obtain the necessary selectors and information to target elements and use them from puppeteer. We can do this by examining unique identifying factors of the inputs, like the IDs and names. Sometimes you may see classes, elements or other attributes that can also be used to identify elements.

In the above screenshot for example, we can note the first name and last name input both have unique ID values that are easy to use. Even if they didn't though, they have unique label elements that Playwright can also use.

Automating Forms

To automate form inputs and clicks using Playwright, you'll need to identify the form elements on the webpage and interact with them programmatically.

The main methods are:

  • locator.fill() - Populates text input values.
  • locator.click() - Clicks buttons and controls.
  • locator.setChecked() - Checks checkboxes/radio buttons.
  • locator.selectOption() - Selects items in a drop down.

When dealing with basic input fields, fill is the best option.

For elements that require mouse clicks, like buttons, the click method should be used.

For checkboxes and radio buttons, the setChecked method can be used to select an option.

Finally, for dropdowns, the selectOption can be used to choose an option.

Form Validation

Form validation is a crucial aspect of automating form submissions with Playwright. It involves checking the data entered into a form to ensure it adheres to certain rules and constraints. This could be as simple as checking if a required field is filled, or more complex like verifying the format of an email address or the strength of a password.

In the context of Playwright, form validation can be used to ensure that the data being entered programmatically into the form is correct and valid. This can help catch errors early in the automation process, before the form is submitted.

Validating Form Input Before Submission

Before submitting a form with Playwright, you can validate the form inputs by reading the values and checking them against your validation rules.

Here's an example of how you can do this:

// Fill the form
await page.fill("#firstName", "John");
await page.fill("#lastName", "Doe");
await page.fill("#email", "john.doe@example.com");

// Validate the form inputs
let firstName = await page.inputValue("#firstName");
let lastName = await page.inputValue("#lastName");
let email = await page.inputValue("#email");

if (!firstName || !lastName || !email.includes("@")) {
console.log("Form validation failed");
return;
}

// If validation passes, submit the form
await page.click("#submitButton");
  • In this example, the form inputs are validated by checking if the first name and last name are not empty, and if the email contains an '@' symbol.
  • If the validation fails, the form submission is aborted.
  • We do this by grabbing the value of the actual input elements after using fill to give input.

Capturing and Analyzing Responses

After submitting a form, it's crucial to analyze the response to verify if the submission was successful, extract any returned data, or handle any errors that may have occurred. Playwright provides several methods to help with this.

On success:

When a form is successfully submitted, the server often sends a response that includes a success message or some data. Here's how you can handle this in Playwright:

// Submit form
await page.click('input[type="submit"]');

// Wait for navigation after submit
await page.waitForNavigation();

// Check if success message is displayed
const success = await page.$(".success-message");

if (success) {
console.log("Form submitted successfully!");

// Can also extract data from success page
const result = await page.textContent(".result");
console.log("Result:", result);
}
  • In this example, after the form is submitted, we wait for the page to navigate to the success page using page.waitForNavigation(). Note, we can do this because forms cause the browser to trigger a navigation event.
  • Then, we check if an element with the class "success-message" exists on the page. If it does, we log a success message to the console.
  • Additionally, we extract some data from an element with the class "result" and log it to the console. This could be any data that the server returns after a successful form submission. Or even data on a dashboard after successful login.

On failure:

If a form submission fails, the server might send an error message. Here's how you can handle this in Playwright:

try {
await page.click('input[type="submit"]');
} catch (error) {
// Check for error message
const errorMessage = await page.$(".error-message");

if (errorMessage) {
console.error("Form submission failed!");
console.error(await errorMessage.textContent());
}
}
  • In this example, we try to submit the form.
  • If an error occurs during submission, we catch it and check if an element with the class "error-message" exists on the page.
  • If it does, we log an error message to the console, along with the text of the error message.

By analyzing responses in this way, we can ensure that our Playwright scripts handle both successful and failed form submissions appropriately, making them more robust and reliable.


Handling Form Elements

Handling form elements with Playwright involves using Playwright's methods to interact with HTML form elements such as text inputs, checkboxes, radio buttons, dropdowns, and buttons on a web page.

Here's a breakdown of how to handle various types of form elements using Playwright:

Text Inputs:

Handling text inputs with Playwright involves filling in text fields on a web page.

await page.getByLabel("Password").fill("secret-password");

In the above example, page.getByLabel("Password") selects the form input element associated with the label "Password". The fill method is then used to enter the text "secret-password" into this input field. This simulates a user typing "secret-password" into the password field of the form.

Checkboxes:

Checkboxes are form controls that allow users to select one or more options from a set. In Playwright, you can check or uncheck a checkbox using the setChecked method.

await page.getByLabel("I agree to the terms above").setChecked(true);

In the above example, page.getByLabel("I agree to the terms above") selects the checkbox associated with the label "I agree to the terms above". The setChecked method is then used to check this checkbox. The argument true passed to setChecked indicates that the checkbox should be checked. If you want to uncheck the checkbox, you can pass false to setChecked.

Radio Buttons

Radio buttons are similar to checkboxes, but they are used when you want the user to select exactly one option from a set. In Playwright, you can select a radio button using the check method.

Here's a practical example:

// Select a radio button
await page.check("#paymentMethodCreditCard");

In the above example, #paymentMethodCreditCard is the CSS selector of the radio button. The check method selects the radio button. This simulates a user clicking on the radio button to select it. Note that unlike checkboxes, radio buttons cannot be unchecked by clicking on them again, so there is no uncheck method for radio buttons.

await page.getByLabel("Choose a color").selectOption("blue");

The above example finds a drop down with the "Choose a color" label and selects the option with the value or label of "blue."

We can also use CSS selectors and other selectOption arguments.

// Select by value
await page.selectOption("#dropdown", "value1");

// Select by label
await page.selectOption("#dropdown", { label: "Option Label" });

// Select by index
await page.selectOption("#dropdown", { index: 2 });

In the above code we are using the #dropdown selector but you can see there are multiple methods to choose a value. The first is by directly using the value. Then we use the dropdown label rather than value. And finally we can also use the index (position) of the value as well.

Date & Calendar Inputs

Date and calendar inputs are form controls that allow users to select a date from a calendar picker or enter a date in a specific format. In Playwright, you can set the value of a date input using the fill method.

Here's a practical example:

// Set a date input
await page.fill("#startDate", "2022-12-31");

In the above example, #startDate is the CSS selector of the date input. The fill method sets the value of the date input to '2022-12-31'. This simulates a user entering a date into the date input field.

Note that the date must be in the format 'yyyy-mm-dd'. This is the format that HTML date inputs use.

File Uploads

Handling file uploads with Playwright involves interacting with file input elements on a web page to select and upload files.

await page.setInputFiles("#file", "./image.png");

The above element finds a file input with the #file CSS selector and uploads the ./image.png to it.

Content Editable Fields (Like Rich Text Editors):

Filling in rich text editor fields with Playwright can be a bit more complex than dealing with standard HTML input fields. This is because rich text editors often use content-editable divs or iFrames, rather than simple input or textarea elements. However, you can still interact with these elements using Playwright.

Here are some strategies to handle rich text editors:

  1. Directly Setting Inner HTML:

If the rich text editor is based on a content-editable div, you can set its HTML content directly. For example:

await page.evaluate(() => {
document.querySelector("selector").innerHTML = "Your rich text content here";
});

Replace 'selector' with the CSS selector of the content-editable element.

  1. Using Keyboard Inputs:

If the editor mimics more complex behavior or if you need to emulate specific user actions (like typing), you can use keyboard inputs:

await page.click("selector"); // Focus on the editor
await page.keyboard.type("Your rich text content here");

This method simulates more closely how a user would input text.

  1. Dealing with iframes:

If the rich text editor is embedded within an iframe, you first need to switch context to the iframe and then interact with the content-editable element:

const frame = await page.frame("frameSelector");
await frame.evaluate(() => {
document.querySelector("selector").innerHTML = "Your rich text content here";
});

Replace 'frameSelector' with the selector or name of the iframe, and 'selector' with the selector of the content-editable element within that iframe.

Submitting the Forms

In Playwright, there are several ways to submit a form. The most straightforward way is to simulate a click on the submit button:

await page.click("#submitButton");

This line of code will find the element with the id "submitButton" and simulate a click event on it.

Alternatively, you can directly submit the form using JavaScript's native submit method. This can be done within Playwright's evaluate function, which allows you to run arbitrary JavaScript in the context of the page:

await page.evaluate(() => {
document.myForm.submit();
});

In this example, document.querySelector('form') selects the form element, and .submit() submits the form.

Another method is to simulate the "Enter" key press, which can also trigger a form submission in most cases:

await page.press("Enter");

Here, we're selecting the form and simulating an "Enter" key press.


Practical Example: Text Inputs

The above sections have discussed a lot about how you might utilize playwright to submit a form but next we will cover a practical example so you can see how it actually works.

First, we will fill out this Subscription Form with the following code.

const { chromium } = require("playwright");

(async () => {
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext();
const page = await context.newPage();

await page.goto("https://formsmarts.com/form/mfe?mode=h5");

await page.getByLabel("First Name: ").fill("John");
await page.getByLabel("Last Name: ").fill("Doe");
await page.getByLabel("Email Address: ").fill("jdoe@gmail.com");
await page.getByLabel("Country:").selectOption("United Kingdom");
await page
.getByLabel("6-Month Subscription Save $7.94 ($52 USD/6 Months)")
.setChecked(true);
await page
.getByLabel("Claim a FREE 7-day Trial Subscription ($0 USD/7 Days)")
.setChecked(true);

await page.getByText("Continue").click();

await page.screenshot({ path: "subscribe-demo.png" });
await browser.close();
})();

As a result, we can see our information entered on the confirmation screen below. Subscribe Form Confirmation

There's a couple key points here,

  • We are using getByLabel and getByText rather than CSS selectors.
  • For the radio button and checkbox we use the label of the control rather than the label of the section (i.e "Choose a Subscription")

Practical Example: Files and Selectors

As a further example, we will use CSS selectors to fill out this File Upload Form.

const { chromium } = require("playwright");

(async () => {
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext();
const page = await context.newPage();

await page.goto("https://formsmarts.com/form/lqh?mode=h5");

await page.fill("#u_uhD_122619", "John Doe");
await page.fill("#u_uhD_122620", "jdoe@gmail.com");
await page.setInputFiles("#u_uhD_122621", "./subscribe-demo.png");
await page.fill("#u_uhD_123744", "This is a test message.");
await page.click(".button-primary");

await page.screenshot({ path: "file-demo.png" });
await browser.close();
})();

After the execution we see our screenshot produces the confirmation screen with all of our inputs File Form Submission

We learn some new things from this example:

  • CSS Selectors: We are using the action methods (fill, click, etc) directly on the page variable which allows us to pass CSS selectors rather than label text. it is worth noting, this form is rather simple because all of the elements have unique IDs and classes. Real forms will likely be more comp
  • File Input Upload: we use the setInputFiles method which can be used on the page variable or a locator.

Handling Confirmation Dialog Boxes

Sometimes, when submitting a form, a user may be faced with a browser Message Box / Dialog. Fear not though, playwright gives you a way to handle these. You can use the dialog event to respond to them.

page.on("dialog", async (dialog) => {
console.log("Dialog message:", dialog.message());
// To accept the dialog, or use dialog.dismiss() to cancel it.
await dialog.accept();
});

This code listens for the "dialog" event, and when a dialog appears, it automatically accepts it by calling dialog.accept(). This is equivalent to clicking the "OK" or "Yes" button in the dialog.


Handling CAPTCHAs

Captcha is a security feature used by many websites to ensure that the user is a human and not a bot. However, this can pose a challenge when automating form submissions with Playwright. One way to handle captchas is by using a captcha solving service like 2captcha.com.

Here's a practical example:

const axios = require("axios");

// Get the sitekey from the reCAPTCHA element
const sitekey = await page.$eval(".g-recaptcha", (el) => el.dataset.sitekey);

// Get the current page URL
const pageUrl = page.url();

// Send a request to 2captcha.com with the sitekey and page URL
const response = await axios.get("https://2captcha.com/in.php", {
params: {
key: "YOUR_2CAPTCHA_API_KEY",
method: "userrecaptcha",
googlekey: sitekey,
pageurl: pageUrl,
},
});

// Get the captcha ID from the response
const captchaId = response.data.split("|")[1];

// Wait for 2captcha.com to solve the captcha
let captchaText;
do {
await new Promise((r) => setTimeout(r, 5000)); // wait for 5 seconds

const res = await axios.get("https://2captcha.com/res.php", {
params: {
key: "YOUR_2CAPTCHA_API_KEY",
action: "get",
id: captchaId,
},
});

const data = res.data.split("|");
if (data[0] === "OK") {
captchaText = data[1];
}
} while (!captchaText);

// Fill the reCAPTCHA response textarea with the solved captcha text
await page.evaluate((captchaText) => {
document.getElementById("g-recaptcha-response").value = captchaText;
}, captchaText);

// Now you can submit the form
await page.click("#submitButton");

In this example, we first get the sitekey from the reCAPTCHA element on the page and the current page URL. We then send a request to 2captcha.com with the sitekey and page URL to get the captcha solved. We periodically check if the captcha has been solved, and once it's solved, we fill the reCAPTCHA response textarea with the solved captcha text.

Remember to replace 'YOUR_2CAPTCHA_API_KEY' with your actual 2captcha API key. Also, the selector '.g-recaptcha' and '#submitButton' should be replaced with the actual selectors in your webpage.


Best Practices

When automating form submissions with Playwright, adhering to best practices ensures the reliability and maintainability of your automation scripts.

Here are some best practices for submitting forms in Playwright:

  • Avoiding Bot Detection: The "honeypot" or "hidden CAPTCHA" method involves using hidden fields (<input type="hidden">) in forms to detect bots. Add a hidden input field to your form that should remain unfilled by human users. A bot might inadvertently fill this field. In your Playwright script, ensure that this field remains empty when automating form submissions. If the hidden field is populated, the form submission might be flagged as automated.

  • Handling Errors on Form Submission Failure: It's crucial for your Playwright script to handle errors like empty input fields, incorrect email formats, or invalid usernames and passwords during form submission. Use Playwright’s capabilities to check for error messages or indicators that appear when a form submission fails, and then adjust the script accordingly to handle these cases.

  • Overwriting Text in Input Fields: In Playwright, to overwrite existing text in input fields (possibly due to auto-fill or previous sessions), use page.fill(selector, value). This method clears the field before typing, which is a more efficient way than triple-clicking and typing over the selected text. Example: await page.fill('selector', 'new text'); Replace 'selector' with the appropriate selector and 'new text' with the text you want to input.

  • Multi-Step forms: When automating multi-step forms that span multiple pages, use Playwright’s page.waitForNavigation() method in conjunction with actions that trigger navigation. This ensures your script waits for the navigation to complete before proceeding to the next step. Example:

    const [response] = await Promise.all([
    page.waitForNavigation(), // Waits for the next navigation
    page.click("submit-button-selector"), // Triggers navigation
    ]);

Replace 'submit-button-selector' with the selector for the form's submit button. This approach helps in synchronizing the script with the multi-step form’s flow. Proper form handling is vital for automation. Playwright provides the building blocks to emulate user interactions at scale.

You're absolutely right, my apologies. Here is the missing troubleshooting section:


Troubleshooting

Here are some common issues faced when automating forms in Playwright and potential solutions:

Issue 1: Element Not Found

Issue: Playwright cannot find an element to interact with.

Solution: Ensure the correct locators (ID, name, XPath, CSS selectors) are used. Wait for the element to be present or visible using explicit waits:

await page.waitForSelector("#elementID");

Or implicit waits:

await page.setDefaultTimeout(5000); // 5 sec implicit wait

Issue 2: Element Not Interactable

Issue: The element is present but not in an interactable state.

Solution: Wait until the element is visible and enabled:

await page.waitForSelector("#elementID", { visible: true, enabled: true });

Check for overlaying elements, iframes, or delayed rendering.

Issue 3: Incorrect Element Interactions

Issue: Actions like clicking or typing don't have the intended effect.

Solution: Verify the correct element is targeted using DevTools. Fallback to executing JavaScript on the element:

await page.evaluate((el) => el.click(), element);

Issue 4: Form Not Submitting

Issue: Form does not submit after filling and clicking submit.

Solution: Check for JavaScript validation preventing submission. Handle alerts blocking submission. Try submitting directly:

await page.evaluate(() => {
document.forms[0].submit();
});

Issue 5: Handling Alerts and Popups

Issue: Alerts or popups appear when submitting.

Solution: Use page's dialog event listener:

page.on("dialog", (dialog) => {
dialog.accept(); // Or dismiss()
});

Issue 6: Dynamic Content Issues

Issue: AJAX content doesn't load properly.

Solution: Use explicit waits for specific conditions:

await page.waitForSelector(".loaded-data");

Issue 7: Iframe Handling

Issue: Unable to interact with iframe elements.

Solution: Switch context to iframe before interacting:

const frame = await page.frame("iframe#id");
// Interact with frame

Application of Forms

  • User Registration and Sign-Up: Forms are fundamental for user onboarding, allowing individuals to register and create accounts on websites or applications. This process typically involves capturing essential user details, such as username, email, and password.
  • User Authentication and Sign-In: Forms play a critical role in user authentication, providing a secure way for individuals to sign in to their accounts. This involves verifying credentials entered through a sign-in form, ensuring access to personalized content and features.
  • Contact Forms: Web developers commonly implement contact forms to facilitate communication between users and website owners. These forms enable users to submit inquiries, feedback, or specific requests directly to the website administrators.
  • Search and Filtering: In web applications, forms are utilized for advanced search and filtering functionalities, enabling users to refine and tailor their searches based on specific criteria.
  • Account Settings and Preferences: Forms play a crucial role in allowing users to customize their account settings and preferences, providing a personalized and user-friendly experience.
  • Opinion Polls and Surveys: Developers leverage forms to conduct polls or surveys, gathering opinions and data on specific topics. This interactive approach helps in understanding user perspectives and preferences.

More Playwright Guides

From this article you have learned the importance of working with forms in Playwright and you have learned the tools available to work with forms. For further reading and guides related to forms and playwright see the following