← Back to Index

Playwright Actions & Interactions

Learn how Playwright interacts with real web applications: clicking, typing, filling forms, selecting options, checking controls, hovering, keyboard actions, mouse actions, drag-and-drop, file uploads, dialogs, frames, tabs, scrolling and more.

Beginner → Real-Time Examples → Auto-Waiting → Robust Automation

1. What Are Actions and Interactions?

Automation is not only about finding elements. A real test must interact with the application exactly as a real user would: click buttons, enter information, select values, upload files, navigate between pages, and interact with dynamic components.

Find Element
→
Check Actionability
→
Perform Action
→
Wait for Result
→
Assert
Key Playwright concept: Playwright actions work with locators and automatically wait for relevant actionability conditions before performing actions.

2. Types of Playwright Actions

1

Click

Click buttons, links, cards and controls.

Core
2

Fill

Quickly enter values into input controls.

Core
3

Type

Simulate sequential keyboard input when required.

Useful
4

Check / Uncheck

Interact with checkboxes and similar controls.

Forms
5

Radio Buttons

Select a single option from a group.

Forms
6

Select Option

Select values from native HTML select controls.

Forms
7

Hover

Trigger menus, tooltips and hover-based components.

UI
8

Keyboard

Press Enter, Tab, Escape, shortcuts and key combinations.

Advanced
9

Mouse

Perform low-level mouse movements, clicks and coordinates.

Advanced
10

Drag & Drop

Move an element from one location to another.

Interaction
11

File Upload

Upload documents, images and test files.

Real-Time
12

Dialogs

Handle alerts, confirmations and prompts.

Advanced

3. Click Action

Clicking is one of the most common browser interactions.

Python
page.get_by_role(
    "button",
    name="Login"
).click()

Click a Link

Python
page.get_by_role(
    "link",
    name="Dashboard"
).click()
Playwright does not simply issue a raw click immediately. It uses locator actionability checks and waits for the target to become actionable.

4. Fill Text Input

For most text inputs, fill() is the preferred way to enter a value.

Python
page.get_by_label("Username").fill("john.smith")

page.get_by_label("Password").fill("TestPassword123")
Why use fill? It replaces the existing input value and is concise and reliable for normal form fields.

5. Type Text Character by Character

Use keyboard typing when the application specifically needs individual key events or typing behavior.

Python
page.get_by_label(
    "Search"
).press_sequentially("Playwright")
For normal form entry, prefer fill(). Use sequential keyboard input when the application's behavior depends on individual key events.

6. Clear Existing Text

Python
search = page.get_by_placeholder(
    "Search products"
)

search.fill("Laptop")

search.fill("")

Using fill("") is usually simpler than manually selecting and deleting the existing value.

7. Checkbox Interaction

Playwright provides dedicated methods for checkbox controls.

Python
page.get_by_label(
    "Accept Terms"
).check()

Uncheck

Python
page.get_by_label(
    "Accept Terms"
).uncheck()

Verify State

Python
from playwright.sync_api import expect

expect(
    page.get_by_label("Accept Terms")
).to_be_checked()

8. Radio Button Interaction

Python
page.get_by_label("Credit Card").check()

page.get_by_label("PayPal").check()
Prefer labels or roles instead of selecting a radio button by its DOM position.

9. Select Dropdown Option

For a native HTML <select> element, Playwright provides select_option().

Python
page.get_by_label(
    "Country"
).select_option("IN")

Select by Visible Label

Python
page.get_by_label(
    "Country"
).select_option(
    label="India"
)
Custom JavaScript dropdowns are different from native <select> controls. For custom dropdowns, interact with the visible component just as the user would.

10. Custom Dropdown Interaction

Modern applications often use custom dropdown components instead of native select elements.

Python
page.get_by_role(
    "combobox",
    name="Country"
).click()

page.get_by_role(
    "option",
    name="India"
).click()
The test follows the actual user interaction: open dropdown → choose option.

11. Hover Interaction

Hover is useful for menus, tooltips and actions that appear when the mouse moves over an element.

Python
page.get_by_role(
    "button",
    name="Products"
).hover()

