← All practice topics
Automation Practice · Playwright

Buttons & Mouse Actions

Every way a user can click, hover and type. Each block shows a result you can assert on.

Every solution on this page starts with this hook. With baseURL set to https://hiraanachwani.com in your playwright.config.ts, it opens this page before each test.

test.beforeEach(async ({ page }) => {
  await page.goto('/practice/buttons');
});

Click

The simplest action. The counter shows how many times the button was clicked.

Clicked 0 times

Try this

  1. Click Click me three times and check the result says Clicked 3 times.
  2. Click Reset and check the count goes back to 0.

Locator tip: page.getByRole('button', { name: 'Click me' }) matches the button's text. Add { exact: true } if another button's name contains the same words.

Show Playwright solution
test('click', async ({ page }) => {
  const clickMe = page.getByRole('button', { name: 'Click me', exact: true });
  await clickMe.click();
  await clickMe.click();
  await clickMe.click();
  await expect(page.getByTestId('click-result')).toHaveText('Clicked 3 times');

  await page.getByRole('button', { name: 'Reset' }).click();
  await expect(page.getByTestId('click-result')).toHaveText('Clicked 0 times');
});

Double-click

This button ignores single clicks and reacts only to a double-click.

Waiting for a double-click

Try this

  1. Click the button once and check nothing changes.
  2. Double-click it and check the result says Double-clicked!

Locator tip: use dblclick(). Two separate click() calls are too far apart for the browser to count them as a double-click.

Show Playwright solution
test('double-click', async ({ page }) => {
  const button = page.getByRole('button', { name: 'Double-click me' });
  const result = page.getByTestId('double-click-result');

  await button.click();
  await expect(result).toHaveText('Waiting for a double-click');

  await button.dblclick();
  await expect(result).toHaveText('Double-clicked!');
});

Right-click

Right-clicking the box opens a custom context menu instead of the browser's own.

Right-click anywhere in this box
No action chosen

Try this

  1. Right-click the box and check the menu appears with three items.
  2. Choose Rename and check the result says You chose: Rename and the menu closes.

Locator tip: right-click is a click with an option: click({ button: 'right' }). The menu items have the menuitem role, so find them with getByRole('menuitem', { name: 'Rename' }).

Show Playwright solution
test('right-click', async ({ page }) => {
  await page.getByTestId('context-area').click({ button: 'right' });

  const menu = page.getByRole('menu', { name: 'File actions' });
  await expect(menu).toBeVisible();
  await expect(menu.getByRole('menuitem')).toHaveText(['Open', 'Rename', 'Delete']);

  await menu.getByRole('menuitem', { name: 'Rename' }).click();
  await expect(page.getByTestId('context-result')).toHaveText('You chose: Rename');
  await expect(menu).toBeHidden();
});

Hover & tooltips

A menu that opens only while the mouse is over it, and a tooltip that appears on hover.

Saves your changes (Ctrl+S)
No menu item chosen

Try this

  1. Check Settings is hidden, then hover over Account and check it appears.
  2. Click Settings and check the result says You chose: Settings.
  3. Hover over Save and check the tooltip text.

Locator tip: hover() moves the mouse over an element and leaves it there, so anything shown on :hover stays open for your next step.

Show Playwright solution
test('hover menu and tooltip', async ({ page }) => {
  const settings = page.getByRole('button', { name: 'Settings' });
  await expect(settings).toBeHidden();

  await page.getByRole('button', { name: 'Account' }).hover();
  await expect(settings).toBeVisible();
  await settings.click();
  await expect(page.getByTestId('hover-result')).toHaveText('You chose: Settings');

  await page.getByRole('button', { name: 'Save' }).hover();
  await expect(page.getByRole('tooltip')).toHaveText('Saves your changes (Ctrl+S)');
});

Keyboard keys

Focus the box and press keys. It shows the last key and any modifiers held with it.

You pressed: nothing yet

Try this

  1. Press Enter in the box and check the result says You pressed: Enter.
  2. Press Control+K and check the result shows both keys.
  3. Type hello one key at a time and check the last key was o.

Locator tip: press('Control+K') sends a key combination. pressSequentially('hello') types one key at a time, firing every key event, unlike fill() which sets the value in one go.

Show Playwright solution
test('keyboard keys', async ({ page }) => {
  const input = page.getByLabel('Key catcher');
  const result = page.getByTestId('key-result');

  await input.press('Enter');
  await expect(result).toHaveText('You pressed: Enter');

  await input.press('Control+K');
  await expect(result).toHaveText('You pressed: Control+K');

  await input.pressSequentially('hello');
  await expect(input).toHaveValue('hello');
  await expect(result).toHaveText('You pressed: o');
});

Button states

A button that stays disabled until you accept, and a toggle button that remembers whether it's on.

Not continued yet · Notifications off

Try this

  1. Check Continue is disabled, tick the checkbox, and check it becomes enabled.
  2. Click Continue and check the result starts with Continued.
  3. Click Notifications and check it is now pressed and the result says Notifications on.

Locator tip: a toggle button reports its state with aria-pressed. Playwright can match on it directly: getByRole('button', { name: 'Notifications', pressed: true }).

Show Playwright solution
test('button states', async ({ page }) => {
  const continueButton = page.getByRole('button', { name: 'Continue' });
  const result = page.getByTestId('states-result');

  await expect(continueButton).toBeDisabled();
  await page.getByLabel('I have read the instructions').check();
  await expect(continueButton).toBeEnabled();
  await continueButton.click();
  await expect(result).toHaveText(/^Continued/);

  const notifications = page.getByRole('button', { name: 'Notifications' });
  await expect(notifications).toHaveAttribute('aria-pressed', 'false');
  await notifications.click();
  await expect(page.getByRole('button', { name: 'Notifications', pressed: true })).toBeVisible();
  await expect(result).toHaveText('Continued · Notifications on');
});