Playwright: Interacting with Button

Learn how to interact with buttons in Playwright, including single clicks, double clicks, right clicks, and more.
Ryan | Jul 05, 2026

Buttons are one of the most common interactive elements on web pages. In this guide, you'll learn how to test different types of button clicks using Playwright, from basic single clicks to more advanced interactions like double clicks and long presses.

Why Test Button Interactions?

Buttons trigger actions on web pages. Testing them ensures:

  • Users can click buttons and trigger the intended actions
  • Buttons respond correctly to different types of interactions
  • Buttons show the correct state (enabled, disabled)
  • Click events are properly handled by your application

Basic Button HTML

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

index.html
<button type="button" class="btn-primary">
  Click Me
</button>

A button element can have attributes like:

  • type: Specifies the button type (button, submit, reset)
  • disabled: Makes the button non-clickable
  • class: CSS class for styling

Finding Buttons with Playwright Locators

Playwright provides several ways to locate buttons. The recommended approach is using getByRole():

// Find by button role and text
await page.getByRole("button", { name: "Click Me" }).click()

// Find by role without specific name
await page.getByRole("button").click()

// Find by exact text
await page.getByText("Click Me").click()
Prefer getByRole() over CSS selectors because it's more resilient to HTML changes and aligns with how users interact with your page.

Practice with Codemo Playground

Codemo provides an interactive button testing playground where you can practice testing different button interactions. Each scenario has:

  • A button to interact with
  • A description of what to test
  • A counter to verify the interaction was registered

Try these challenges:

  1. Single-click the button and verify the counter increments
  2. Double-click the button and check the result
  3. Right-click the button to trigger a context menu
  4. Middle-click the button to test the middle button event
  5. Long-press the button for 2 seconds and verify registration
  6. Wait for a disabled button to become enabled, then click it

Playground - General UI - Button

Click here to practice button interactions in the Codemo Playground.

Example Button Test Code

Check out the example button test code

Testing Single Clicks

A single click is the most basic button interaction. Here's how to test it:

playground/general-ui/button.spec.ts
import { test, expect } from "@playwright/test"

// Test that verifies a single click on a button increments the counter
test("button should register a single click", async ({ page }) => {
  await page.goto("http://codemo.dev/playground/general/button/")
  
  // Perform a single click and verify the counter increments
  await page.getByRole("button", { name: "Single Click" }).click()

  // Assert that the counter now shows 1 click
  await expect(page.getByTestId("playground-case-footer-single-click")).toHaveText("Single Clicks: 1")
})

Testing Double Clicks

A double click occurs when a button is clicked twice rapidly. Double-click events are useful for actions like editing or toggling modes:

playground/general-ui/button.spec.ts
// Test that verifies a double click on a button registers as a double-click event
test("button should register a double click", async ({ page }) => {
  await page.goto("http://codemo.dev/playground/general/button/")

  // Perform a double click using the dblclick() method
  await page.getByRole("button", { name: "Double Click" }).dblclick()

  // Assert that the counter registers the double click
  await expect(page.getByTestId("playground-case-footer-double-click")).toHaveText("Double Clicks: 1")
})

The dblclick() method sends a double-click event to the button.

Testing Right Clicks

A right-click (context menu) event happens when a user clicks the right mouse button. This is useful for testing custom context menus:

playground/general-ui/button.spec.ts
// Test that verifies a right-click (context menu) event on a button
test("button should register a right click", async ({ page }) => {
  await page.goto("http://codemo.dev/playground/general/button/")

  // Perform a right-click by specifying button: "right"
  await page.getByRole("button", { name: "Right Click" }).click({ button: "right" })

  // Assert that the counter registers the right click
  await expect(page.getByTestId("playground-case-footer-right-click")).toHaveText("Right Clicks: 1")
})

The click({ button: "right" }) option triggers a right-click event.

Testing Middle Clicks

A middle click occurs when the user clicks the scroll wheel (middle button) on a mouse. Some applications use middle clicks to open links in new tabs:

playground/general-ui/button.spec.ts
// Test that verifies a middle-click (scroll wheel) event on a button
test("button should register a middle click", async ({ page }) => {
  await page.goto("http://codemo.dev/playground/general/button/")

  // Perform a middle-click by specifying button: "middle"
  await page.getByRole("button", { name: "Middle Click" }).click({ button: "middle" })

  // Assert that the counter registers the middle click
  await expect(page.getByTestId("playground-case-footer-middle-click")).toHaveText("Middle Clicks: 1")
})

Testing Long Presses

A long press (or hold) occurs when a user holds down the mouse button for an extended period. This is common for mobile interactions or special actions:

playground/general-ui/button.spec.ts
// Test that verifies a long press (hold) event on a button for 2 seconds
test("button should register a long press", async ({ page }) => {
  await page.goto("http://codemo.dev/playground/general/button/")

  // Long press by clicking the button with a delay to simulate holding the mouse button down
  await page.getByRole("button", { name: "Long Press" }).click({ delay: 2500 })

  // Assert that the counter registers the long press
  await expect(page.getByTestId("playground-case-footer-long-press")).toHaveText("Long Presses: 1")
})

For long presses, we use the click method with a delay option to simulate holding the mouse button.

Testing Button States

Buttons can be in different states. The most important is the disabled state:

