Playwright: Interacting with Radio Button

Learn how to test radio button interactions in Playwright, including selection, default values, disabled states, and value mapping.
Ryan | Oct 24, 2026

Radio buttons are a small UI control, but they expose a few important testing patterns. They let users choose exactly one option from a group, which makes them useful for shipping methods, account preferences, filters, and status choices.

This guide focuses on the Codemo radio buttons playground. It covers the core scenarios you usually need in UI automation: standard selection, default values, disabled state, and value mapping.

Playground - General UI - Radio Buttons

Click here to practice radio button interactions in the Codemo Playground.

Why Test Radio Buttons?

Radio button tests help you verify that:

  • a single option can be selected correctly
  • the expected default option is already checked on load
  • disabled radio buttons cannot be changed
  • the UI label and the stored value stay aligned

These checks matter because radio buttons often control downstream form logic or conditional UI.

Basic Radio Button HTML

Before we test radio buttons, let's understand what a radio element looks like in HTML:

index.html
<input type="radio" id="option1" name="options" value="1" />
<label for="option1">Option 1</label>

<input type="radio" id="option2" name="options" value="2" />
<label for="option2">Option 2</label>

A radio input element can have attributes like:

  • type: Should be radio for radio buttons
  • id: Unique identifier for the input
  • name: Groups radio buttons together
  • value: The value submitted when the radio button is selected
  • disabled: Makes the radio button non-selectable
  • aria-checked: Indicates the current checked state of the radio button (true or false)

Testing Standard Selection

The most basic case is selecting one option from the group and confirming the footer reflects that choice.

playground/radio-buttons.spec.ts
test("radio button should register a standard selection", async ({ page }) => {
  await page.goto("https://codemo.dev/playground/general/radio-buttons/")

  await page.getByRole("radio", { name: "Beta" }).check()

  await expect(page.getByRole("radio", { name: "Beta" })).toBeChecked()
})

This verifies both the actual control state and the UI feedback that proves the selection was stored.

Testing Default Selection

Some forms pre-select a safe or recommended option. That is useful, but only if it stays correct across updates.

playground/radio-buttons.spec.ts
test("radio group should keep the default option selected", async ({ page }) => {
  await page.goto("https://codemo.dev/playground/general/radio-buttons/")

  await expect(page.getByRole("radio", { name: "Default" })).toBeChecked()
  await expect(page.getByRole("radio", { name: "Option 2" })).not.toBeChecked()
  await expect(page.getByRole("radio", { name: "Option 3" })).not.toBeChecked()
})

Default-state assertions are easy to skip, but they catch subtle regressions in initial form behavior.

Testing Disabled State

Disabled radio buttons should not respond to user input. This case checks that the existing value stays unchanged.

playground/radio-buttons.spec.ts
test("disabled radio buttons should stay unchanged", async ({ page }) => {
  await page.goto("https://codemo.dev/playground/general/radio-buttons/")

  await expect(page.getByRole("radio", { name: "Closed" })).toBeDisabled()
  await expect(page.getByRole("radio", { name: "Open" })).toBeDisabled()
  await expect(page.getByRole("radio", { name: "Closed" })).not.toBeChecked()
  await expect(page.getByRole("radio", { name: "Open" })).toBeChecked()
})

Testing Stored Values Instead Of Labels

The most interesting case in this playground is the value-mapping scenario. The user sees labels such as Banana, Apple, and Cherry, but the stored values are lowercase.

playground/radio-buttons.spec.ts
test("radio group should store the internal value", async ({ page }) => {
  await page.goto("https://codemo.dev/playground/general/radio-buttons/")

  await page.getByRole("radio", { name: "Cherry" }).check()

  await expect(page.getByRole("radio", { name: "Cherry" })).toHaveId(/cherry/i)
  await expect(page.getByText("Selected value: cherry")).toBeVisible()
})

This is a practical reminder that UI labels and submitted values are not always identical. Your test should verify whichever one actually matters to the application

In this example, I'm using toHaveId to verify the internal value of the radio button rather than relying solely on the visible label. This is because the UI is using the button element with type radio instead of the radio element. It also depends on your project implementation and how you want to assert the stored value.
If your app sends the radio value to an API or uses it to drive business rules, validate the stored value rather than stopping at the visible label.

Practice With Codemo Playground

Codemo provides an interactive radio buttons playground where you can practice the main interaction patterns one case at a time.

Try these challenges:

  1. Select Beta in the standard case and confirm the footer changes.
  2. Check that the default case already has Default selected before any interaction.
  3. Verify that the disabled radio group cannot be changed from Open.
  4. Select Cherry in the value-selection case and confirm the footer shows cherry.

Complete Test Example

playground/radio-buttons.spec.ts
import { test, expect } from "@playwright/test"

test.describe("Playground - General UI - Radio Buttons", () => {
  test("radio button should register a standard selection", async ({ page }) => {
    await page.goto("https://codemo.dev/playground/general/radio-buttons/")

    await page.getByRole("radio", { name: "Beta" }).check()

    await expect(page.getByRole("radio", { name: "Beta" })).toBeChecked()
  })

  test("radio group should keep the default option selected", async ({ page }) => {
    await page.goto("https://codemo.dev/playground/general/radio-buttons/")

    await expect(page.getByRole("radio", { name: "Default" })).toBeChecked()
    await expect(page.getByRole("radio", { name: "Option 2" })).not.toBeChecked()
    await expect(page.getByRole("radio", { name: "Option 3" })).not.toBeChecked()
  })

  test("disabled radio buttons should stay unchanged", async ({ page }) => {
    await page.goto("https://codemo.dev/playground/general/radio-buttons/")

    await expect(page.getByRole("radio", { name: "Closed" })).toBeDisabled()
    await expect(page.getByRole("radio", { name: "Open" })).toBeDisabled()
    await expect(page.getByRole("radio", { name: "Closed" })).not.toBeChecked()
    await expect(page.getByRole("radio", { name: "Open" })).toBeChecked()
  })

  test("radio group should store the internal value", async ({ page }) => {
    await page.goto("https://codemo.dev/playground/general/radio-buttons/")

    await page.getByRole("radio", { name: "Cherry" }).check()

    await expect(page.getByRole("radio", { name: "Cherry" })).toHaveId(/cherry/i)
    await expect(page.getByText("Selected value: cherry")).toBeVisible()
  })
})

Source Code & Example Tests

Check out the source code and more example tests on GitHub.

Key Takeaways

  • Use getByLabel() or getByRole("radio") to target radio controls reliably.
  • Always verify that only one option in the group is active at a time.
  • Check default selections explicitly instead of assuming they are correct.
  • Validate disabled state and internal stored values when those behaviors matter.
Practice radio button selection, defaults, and value mapping on the Codemo Playground and make your form tests more reliable.

References