Playwright: Interacting with Checkbox
Checkboxes are one of the simplest form controls, but they exercise a different testing pattern than radio buttons. A radio group accepts one choice at a time. Checkboxes let users opt into multiple independent values, which makes them common in filters, permissions, preferences, and multi-select forms.
This guide uses the Codemo checkboxes playground to walk through the main scenarios you should automate: standard checks, default state, disabled checkboxes, and stored value verification.
Playground - General UI - Checkbox
Click here to practice checkbox interactions in the Codemo Playground.
Why Test Checkboxes?
Checkbox tests help you verify that:
- users can select more than one option at a time
- toggling one box does not unexpectedly affect another
- default checked states are preserved on load
- disabled checkboxes remain unchanged
- the application stores the correct internal values
Basic Checkbox HTML
Before we test checkboxes, let's understand what a checkbox element looks like in HTML:
<input type="checkbox" id="option1" name="options" value="1" />
<label for="option1">Option 1</label>
<input type="checkbox" id="option2" name="options" value="2" />
<label for="option2">Option 2</label>
A checkbox input element can have attributes like:
type: Should becheckboxfor checkboxesid: Unique identifier for the inputname: Groups checkboxes together (optional)value: The value submitted when the checkbox is selecteddisabled: Makes the checkbox non-selectablearia-checked: Indicates the current checked state of the checkbox (true or false)
Testing Standard Checks
The first case verifies that multiple checkboxes can be toggled independently.
test("checkboxes should support multiple selections", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/checkboxes/")
const caseCard = page.getByTestId("playground-case-standard-checks")
await caseCard.getByLabel("Option 1").check()
await caseCard.getByLabel("Option 2").check()
await caseCard.getByLabel("Option 3").check()
await caseCard.getByLabel("Option 2").uncheck()
await expect(caseCard).toContainText("Selected: 2")
})
This is the main difference from radio button testing: a second selection should not clear the first one.
Testing Default Checked State
Some forms ship with a recommended or pre-approved option already selected. That state should be validated explicitly.
test("checkboxes should keep the default option checked", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/checkboxes/")
const caseCard = page.getByTestId("playground-case-default-check")
await expect(caseCard.getByLabel("Option 1 (Default)")).toBeChecked()
await expect(caseCard).toContainText("Option 1 checked: Yes")
await expect(caseCard.getByLabel("Option 2")).not.toBeChecked()
await expect(caseCard.getByLabel("Option 3")).not.toBeChecked()
})
Testing Disabled State
Disabled checkboxes should not allow any interaction, even if one of them is already checked.
test("disabled checkboxes should stay unchanged", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/checkboxes/")
const caseCard = page.getByTestId("playground-case-disabled-state")
await expect(caseCard.getByLabel("Option 2 (Checked, Disabled)")).toBeDisabled()
await expect(caseCard.getByLabel("Option 2 (Checked, Disabled)")).toBeChecked()
await expect(caseCard).toContainText("Option 2 state: Checked")
})
Testing Stored Values
The value-check case is useful because the visible labels are not the only thing that matters. The checkbox group stores the internal values from the items array, such as banana, apple, and cherry.
test("checkboxes should store internal values", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/checkboxes/")
const caseCard = page.getByTestId("playground-case-value-check")
await caseCard.getByLabel("Banana").check()
await caseCard.getByLabel("Cherry").check()
await expect(page.getByTestId("playground-case-footer-value-check")).toHaveText(
"Selected: banana, cherry",
)
})
Practice With Codemo Playground
Codemo provides an interactive checkboxes playground where you can test multi-select behavior one case at a time.
Try these challenges:
- Check Option 1 and Option 2 together and verify both remain selected.
- Confirm the default case loads with Option 1 already checked.
- Verify that the disabled case keeps Option 2 checked and unchanged.
- Select Banana and Cherry in the value case and confirm the footer shows stored values.
Complete Test Example
import { test, expect } from "@playwright/test"
test.describe("Playground - General UI - Checkboxes", () => {
test("checkboxes should support multiple selections", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/checkboxes/")
const caseCard = page.getByTestId("playground-case-standard-checks")
await caseCard.getByLabel("Option 1").check()
await caseCard.getByLabel("Option 2").check()
await caseCard.getByLabel("Option 3").check()
await caseCard.getByLabel("Option 2").uncheck()
await expect(caseCard).toContainText("Selected: 2")
})
test("checkboxes should keep the default option checked", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/checkboxes/")
const caseCard = page.getByTestId("playground-case-default-check")
await expect(caseCard.getByLabel("Option 1 (Default)")).toBeChecked()
await expect(caseCard).toContainText("Option 1 checked: Yes")
await expect(caseCard.getByLabel("Option 2")).not.toBeChecked()
await expect(caseCard.getByLabel("Option 3")).not.toBeChecked()
})
test("disabled checkboxes should stay unchanged", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/checkboxes/")
const caseCard = page.getByTestId("playground-case-disabled-state")
await expect(caseCard.getByLabel("Option 2 (Checked, Disabled)")).toBeDisabled()
await expect(caseCard.getByLabel("Option 2 (Checked, Disabled)")).toBeChecked()
await expect(caseCard).toContainText("Option 2 state: Checked")
})
test("checkboxes should store internal values", async ({ page }) => {
await page.goto("https://codemo.dev/playground/general/checkboxes/")
const caseCard = page.getByTestId("playground-case-value-check")
await caseCard.getByLabel("Banana").check()
await caseCard.getByLabel("Cherry").check()
await expect(page.getByTestId("playground-case-footer-value-check")).toHaveText(
"Selected: banana, cherry",
)
})
})
Key Takeaways
- Use
getByLabel()orgetByRole("checkbox")for stable checkbox locators. - Assert that multiple selections can coexist when the UI is designed for it.
- Validate default and disabled states explicitly.
- Check internal stored values when labels and values are different.