Reputation: 241
like in selenium, do we have option in Playwright to wait for an element to be clickable ?
Upvotes: 20
Views: 114692
Reputation: 71
I haven't found the solution offered by playwright directly. For performance testing, you should not wait for load event. This way you can verify latency of e.g. button doing what it's expected to do and click it. Even if page browser hasn't emitted load event yet.
Waiting for load event, which is a default Page.NavigateOptions, disturbs the measurements. Depending on what page you're testing you might have even 50%+ worse latencies which do not reflect the reality - that user can already interact with an element before regardless of browser events
Side effect is a visible element might not have event listeners yet and be clickable.
What I wish was working
page.navigate("https://some-tested-page.com", Page.NavigateOptions().setWaitUntil(WaitUntilState.COMMIT))
val button = page.getByText("Yes").first()
button.waitFor()
// nope, not working. button is clicked before it has event listeners
button.click(Locator. ClickOptions().setTimeout(2000.0))
// timeout
page.getByText("This field is required").first().waitFor()
And what actually works. It's using a piece of code out of playwright that does the polling and checks if button started working
page.navigate("https://some-tested-page.com")
val success = conditionPolling.await(
retryWait = ofMillis(10),
timeout = ofMillis(2000)
) {
val button = page.getByText("Yes").first()
button.waitFor()
button.click(Locator.ClickOptions().setNoWaitAfter(true))
page.getByText("This field is required").count() >= 1
}
if (!success) {
throw TimeoutError("Waiting for working button timed out")
}
It would be great if if playwright checked if element is not only clickable, but handling events. Or offer a method for waiting until element has event listeners attached.
EDIT: I've found out there is Page.waitForCondition. I don't know what polling interval there is under the hood, but it doesn't give the same performance results as 'manual' polling every 10ms. Using Page.waitForCondition gives ~4% bigger latencies in measurements. So the additional complexity by using custom code has value.
page.navigate("https://some-tested-page.com")
page.waitForCondition {
val button = page.getByText("Yes").first()
button.waitFor()
button.click(Locator.ClickOptions().setNoWaitAfter(true))
page.getByText("This field is required").count() >= 1
}
Upvotes: 0
Reputation: 4178
The LocatorAssertions class provides assertion methods that can be used to make assertions about the Locator state in the tests.This will wait for an locator to reach an certain state until timeout.
Ensures the Locator points to an enabled element.
const locator = page.locator('button.submit');
await expect(locator).toBeEnabled();
Ensures the Locator points to an editable element.
const locator = page.getByRole('textbox');
await expect(locator).toBeEditable();
Upvotes: 2
Reputation: 21
This is not exactly about clickable-state, but for visible one. Java implementation
page.locator("button[aria-label=Search]")
.waitFor(new Locator.WaitForOptions()
.setState(WaitForSelectorState.VISIBLE)
.setTimeout(10000));
I have found only 4 possible states to wait for:
package com.microsoft.playwright.options;
public enum WaitForSelectorState {
ATTACHED,
DETACHED,
VISIBLE,
HIDDEN
}
Upvotes: 1
Reputation: 1506
The Click
method has an overload that lets you pass an instance of LocatorClickOptions
. Use that with the Trial
property set to true
.
In the following C# code, the execution will wait until the page has a button labelled "Sign In" that is clickable.
await Page.GetByRole(AriaRole.Button, new() { Name = "Sign In" }).ClickAsync(new LocatorClickOptions() { Trial = true });
Clickable means:
Upvotes: -1
Reputation: 18634
For page.click(selector[, options])
, Playwright will ensure that:
So, you can use this:
await page.click('button');
If you want to add a timeout, basically to allow playwright to complete the above checks and then click, you can do like this:
await page.click('button', {timeout: 9000});
To first check that the element is visible and then click another element based on the result, you can use an if-else
like this:
if (await page.locator('modal-selector').isEnabled()) {
await page.click('button1')
} else {
//do something
}
Upvotes: 11
Reputation: 36143
Playwright is "auto-waiting" for this.
Checkout the documentation: https://playwright.dev/docs/actionability
You can check the button state with the method isDisabled()
Checkout the docs: https://playwright.dev/docs/api/class-elementhandle#element-handle-is-disabled
Upvotes: 3