Playwright Actions: Click, Fill, Type, Check, Press, Hover and More

Locators find elements; actions make things happen. In this lesson you will learn every Playwright action a tester needs, how auto-waiting protects each one, and which method to choose, with runnable examples on free practice pages.

Advertisement

📖 What You’ll Learn: actionability checks, click options, dblclick and right-click, fill vs pressSequentially, clear, press and the keyboard API, check/uncheck/setChecked, hover, focus, drag and drop, scrolling, and reading values from the page.


🤖 How Playwright Actions Work

An action is anything a user does: click, type, tick a checkbox, press a key, hover, drag. In Playwright you call actions on a locator, and Playwright handles the timing for you.

Before each action, Playwright runs actionability checks and waits until they pass (up to the timeout):

Checkclickfillcheckhover
Attached to the DOM✅✅✅✅
Visible✅✅✅✅
Stable (not animating)✅✅✅
Receives events (not covered)✅✅✅
Enabled✅✅✅
Editable✅

This is why you do not need sleeps before clicks. If a check never passes, the error tells you exactly which one failed, for example “element is not visible” or “another element would receive the click”. Learn more in Playwright waits.


🖱️ click() and Its Options

const login = page.getByRole('button', { name: 'Login' });

await login.click();                                  // left click
await login.click({ button: 'right' });              // right click
await login.click({ clickCount: 2 });                 // same as dblclick()
await login.dblclick();                               // double click
await login.click({ modifiers: ['Shift'] });          // Shift + click ('Control', 'Alt', 'Meta')
await login.click({ position: { x: 5, y: 5 } });      // click a specific point
await login.click({ trial: true });                   // run checks only, do not click

Example on a practice page that adds a button every time you click:

import { test, expect } from '@playwright/test';

test('add three elements', async ({ page }) => {
  await page.goto('https://the-internet.herokuapp.com/add_remove_elements/');
  const add = page.getByRole('button', { name: 'Add Element' });

  for (let i = 0; i < 3; i++) {
    await add.click();
  }
  await expect(page.locator('.added-manually')).toHaveCount(3);

  await page.locator('.added-manually').first().click(); // delete one
  await expect(page.locator('.added-manually')).toHaveCount(2);
});

⚠️ Common mistake: Avoid click({ force: true }). It skips the actionability checks and clicks even when the element is hidden or covered, so your test can pass while a real user would be blocked. Fix the cause (wait for the overlay to disappear) instead.


⌨️ Typing Text: fill(), clear() and pressSequentially()

const username = page.getByPlaceholder('Username');

await username.fill('standard_user');     // clears the field, then sets the value in one step
await username.clear();                   // empties the field
await username.pressSequentially('standard_user', { delay: 100 }); // types key by key
MethodUse it when
fill()Almost always: fastest and most reliable for inputs and textareas
pressSequentially()The page reacts to each keystroke (autocomplete, live search, input masks)
clear()You need an empty field

📝 Note: locator.type() is deprecated. Use fill(), or pressSequentially() when you really need individual key events.

Verify what was typed with toHaveValue:

test('number input', async ({ page }) => {
  await page.goto('https://the-internet.herokuapp.com/inputs');
  const number = page.locator('input[type="number"]');

  await number.fill('42');
  await expect(number).toHaveValue('42');

  await number.press('ArrowUp');           // increments the number input
  await expect(number).toHaveValue('43');
});

🔑 Pressing Keys: press() and page.keyboard

await page.getByLabel('Search').press('Enter');
await page.getByLabel('Search').press('Control+A');   // key combination
await page.getByLabel('Search').press('Backspace');

// Page-level keyboard (acts on whatever has focus)
await page.keyboard.press('Tab');
await page.keyboard.type('Hello');
await page.keyboard.down('Shift');
await page.keyboard.press('ArrowLeft');
await page.keyboard.up('Shift');

Common key names: Enter, Tab, Escape, Backspace, Delete, ArrowUp, ArrowDown, ArrowLeft, ArrowRight, Home, End, PageDown, F1 to F12, and modifiers Shift, Control, Alt, Meta. Use ControlOrMeta for shortcuts that are Ctrl on Windows/Linux and Cmd on macOS.

test('key press is detected', async ({ page }) => {
  await page.goto('https://the-internet.herokuapp.com/key_presses');
  await page.locator('#target').press('Escape');
  await expect(page.locator('#result')).toContainText('You entered:');
});

☑️ Checkboxes and Radio Buttons: check(), uncheck(), setChecked()