page.get_by_role(
    "link",
    name="Laptops"
).click()

12. Keyboard Actions

Press Enter

Python
page.get_by_placeholder(
    "Search"
).press("Enter")

Press Escape

Python
page.keyboard.press("Escape")

Keyboard Shortcut

Python
page.keyboard.press("Control+A")

page.keyboard.press("Control+C")

13. Mouse Actions

Playwright also exposes a mouse API for advanced interactions.

Python
page.mouse.move(500, 300)

page.mouse.click(500, 300)
Prefer locator-based actions when possible. Coordinate-based mouse actions can be sensitive to layout changes, viewport size and responsive design.

14. Double Click

Python
page.get_by_text(
    "Customer Report"
).dblclick()

Useful for applications where double-click opens an editor, record, document or detail view.

15. Right Click / Context Menu

Python
page.get_by_text(
    "Customer Record"
).click(button="right")
If the application creates a custom context menu, locate and interact with the resulting menu using normal locators.

16. Drag and Drop

Drag-and-drop is common in dashboards, kanban boards and file management applications.

Python
source = page.get_by_text("Task A")

target = page.get_by_text("Done")

source.drag_to(target)
Preferred approach: Use drag_to() when the application supports a standard draggable interaction. It is clearer and more maintainable than manually calculating mouse coordinates.

17. File Upload

Playwright can upload files without interacting with the operating system's native file picker.

Python
page.get_by_label(
    "Upload Document"
).set_input_files(
    "test-data/invoice.pdf"
)

Multiple Files

Python
page.get_by_label(
    "Upload Documents"
).set_input_files([
    "test-data/invoice.pdf",
    "test-data/receipt.pdf"
])

18. File Download

Downloads can be captured using Playwright's download event.

Python
with page.expect_download() as download_info:

    page.get_by_role(
        "button",
        name="Download Report"
    ).click()

download = download_info.value

download.save_as(
    "downloads/report.pdf"
)
Playwright waits for the expected download event rather than forcing the test to guess how long the download will take.

19. Browser Dialogs — Alert, Confirm and Prompt

JavaScript dialogs such as alert(), confirm() and prompt() are handled using the dialog event.

Accept Dialog

Python
def handle_dialog(dialog):

    print(dialog.message)

    dialog.accept()


page.on("dialog", handle_dialog)

page.get_by_role(
    "button",
    name="Delete"
).click()

Dismiss Confirmation

Python
def handle_dialog(dialog):

    dialog.dismiss()


page.on("dialog", handle_dialog)

20. New Tab / Popup Interaction

Applications often open reports, payment pages or external systems in a new tab.

Python
with page.expect_popup() as popup_info:

    page.get_by_role(
        "link",
        name="Open Report"
    ).click()

popup = popup_info.value

popup.wait_for_load_state()

print(popup.title())
The popup is captured as part of the action that creates it, avoiding race conditions caused by trying to find the new page later.

21. Multiple Tabs

Python
pages_before = len(context.pages)

page.get_by_role(
    "link",
    name="Open Report"
).click()

page.wait_for_timeout(500)

pages_after = len(context.pages)

print(
    "Pages:",
    pages_after
)
For deterministic tests, prefer event-based handling such as expect_popup() when the action is known to open a popup. Avoid fixed sleeps as the synchronization mechanism.

22. Interaction Inside iframe

Embedded payment forms, support widgets and third-party components can live inside an iframe.

Python
payment = page.frame_locator(
    "#payment-frame"
)

payment.get_by_label(
    "Card number"
).fill("4111111111111111")

payment.get_by_label(
    "Expiry"
).fill("12/30")

23. Scrolling

Playwright can scroll an element into view as part of interaction. You can also explicitly scroll a locator when needed.

Python
footer = page.get_by_text(
    "Terms and Conditions"
)

footer.scroll_into_view_if_needed()

footer.click()
Locator-based interaction is preferable to manually calculating scroll positions.

24. What Happens When an Element Is Hidden?

Suppose the button exists in the DOM but is initially hidden.

Python
page.get_by_role(
    "button",
    name="Submit"
).click()

