If Playwright appears to ignore a toBeVisible() timeout, check three things first: the assertion must be awaited, the timeout must be configured for expect rather than the overall test, and the locator must resolve to the attached, visible element you actually intend to test. The documented defaults are 5,000 ms per assertion and 30,000 ms per test, but your configuration and installed Playwright version may differ.
Start with the correct assertion
toBeVisible() is an asynchronous locator assertion. In Playwright Test, write it as an awaited call so the runner observes the promise and waits while the web-first assertion retries:
import { test, expect } from '@playwright/test';
test('shows the saved message', async ({ page }) => {
await page.goto('https://example.com/settings');
const status = page.getByRole('status', { name: 'Saved' });
await expect(status).toBeVisible();
});
Playwright’s assertion documentation describes this behavior as re-testing the element until the expected state is met or the assertion timeout expires. An un-awaited promise, or a helper that creates the assertion without returning or awaiting it, can make the failure appear detached from the line you expected to control. That is a code-path diagnosis, not a universal explanation: inspect the actual test and helper chain.
Import expect from the Playwright Test runner, normally @playwright/test. Do not mix it accidentally with an assertion library that does not implement Playwright’s locator assertions.
#1 Best Overall
Identify which timeout expired
Playwright has separate budgets. Changing one does not automatically change the other.
| Budget | Documented default | Controls | How to change it |
|---|---|---|---|
| Expect timeout | 5,000 ms | Each async matcher such as toBeVisible() |
expect: { timeout: 10_000 } or a matcher-level timeout |
| Test timeout | 30,000 ms | The complete test, including navigation, actions and assertions | Test configuration or test.setTimeout() |
These are defaults in Playwright’s current timeout documentation (accessed 2026), not performance guarantees. A project configuration, command-line setup or installed version can override them. The failure call log normally reports wording such as expect.toBeVisible with timeout 5000ms. Compare that number with the value you intended to set.
The official references are Playwright’s timeout guide and the TestConfig reference.
Raise one assertion only
Use an assertion-level timeout when one known operation is slower and the rest of the suite should retain its normal budget:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
await expect(page.getByRole('button', { name: 'Save' }))
.toBeVisible({ timeout: 10_000 });
This changes only that matcher invocation. It does not extend the test’s total 30-second budget, so a test can still fail at the test-timeout boundary first.
Raise the project-wide expect timeout
Set a shared default in playwright.config.ts when the application consistently needs more time for UI assertions:
Rank #2
import { defineConfig } from '@playwright/test';
export default defineConfig({
expect: {
timeout: 10_000,
},
});
Keep this value targeted to your application’s real behavior. A large global value can make genuine locator mistakes take much longer to report.
Change the overall test budget only when appropriate
test.setTimeout() controls the whole test, not an individual matcher:
import { test } from '@playwright/test';
test('long workflow', async ({ page }) => {
test.setTimeout(60_000);
// navigation, actions and assertions share this total budget
});
If the call log still says the assertion timed out after 5,000 ms, increasing this test budget did not address the failure.
Verify what the locator actually matches
The LocatorAssertions API defines toBeVisible() as ensuring that the locator points to an attached and visible DOM node. Visibility is therefore only meaningful if the locator identifies the intended node in the correct page or frame.
- Confirm the test is on the expected URL and has not navigated to a login, error or redirect page.
- For an iframe, obtain the correct frame locator before locating the element.
- Check the role, accessible name, text, test id or CSS selector against the rendered DOM, not just the source template.
- Inspect whether the element is attached but hidden by CSS, covered by another element, or rendered only after a state change.
- Check whether the locator matches zero, one or several nodes. A broad locator can point at a hidden duplicate instead of the visible copy.
Use strict, intent-revealing locators where possible:
const saveButton = page.getByRole('button', { name: 'Save changes' });
await expect(saveButton).toBeVisible();
When a collection is the requirement
If the requirement is “at least one matching item is visible,” the API documentation specifically shows using .first():
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsconst results = page.getByRole('listitem', { name: /invoice/i });
await expect(results.first()).toBeVisible();
Use this only when any first matching item represents the product requirement. It can hide a bug if the first match is an unrelated hidden duplicate. If every item must be visible, assert the collection or each intended item explicitly instead of weakening the locator.
Read the call log before changing code
A timeout message contains diagnostic information. Look for the timeout value, the locator expression and the last observed state. A log that says it was “waiting for” a role or selector usually means the retry loop never found an attached, visible match; it does not prove that the timeout setting was ignored.
Run the test with the Inspector:
npx playwright test path/to/test.spec.ts --debug
Step to the assertion and inspect the live page, locator preview and matched elements. You can also pause from code while debugging:
await page.pause();
The Playwright debugging guide explains Inspector workflows and traces. Capture the failing URL, frame, locator and DOM state at the assertion; those details distinguish a wrong selector from a genuinely delayed UI.
Free tools Windows power users keep installed
One-click scans. No signup required.
Replace arbitrary sleeps with the real readiness signal
When the interface is asynchronous, wait for the event that makes the element meaningful: a response, a navigation, a loading indicator disappearing, or a selector becoming visible. For example:
await Promise.all([
page.waitForResponse(response =>
response.url().endsWith('/api/profile') && response.ok()
),
page.getByRole('button', { name: 'Refresh' }).click(),
]);
await expect(page.getByRole('status', { name: 'Loaded' }))
.toBeVisible();
Do not use a fixed delay as the normal fix:
// Debugging only; it is not a readiness guarantee.
await page.waitForTimeout(2000);
The Frame API reference states that frame.waitForTimeout() should only be used for debugging. A sleep can be too short on a slow run and unnecessarily long on a fast one; it also leaves the test blind to the condition it actually needs.
Rank #4
A repeatable diagnosis sequence
- Check observation. Ensure the line is
await expect(locator).toBeVisible(), the test function is async, and any helper awaits or returns the assertion. - Check the import. Use
expectfrom@playwright/testand verify the installed package version with your project’s normal package tooling. - Read the exact call log. Record the reported timeout and locator. If it says 5,000 ms, a test-level timeout change is not the setting used by this matcher.
- Check configuration scope. Apply
{ timeout: ... }to this matcher or configureexpect.timeoutglobally. Confirm the test is loading the configuration file you edited. - Inspect page and frame. Use
--debug, Inspector and the DOM to verify URL, frame, accessible name, attachment and CSS visibility. - Resolve multiplicity. Decide whether one exact node, any matching node or every node is required. Use a narrower locator or
.first()only when that semantics is correct. - Replace sleeps. Wait for the response, navigation or UI state that causes visibility, then retain
toBeVisible()as the user-facing assertion. - Confirm version compatibility. Playwright documents
toBeVisible()as added in v1.20 and its timeout option in v1.18. Check the API for the version installed in the project before relying on newer syntax.
Common failures and precise fixes
“The timeout option is ignored”
First inspect the call log. If the log reports 5,000 ms, the option may be attached to a different call, misspelled, or hidden by a wrapper that does not forward matcher options. Put the option directly on toBeVisible({ timeout: 10_000 }) to verify the scope, then update the helper’s parameter handling.
“I increased the test timeout, but the assertion still fails”
That is expected when the expect timeout remains 5,000 ms. Configure expect.timeout or the individual matcher. Increase the test timeout only if the complete workflow also needs more time.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstall“The element is in the HTML, but it is not visible”
Being present in markup is not enough. Check computed visibility, size, overlays, animation state, a collapsed ancestor and whether a second hidden copy matches first. Assert the visible, user-facing locator and wait for the state transition that reveals it.
“The selector works manually but not in the test”
The test may be in another frame, before authentication, on a different route or using a different viewport or feature flag. Inspector shows the state at the failing moment; use that state to correct navigation, frame selection or locator semantics.
“A list assertion is flaky”
Determine whether the product requires all results or merely one. A locator that matches transient placeholders and final rows can retry against the wrong node. Narrow it by role, name or state, and use .first() only for an explicit “any one” requirement.
“A sleep made it pass once”
The delay changed timing without proving readiness. Replace it with a response, navigation, selector or application-state signal, then let the web-first assertion retry within a deliberately chosen expect timeout.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Performance, reliability and timeout choices
- Keep the default small for fast feedback. A 5,000 ms expect timeout catches selector errors quickly in ordinary tests.
- Use a local override for exceptional latency. A 10,000 ms matcher timeout documents why one assertion differs from the suite.
- Do not mask regressions. A very large global expect timeout can turn a missing element into a slow failure across every worker.
- Leave test and expect budgets independent. The test budget must cover the whole workflow; the expect budget should cover the specific UI state.
- Prefer deterministic signals. Network and UI conditions adapt to machine speed and reveal the actual dependency.
These choices improve diagnosis, but they cannot determine the cause of an unspecified failure. A definitive answer requires the failing assertion, imports and helper path, Playwright version, relevant configuration, error/call log, and page or DOM state.
Or skip the browser setup
If your goal is a clean image of the page you are debugging or documenting rather than an interactive assertion, ScreenshotNeo provides a website screenshot API and MCP server. One request returns PNG, JPEG, WebP or PDF. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing state.
cURL (see the ScreenshotNeo API documentation):
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo also supports full-page captures with lazy images loaded, CSS-selector element captures, dark mode, device presets, custom viewports, retina scale, PDF paper and page-range controls, custom CSS or JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API and an OpenAPI specification. Its MCP tools—take_screenshot, get_page_info and capture_pdf—work with Claude, Cursor and other MCP clients.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is included on every plan, and yearly billing gives two months free. Sign up free for ScreenshotNeo.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →FAQ
Does toBeVisible() wait for animations to finish?
It retries until the locator satisfies Playwright’s visible-state condition or the expect timeout expires. If your application exposes a separate “ready” state, wait for that signal as well.
Can I use toBeVisible() outside Playwright Test?
The locator assertion and its timeout behavior are documented in Playwright Test’s assertion APIs. Confirm that the runner and package in your project support the API before transferring examples to another integration.
Why does a passing manual check not prove the test is correct?
A manual browser session can have different cookies, authentication, viewport, timing and frame state. The assertion evaluates the DOM at the exact moment and context of the automated test.
What should I include when asking for help?
Include the smallest failing test, the expect import, helper implementation, Playwright version, effective configuration, complete timeout call log, URL and frame, and a description or capture of the DOM at failure.
Recommended Free Tools
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