test('toggle checkboxes', async ({ page }) => {
  await page.goto('https://the-internet.herokuapp.com/checkboxes');
  const boxes = page.locator('#checkboxes input[type="checkbox"]');

  await boxes.nth(0).check();             // ticks it (no-op if already ticked)
  await expect(boxes.nth(0)).toBeChecked();

  await boxes.nth(1).uncheck();
  await expect(boxes.nth(1)).not.toBeChecked();

  await boxes.nth(1).setChecked(true);     // set to a specific state
  await expect(boxes.nth(1)).toBeChecked();
});

Prefer check() over click() for checkboxes: check() is idempotent and verifies the final state, while click() simply toggles. Radio buttons work the same way: page.getByLabel('Express delivery').check().


🔽 Dropdowns, File Uploads and Dialogs

These have their own detailed lesson, Alerts, Popups, Dropdowns, iFrames and Tabs. Quick reference:

await page.locator('#dropdown').selectOption({ label: 'Option 2' });   // native select
await page.locator('#file-upload').setInputFiles('data/sample.txt');    // upload
page.once('dialog', d => d.accept());                                    // JS alert

🖐️ Hover, Focus, Drag and Scroll

test('hover reveals the caption', async ({ page }) => {
  await page.goto('https://the-internet.herokuapp.com/hovers');
  await page.locator('.figure').nth(1).hover();
  await expect(page.getByText('name: user2')).toBeVisible();
});

test('right-click opens a context-menu alert', async ({ page }) => {
  await page.goto('https://the-internet.herokuapp.com/context_menu');
  page.once('dialog', async dialog => {
    expect(dialog.message()).toBe('You selected a context menu');
    await dialog.accept();
  });
  await page.locator('#hot-spot').click({ button: 'right' });
});
await page.getByLabel('Email').focus();                         // focus without clicking
await page.getByLabel('Email').blur();                          // trigger validation on blur
await page.locator('#source').dragTo(page.locator('#target'));  // drag and drop
await page.getByText('Footer links').scrollIntoViewIfNeeded();  // scroll to an element
await page.mouse.wheel(0, 800);                                 // scroll the page down

Playwright scrolls elements into view automatically before acting, so you rarely need to scroll yourself. Explicit scrolling is useful for infinite-scroll pages and lazy-loaded content.


📖 Reading Values from the Page

Actions often go together with reading data:

MethodReturns
textContent()All text, including hidden text (string or null)
innerText()Visible, rendered text
inputValue()Current value of an input, textarea or select
getAttribute('href')An attribute value
allTextContents()Text of every matching element (string array)
count()Number of matching elements
isVisible(), isChecked(), isEnabled()Current state, checked once without waiting

💡 Tip: To verify something, use web-first assertions such as toHaveText, toHaveValue and toBeChecked. They wait and retry. Use the read methods above only when you need the value for later steps.


📝 Assignment

  1. On https://the-internet.herokuapp.com/add_remove_elements/ add five elements, delete two, and assert three remain.
  2. On https://the-internet.herokuapp.com/checkboxes make both boxes checked using check(), then both unchecked using setChecked(false), with assertions after each step.
  3. On https://the-internet.herokuapp.com/inputs type 100, press ArrowDown three times and assert the value is 97.
  4. On https://the-internet.herokuapp.com/hovers hover each of the three figures in a loop and assert the matching caption (name: user1 to name: user3) is visible.
  5. On https://www.saucedemo.com log in using pressSequentially for the username and press('Enter') in the password field instead of clicking the button.
  6. Bonus: read all three hover captions with allTextContents() and assert the array length is 3.

❓ FAQ

🤔 What is the difference between fill and type in Playwright?

fill() sets the whole value at once and is the recommended method. The old type() is deprecated; its replacement pressSequentially() types key by key for pages that react to each keystroke.

🤔 How do I double-click or right-click in Playwright?

Use locator.dblclick() for a double click and locator.click({ button: 'right' }) for a right click.

🤔 How do I press Enter in Playwright?

await locator.press('Enter') on an element, or await page.keyboard.press('Enter') for whatever has focus.

🤔 Why does Playwright say “element is not visible” or “intercepts pointer events”?

The element is hidden or covered by another element, such as a loader or cookie banner. Wait for the overlay to disappear or close it first, instead of using force: true.

🤔 How do I check a checkbox in Playwright?

await page.getByLabel('Remember me').check(), then verify with await expect(...).toBeChecked().


🎯 Next Steps

🎬 Watch the full Playwright tutorial series on YouTube!

Subscribe to SqaToolsByDY • Practice on sqatools.in

2 thoughts on “Playwright Actions: Click, Fill, Type, Check, Press, Hover and More”

Leave a Comment