Actions and Auto-Waiting
By Syncfusion®, Inc. · Last updated
Navigation (goto options, reload, back and forward, waitForURL, waitForLoadState), mouse actions (click options, double-click, right-click, hover), typing (fill, clear, pressSequentially, press, focus, blur), form actions (check, selectOption, radio buttons), scrolling, and how auto-waiting works: actionability checks, retries, waitFor, why fixed sleeps fail, and the force and trial options.
Actions and Auto-Waiting
- Actions are the things a test does: open pages, click, type, tick boxes and pick options.
- Before every action, Playwright waits until the element is ready. This is auto-waiting.
- In this lesson, you will use every common action, and see exactly what Playwright waits for.
Simple lesson: actions do what users do, and auto-waiting makes them wait for the page.
A Real-World Example
- A customer writes to the Practice Shop's support team.
- They type their name, pick a topic, choose how to get a reply, agree to the terms and send.
- This test does the same, one action per step.
- The Send message button starts disabled. It becomes enabled when you tick the terms box.
- Playwright clicked it only after it was enabled. You did not write any waiting code.
What You Will Learn
- How to move between pages.
- Mouse actions: click and its options, double-click, right-click and hover.
- Typing: fill, clear, type key by key, and press keys.
- Form actions: checkboxes, radio buttons and drop-down menus.
- Scrolling.
- How auto-waiting works, and why fixed sleeps are a bad idea.
Navigation
page.goto() Options
page.goto(url)opens a page and waits until it has loaded.waitUntilsays what "loaded" means:
| Value | Waits until |
|---|---|
'load' (default) | the page and its images and styles have loaded |
'domcontentloaded' | the HTML is read, before images finish |
'networkidle' | no network traffic for 500 ms. Discouraged: slow and unreliable |
'commit' | the first bytes of the response arrive |
timeoutsets how long to wait, in milliseconds:page.goto(url, { timeout: 10_000 }).goto()returns the response. A 404 page does not throw an error, so checkresponse.status()if it matters.
reload(), goBack() and goForward()
page.reload()reloads the page, like pressing F5.page.goBack()andpage.goForward()work like the browser's back and forward buttons.
page.waitForURL() and page.waitForLoadState()
- After a click that opens another page,
page.waitForURL('**/account')waits for that URL. **matches any text, so you do not need the full address.page.waitForLoadState()waits for the current page to finish loading. You rarely need it, becausegoto()andwaitForURL()already wait.
Simple lesson: goto() opens a page. waitForURL() waits for a page that a click opens.
Mouse Actions
click() and Its Options
| Option | Example | Does |
|---|---|---|
button | click({ button: 'right' }) | Right-click (or 'middle') |
clickCount | click({ clickCount: 2 }) | Clicks more than once |
modifiers | click({ modifiers: ['Shift'] }) | Holds keys while clicking: 'Alt', 'Control', 'ControlOrMeta', 'Meta', 'Shift' |
position | click({ position: { x: 5, y: 5 } }) | Clicks a point inside the element |
dblclick(), Right-Click and hover()
dblclick()double-clicks, for example to edit a value in place.click({ button: 'right' })opens a custom right-click menu.hover()moves the mouse over an element, for example to show a tooltip or a menu.
Simple lesson: click(), dblclick() and hover() cover almost every mouse need. Options change the button and keys.
Text and Keyboard Actions
fill() and clear()
fill('text')clears the field and types the whole text at once. It is fast and the best default.clear()empties the field.
pressSequentially()
pressSequentially('Ko')types one key at a time, like a person.- Use it when the page reacts to each key, for example a search box that suggests results.
- Add
{ delay: 100 }to wait 100 ms between keys.
press()
press('Enter')presses one key or a combination:'Tab','Escape','ArrowDown','Control+A','Shift+Tab'.ControlOrMetameans Control on Windows and Linux, and Command on macOS.
focus() and blur()
focus()puts the cursor in a field.blur()takes it out.- Some forms check a field only when you leave it, on blur.
fill() puts in the whole text in one go. Pages that listen for each key press (keydown, keyup) may not react to it. Use pressSequentially() for those.
Simple lesson: fill() by default, pressSequentially() when every key matters, press() for special keys.
Form Actions
check(), uncheck() and setChecked()
check()ticks a checkbox. If it is already ticked, nothing happens.uncheck()unticks it.setChecked(true)orsetChecked(false)does either, from a variable.- After the click, Playwright checks that the box really changed. If not, the action fails.
selectOption()
selectOption('returns')picks an option in a<select>menu.- You can pass the value, the visible label,
{ label: 'Returns' }or{ index: 2 }. - For multi-select menus, pass a list:
selectOption(['red', 'blue']).
Radio Buttons
- A radio button is ticked with
check(). Ticking one unticks the others in the group. - You cannot
uncheck()a radio button. Playwright throws an error, because a user cannot do it either.
Simple lesson: check() for boxes and radios, selectOption() for menus.
Scrolling
- Before an action, Playwright scrolls the element into view by itself.
scrollIntoViewIfNeeded()scrolls without doing anything else, for example before a screenshot.- To scroll the page with the mouse wheel, use
page.mouse.wheel(0, 500). Handling Complex Page Elements shows it.
await page.getByRole('button', { name: 'Back to top' }).scrollIntoViewIfNeeded();
await page.getByRole('button', { name: 'Back to top' }).click(); // also scrolls by itself
Simple lesson: Playwright scrolls for you. You rarely need to scroll yourself.
Auto-Waiting
What Auto-Waiting Is
- Web pages change all the time. Buttons appear late, menus slide in, forms enable their buttons.
- Before each action, Playwright waits until the element is ready for that action.
- It checks again and again until the checks pass or the time runs out.
Actionability Checks
Animation: a button goes through four states: hidden, sliding in, disabled, and covered by a banner. Each time a check fails, Playwright waits and retries. When all checks pass, it clicks
locator.click() checks: ✘ not visible, retrying ✘ not stable (moving), retrying ✘ not enabled, retrying ✘ a banner receives the click, retrying ✓ visible, stable, enabled, not covered → click
Each check runs again and again, until all pass or time runs out.
Hidden, moving, disabled, covered: each problem makes Playwright wait, not fail.
| Check | Means | Needed by |
|---|---|---|
| Visible | The element has a size and is not hidden | click, hover, fill, check, selectOption |
| Stable | It is not moving (no animation in progress) | click, hover, check |
| Enabled | It is not disabled | click, fill, check, selectOption |
| Editable | It is enabled and not read-only | fill, clear |
| Receives events | Nothing covers it, so the click lands on it | click, hover, check |
| Not inert | It is not blocked by an open modal dialog | click, hover, check |
How Actions Retry
- If a check fails, Playwright waits a moment and checks again.
- It stops when every check passes, or when the action timeout ends.
- The error then lists what it waited for in the Call log.
locator.waitFor() and Its States
- Sometimes you need to wait for an element without acting on it.
waitFor()does that.
| State | Waits until the element is |
|---|---|
'visible' (default) | on the page and visible |
'hidden' | gone or hidden |
'attached' | in the page, visible or not |
'detached' | removed from the page |
- Most of the time, a web-first assertion is better:
await expect(locator).toBeVisible().
Why Fixed Sleeps Are Discouraged
page.waitForTimeout(3000)always waits 3 seconds. It does not look at the page.- On a fast day, it wastes time. On a slow day, it is not long enough, and the test fails.
- Run this test. It waits half a second, then reads the status once. The order needs 1.2 seconds.
- The fix:
await expect(page.getByRole('status')).toHaveText('Order confirmed'). It waits as long as needed, and no longer.
Use page.waitForTimeout() only while you debug, never in a finished test. The Playwright docs say the same.
force and trial Options
click({ force: true })skips the actionability checks and clicks at once.- A forced click still lands on whatever is on top. If a banner covers the button, the banner gets the click.
- Use
forceonly when you know a check is wrong for your page. It hides real problems. click({ trial: true })runs the checks but does not click. It tells you if the click would work.
When an action times out, read the Call log in the error. Lines such as "element is not enabled" or "intercepts pointer events" tell you exactly what blocked it.
Simple lesson: Playwright waits until an element is visible, stable, enabled and not covered. Do not add sleeps.
Try It in Your Project
- These steps take the lesson from the lab to your own app.
- Search your tests for
waitForTimeoutand replace each one with a web-first assertion. - Test one form with keyboard only:
press('Tab'),press('Enter'). - Find one slow element and see which actionability check Playwright waits for in the call log.
Simple lesson: use one idea from this lesson on your own app this week.
TL;DR
goto(),reload(),goBack(),goForward()andwaitForURL()move between pages.click(),dblclick(),hover()andclick({ button: 'right' })cover mouse actions.fill()types fast,pressSequentially()types key by key,press()presses special keys.check(),uncheck(),setChecked()andselectOption()fill in forms.- Every action auto-waits for actionability checks. Never use fixed sleeps in a finished test.
Common mistakes
| Mistake | What happens | Fix |
|---|---|---|
waitForTimeout() before every step | Slow tests that still fail on slow days | Remove it. Actions and web-first assertions wait |
fill() on a box that reacts to each key | No suggestions appear | Use pressSequentially() |
click({ force: true }) to get past an error | The test hides a real bug, or clicks a banner | Find out what blocks the click in the Call log |
click() on a checkbox, then no check | The box may already have been ticked | Use check(). It checks the result for you |
| Reading text right after a click | The page has not updated yet | Use await expect(locator).toHaveText(...) |
Check Yourself
Exercises
What to Remember
- Navigation:
goto(),waitForURL(),goBack(),goForward(),reload(). fill()is the default for typing.pressSequentially()is for key-by-key pages.check()andselectOption()fill in forms and verify the result.- Auto-waiting checks visible, stable, enabled, editable and not covered.
- Replace sleeps with web-first assertions.
Next: Assertions