Playwright waits for the locator's target to satisfy the conditions required for the action. It does not mean that every arbitrary page condition will automatically be understood, so application-specific state may still need explicit assertions or event synchronization.

25. Disabled Elements

If an element is disabled, a normal interaction should not silently pretend that the user successfully clicked it.

Python
from playwright.sync_api import expect

submit = page.get_by_role(
    "button",
    name="Submit"
)

expect(submit).to_be_disabled()

After the required information is entered:

Python
page.get_by_label(
    "Email"
).fill("user@example.com")

expect(submit).to_be_enabled()

submit.click()

26. How Playwright Makes Actions Robust

Before performing an action, Playwright performs actionability checks appropriate to that action.

Locate
→
Visible
→
Stable
→
Receives Events
→
Enabled
→
Action
Exact checks depend on the action. For example, clicking has actionability requirements that are different from entering text.

27. Auto-Waiting vs Hard Sleep

❌ Fragile Approach

Avoid
import time

time.sleep(5)

page.get_by_role(
    "button",
    name="Submit"
).click()

✅ Better Approach

Recommended
from playwright.sync_api import expect

submit = page.get_by_role(
    "button",
    name="Submit"
)

expect(submit).to_be_enabled()

submit.click()
Hard sleeps wait for a fixed amount of time. Playwright's locator actions and web-first assertions wait for conditions instead.

28. Should You Use force=True?

Playwright provides a force option for some actions.

Python
page.get_by_role(
    "button",
    name="Submit"
).click(force=True)
Do not use force=True as a normal solution. If an element is covered, disabled, hidden or otherwise not actionable, investigate why. Force-clicking can make a test pass while bypassing behavior that matters to a real user.

29. Real-Time Example — Login Interaction

Python
from playwright.sync_api import expect


def login(page, username, password):

    page.get_by_label(
        "Username"
    ).fill(username)

    page.get_by_label(
        "Password"
    ).fill(password)

    page.get_by_role(
        "button",
        name="Sign In"
    ).click()

    expect(
        page.get_by_role(
            "heading",
            name="Dashboard"
        )
    ).to_be_visible()
The workflow is: Locate → Fill → Click → Wait → Assert.

30. Real-Time Example — E-Commerce Checkout

Python
product = page.locator(
    ".product-card"
).filter(
    has_text="Laptop"
)

product.get_by_role(
    "button",
    name="Add to Cart"
).click()

page.get_by_role(
    "link",
    name="Cart"
).click()

page.get_by_role(
    "button",
    name="Checkout"
).click()

page.get_by_label(
    "Address"
).fill("123 Main Street")

page.get_by_role(
    "button",
    name="Place Order"
).click()

expect(
    page.get_by_text(
        "Order placed successfully"
    )
).to_be_visible()

31. Real-Time Example — Kanban Board

Imagine a project management application with columns: To Do, In Progress and Done.

Python
task = page.get_by_text(
    "Implement Login API"
)

done_column = page.get_by_test_id(
    "kanban-done"
)

task.drag_to(done_column)

Then verify the result:

Python
expect(
    done_column.get_by_text(
        "Implement Login API"
    )
).to_be_visible()

32. Real-Time Example — Upload Document

Python
page.get_by_label(
    "Upload Document"
).set_input_files(
    "test-data/customer-contract.pdf"
)

page.get_by_role(
    "button",
    name="Upload"
).click()

expect(
    page.get_by_text(
        "Upload completed"
    )
).to_be_visible()

33. Professional Interaction Pattern

A scalable automation framework separates locating, interaction and verification into reusable page objects or components.

Python - Page Object
from playwright.sync_api import Page


class LoginPage:

    def __init__(self, page: Page):

        self.page = page

        self.username = page.get_by_label(
            "Username"
        )

        self.password = page.get_by_label(
            "Password"
        )

        self.sign_in = page.get_by_role(
            "button",
            name="Sign In"
        )

    def login(self, username, password):

        self.username.fill(username)

        self.password.fill(password)

        self.sign_in.click()

Test

Python - Test
def test_login(page):

    login = LoginPage(page)

    login.login(
        "testuser",
        "TestPassword123"
    )

