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.
- TLDR: Submitting Forms with Playwright
- Understanding HTML Forms
- Submitting Forms with Playwright
- Handling Form Elements
- Practical Example: Text Inputs
- Handling Confirmation Dialog Boxes
- Handling Captchas
- Best Practices
- Troubleshooting
- More Playwright Guides
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:
- Find the elements using locators.
- Populate input values using
locator.fill()
- 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 POST
ing data to the /submit
endpoint. Continuing with the HTML, we can see a series of labels and inputs within the form
element. The label
s have no impact on the submission. The input
s 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:
Category | Input Type | Description |
---|---|---|
Basic Input Types | text | Single-line text input. |
password | Secure password input. | |
search | Search query input. | |
tel | Telephone number input. | |
url | URL entry with protocol validation. | |
email | Email address input. | |
Numeric Input Types | number | Numeric input with restrictions. |
range | Slider for selecting numeric values within a specified range. | |
Date and Time Types | date | Calendar-based date input. |
time | Input for specifying a specific time. | |
datetime-local | Combined date and time input in a localized format. | |
month | Selection of a specific month. | |
week | Selection of a specific week. | |
Selection Input Types | checkbox | Binary option that can be checked or unchecked. |
radio | Selection of a single option from a group. | |
File Input Type | file | Allows users to upload files. |
Button Types | button | Clickable button without a predefined action. |
submit | Triggers the form submission process. | |
reset | Resets form fields to default values. | |
Miscellaneous Types | color | Selection of a color from a palette. |
hidden | Stores data on the client-side without display. | |
image | Functions 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:
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.
Dropdowns:
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:
- 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.
- 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.
- 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.
There's a couple key points here,
- We are using
getByLabel
andgetByText
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
We learn some new things from this example:
- CSS Selectors: We are using the action methods (
fill
,click
, etc) directly on thepage
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 thepage
variable or alocator
.
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