playground/general-ui/button.spec.ts
// Test that verifies clicking a button that transitions from disabled to enabled state
test("button should be clickable when enabled", async ({ page }) => {
  await page.goto("http://codemo.dev/playground/general/button/")

  // Verify the button is initially disabled
  await expect(page.getByRole("button", { name: "Disabled" })).toBeDisabled()

  // Click the reset button to enable the button
  await page.getByTestId("playground-case-button-enabled-reset").click()

  // Wait for the button to become enabled (with a 6-second timeout)
  await expect(page.getByRole("button", { name: "Enabled" })).toBeVisible({ timeout: 6000 })

  // Click the button now that it's enabled
  await page.getByRole("button", { name: "Enabled" }).click()

  // Assert that the click was registered
  await expect(page.getByTestId("playground-case-footer-enabled")).toHaveText("Enabled Clicks: 1")
})

The test waits for the button to transition from disabled to enabled, then clicks it and verifies the action was registered.

Common Button Assertions

Here are useful assertions to verify button state and behavior:

playground/general-ui/button.spec.ts
import { test, expect } from "@playwright/test"

// Test that demonstrates common button state and property assertions
test("button assertions", async ({ page }) => {
  await page.goto("http://codemo.dev/playground/general/button/")
  
  // Locate the button to test
  const button = page.getByRole("button", { name: "Single Click" })
  
  // Verify the button is visible in the viewport
  await expect(button).toBeVisible()
  
  // Verify the button is enabled and can be interacted with
  await expect(button).toBeEnabled()
  // Alternatively, check if the button is disabled: await expect(button).toBeDisabled()
  
  // Verify the button exists in the DOM
  await expect(button).toBeDefined()
  
  // Verify the button text contains the expected text
  await expect(button).toContainText("Click")
  
  // Verify the button has the correct HTML type attribute
  await expect(button).toHaveAttribute("type", "button")
})

Complete Test file

Here's a complete test file that covers multiple button interactions:

playground/general-ui/button.spec.ts
import { test, expect } from "@playwright/test"

test.describe("Playground - General UI - Button", () => {

  test("button should register a single click", async ({ page }) => {
    await page.goto("http://codemo.dev/playground/general/button/")

    await page.getByTestId("playground-case-button-single-click-reset").click()
    await page.getByRole("button", { name: "Single Click" }).click()

    await expect(page.getByTestId("playground-case-footer-single-click")).toHaveText("Single Clicks: 1")
  })

  test("button should register a double click", async ({ page }) => {
    await page.goto("http://codemo.dev/playground/general/button/")

    await page.getByTestId("playground-case-button-double-click-reset").click()
    await page.getByRole("button", { name: "Double Click" }).dblclick()

    await expect(page.getByTestId("playground-case-footer-double-click")).toHaveText("Double Clicks: 1")
  })

  test("button should register a right click", async ({ page }) => {
    await page.goto("http://codemo.dev/playground/general/button/")

    await page.getByTestId("playground-case-button-right-click-reset").click()
    await page.getByRole("button", { name: "Right Click" }).click({ button: "right" })

    await expect(page.getByTestId("playground-case-footer-right-click")).toHaveText("Right Clicks: 1")
  })

  test("button should register a middle click", async ({ page }) => {
    await page.goto("http://codemo.dev/playground/general/button/")

    await page.getByTestId("playground-case-button-middle-click-reset").click()
    await page.getByRole("button", { name: "Middle Click" }).click({ button: "middle" })

    await expect(page.getByTestId("playground-case-footer-middle-click")).toHaveText("Middle Clicks: 1")
  })

  test("button should register a long press", async ({ page }) => {
    await page.goto("http://codemo.dev/playground/general/button/")

    await page.getByRole("button", { name: "Long Press" }).click({ delay: 2500 })
    
    await expect(page.getByTestId("playground-case-footer-long-press")).toHaveText("Long Presses: 1")
  })

  test("button should be clickable when enabled", async ({ page }) => {
    await page.goto("http://codemo.dev/playground/general/button/")

    await expect(page.getByRole("button", { name: "Disabled" })).toBeDisabled()
    await page.getByTestId("playground-case-button-enabled-reset").click()
    await expect(page.getByRole("button", { name: "Enabled" })).toBeVisible({ timeout: 6000 })
    await page.getByRole("button", { name: "Enabled" }).click()

    await expect(page.getByTestId("playground-case-footer-enabled")).toHaveText("Enabled Clicks: 1")
  })

  test("button assertions", async ({ page }) => {
    await page.goto("http://codemo.dev/playground/general/button/")

    const button = page.getByRole("button", { name: "Single Click" })

    await expect(button).toBeVisible()
    await expect(button).toBeEnabled()
    await expect(button).toContainText("Click")
    await expect(button).toHaveAttribute("type", "button")
  })

})

Key Takeaways

  • Use click() for single clicks
  • Use dblclick() for double clicks
  • Use click({ button: "right" }) for right-clicks
  • Use click({ button: "middle" }) for middle-clicks
  • Use mouseDown() and mouseUp() for long presses
  • Use the footer test IDs for reliable assertions against the counter values
  • Always check button state with toBeEnabled() or toBeDisabled()
  • Prefer getByRole() over CSS selectors for finding buttons
Let's practice button interactions in the Codemo Playground - General UI - Button

References