34. Playwright Action Cheat Sheet

Interaction Playwright Python Typical Usage
Click locator.click() Buttons, links, controls
Double Click locator.dblclick() Editors, records
Fill locator.fill() Text inputs
Sequential typing locator.press_sequentially() Keyboard-driven UI
Check locator.check() Checkbox/radio controls
Uncheck locator.uncheck() Checkbox controls
Select locator.select_option() Native select
Hover locator.hover() Menus/tooltips
Keyboard locator.press() Keys and shortcuts
Mouse page.mouse.* Advanced interaction
Drag & Drop locator.drag_to() Kanban/components
Upload set_input_files() File upload
Download expect_download() File download
Popup expect_popup() New tab/window
iframe frame_locator() Embedded application
Dialog page.on("dialog") Alert/confirm/prompt

35. How to Build Robust Interactions

  • Use stable locators before performing actions.
  • Prefer user-facing roles, labels and intentional test IDs.
  • Let Playwright perform its normal actionability checks.
  • Use web-first assertions to verify dynamic application state.
  • Avoid fixed sleeps as the normal synchronization mechanism.
  • Avoid coordinate-based mouse actions unless they are genuinely required.
  • Use event-based synchronization for popups and downloads.
  • Use frame_locator() for iframe content.
  • Use dedicated methods such as check(), select_option() and set_input_files() rather than simulating unnecessary low-level operations.
  • Treat force=True as an exceptional tool, not a standard workaround.

36. Fragile vs Robust Interaction

❌ Fragile

Avoid
import time

time.sleep(5)

page.mouse.click(834, 612)

✅ Robust

Recommended
page.get_by_role(
    "button",
    name="Submit Order"
).click()
The robust version describes what the user is interacting with, rather than depending on where the element happens to be displayed.

37. Complete Real-Time Workflow

Navigate
→
Locate
→
Fill
→
Click
→
Wait
→
Verify
Python - End-to-End Example
from playwright.sync_api import expect


def test_customer_order(page):

    # 1. Navigate
    page.goto("https://example.com/login")

    # 2. Fill
    page.get_by_label(
        "Username"
    ).fill("testuser")

    page.get_by_label(
        "Password"
    ).fill("TestPassword123")

    # 3. Click
    page.get_by_role(
        "button",
        name="Sign In"
    ).click()

    # 4. Wait through a web-first assertion
    expect(
        page.get_by_role(
            "heading",
            name="Dashboard"
        )
    ).to_be_visible()

    # 5. Interact with application
    page.get_by_role(
        "link",
        name="Orders"
    ).click()

    # 6. Locate specific order
    order = page.locator(
        "tr"
    ).filter(
        has_text="ORD-10025"
    )

    # 7. Open order
    order.get_by_role(
        "button",
        name="View"
    ).click()

    # 8. Verify result
    expect(
        page.get_by_role(
            "heading",
            name="Order Details"
        )
    ).to_be_visible()

38. Professional Mindset for Actions

❌ Don't

Click coordinates because the button happens to be at a specific screen location.

❌ Don't

Add five seconds of sleep before every action just because the application is dynamic.

✅ Do

Locate the intended element and let Playwright handle normal actionability waiting.

❌ Don't

Use force-click to hide an underlying UI problem.

❌ Don't

Treat every interaction as a low-level mouse operation.

✅ Do

Use Playwright's high-level APIs such as click, fill, check, select_option and drag_to.

39. Final Mental Model

Locator
→
Action
→
Actionability
→
Auto-Wait
→
Browser Interaction
→
Assertion
The goal of robust Playwright automation is not to make the browser click faster.

The goal is to make the test interact with the application in a predictable, readable and user-oriented way while allowing Playwright to handle normal synchronization and actionability.

40. Next Learning Step

Next → Assertions

Learn web-first assertions for text, visibility, URLs, attributes, values, state and application behavior.

Then → Waits

Understand auto-waiting, explicit waits, network synchronization, events and dynamic application behavior.

Professional → Page Object Model

Build reusable components and page objects that combine robust locators, actions and assertions.