Wait, Timeouts, Custom wait in Playwright Python
Some software is slow, some software is fast and some softwares are normal. So when we try to perform operations using Playwright or any other automation tools, we have to adjust our automation to speed according to the software speed, this can be achieved only by delaying the operations. This process is called waiting.
If the proper wait time is not given the test might fail so to avoid failure, it is important to provide the necessary waits
There are two types of waits: one that applies to a given line alone and the other that applies to the complete script.
At the bottom of this tutorial, there is a custom wait, it is bit important do check that.
Note: Default timeout in clarity, 30 seconds or 30,000 ms.
We will be using the waits practice page: https://demo.testercoder.com/waits.html
Sleep
Everyone’s favorite weight method is sleep Function. This is the static wait which means if the sleep function is used then that particular thread is going to wait for a given amount of time. It does not matter whether conditions are met or not, nothing matters it just sleeps there for a given time.
Note: Try to avoid using sleep in your test scripts, it is sometimes useful when none of the other waiting methods work.
Let’s write the code keeping in mind that the button loads after 40 seconds
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
page = pw.chromium.launch(headless=False).new_page()
page.goto("https://demo.testercoder.com/waits.html")
sleep(45)
page.locator("#delayed").click()
Auto-waiting
Playwright by default waits for a particular element to present or a particular condition to be met. The default wait time for the element to present is 30 seconds and 5 seconds for the condition to be met.
while finding an element playwright makes a few checks like whether
- Only one element is found (Unlike selenium, the playwright will not consider the first element if more than one is present)
- element is interactable
- element is enabled
- element is stable
And all this should happen within 30 seconds. If any of this fails then Playwright will fail the step. Our target element is the below button.
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
page = pw.chromium.launch(headless=False).new_page()
page.goto("https://demo.testercoder.com/waits.html")
page.locator("#alert").click()
When you execute the above code, the playwright waits for 30 seconds, if the element is present before that then it clicks the element and moves on otherwise fails the test.
wait_for()
wait_for()
method waits for the element to present on the webpage till the given timeout. I have designed the below button that will be created only after 40 seconds, lets try to click it with auto-waiting. This method helps to wait for the element to present, once the element is present then you can write the operations on that element.
Note: Scope is One line
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
page = pw.chromium.launch(headless=False).new_page()
page.goto("https://demo.testercoder.com/waits.html")
page.locator("#delayed").wait_for(timeout=45000)
page.locator("#delayed").click()
Operations with timeout
Whenever we perform some operation on any element, the playwright provides a way to give some wait time if the element is not there, these are called timeouts
.
For example, I can rewrite the above program without using wait_for()
.
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
page = pw.chromium.launch(headless=False).new_page()
page.goto("https://demo.testercoder.com/waits.html")
page.locator("#delayed").click(timeout=45000)
If you want to override the default timeout, then use set_default_timeout()
to override it. In the below code, I am setting the default timeout to 60 seconds.
from playwright.sync_api import sync_playwright, Locator
pw = sync_playwright().start()
page = pw.chromium.launch(headless=False).new_page()
page.set_default_timeout(timeout=60000)
page.goto("https://demo.testercoder.com/waits.html")
Wait for Url
In Playwright, we click elements and submit forms, these actions sometimes trigger navigation to a different webpage containing a different URL. In such cases, wait_for_url()
will be helpful.
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
page = pw.chromium.launch(headless=False).new_page()
page.goto("https://demo.testercoder.com/waits.html")
page.locator("#xyz").click()
page.wait_for_url("http://google.com")
We can also use regex to wait for the URL.
page.locator("#xyz").click()
page.wait_for_url("**/shopping.html")
Wait for the element state
wait_for_element_state()
waits till the element reaches the specific state. The element state could be “disabled”, “editable”, “enabled”, “hidden”, “stable”, “visible”. Please notice this method will work only when you are using element handle function.
Now below code wait for the button to be visible, the default wait time is 30 seconds
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
page = pw.chromium.launch(headless=False).new_page()
page.goto("https://demo.testercoder.com/waits.html")
page.locator("#display-other-button").click()
page.locator("#hidden").element_handle().wait_for_element_state(state="visible", timeout=45000)
Wait for the selector
We can wait_for_selector()
wait for a specific locator to present, which is the same as wait_for(), but we just apply it on the element handle.
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
page = pw.chromium.launch(headless=False).new_page()
page.goto("https://demo.testercoder.com/waits.html")
page.locator("#display-other-button").element_handle().wait_for_selector("#abc", timeout=45000)
Wait for the page load state
We can wait till the page reaches a certain state, we can see the state of the page in the developer console. Use document.readyState
on the dev console.
The webpage goes through multiple states:
- load: Page load is over (response started flowing in)
- domcontentloaded: All the dom(page) elements loaded
- networkidle: No request is going to sever or no response is coming back
Note: Based on my experience with this, this doesn’t work accurately, be careful of that
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
page = pw.chromium.launch(headless=False).new_page()
page.goto("https://demo.testercoder.com/waits.html")
page.wait_for_load_state(state="load")
Custom wait using sleep
So far we have been using built-in functions from Playwright. Sometimes, in reality, those waits are not enough or do not serve your purpose. In such cases, we have to build our own waits. That is what we are going to do, trust me, this will be way too helpful in your automation.
When do I use it?
When the wait and timeout periods reach their specified times, the step will fail. However, sometimes all you want is to check whether the element is present or not. If it’s present, do one thing; if it’s not there, then do another thing. This is quite a common case in automation.
Let’s build a custom click that should wait for the given time if the element is not present, this sounds like timeout isn’t it, yes it is, but in our own terms.
- Decide what function or what kind of custom wait you want; We decided to create a wait function to wait for an element to be present
- As part of the function accept two parameters
- locator: For which locator we will be waiting for
- timeout_in_seconds: a parameter for total wait time, set the default value to 30 seconds (or a usual wait time of your application)
- Create a loop with a range, the range of timeout_in_seconds
- Write a try-except block, this block will be helpful if there is no element then the test will not fail
- Find the element inside a try block, following a return True. Meaning if the element is present then the control should exit the method. Here you should use a timeout of 1000ms so that we can keep trying after every second.
- If an error occurs control will reach except, here either use “pass” or some log statement
- Now outside the loop, write
return False
, control will reach this step only when the playwright cannot find the element within the given time.
from playwright.sync_api import sync_playwright, Locator
pw = sync_playwright().start()
page = pw.chromium.launch(headless=False).new_page()
page.set_default_timeout(timeout=60000)
page.goto("https://demo.testercoder.com/waits.html")
def custom_wait(locator: Locator, timeout_in_seconds=30):
for i in range(timeout_in_seconds):
try:
page.locator(locator).click(timeout=1000) # 1000 ms = 1 s
print(f"'{locator}' Element is present")
return True
except Exception as e:
print(f"Element not present Trying for {i} time and the error is {e}")
return False
present_or_not = custom_wait("#delayed", timeout_in_seconds=45)
if present_or_not:
page.locator("#delayed").click()
else:
pass # do something else
Posts You Might Like
- Why playwright is better than selenium webdriver, is it?
- Handle dropdowns in Playwright Python
- Open the browser and Close in Playwright Python
- Handle checkbox in Playwright Python
- Element Operations in Playwright Python
- Handle IFrames in Playwright Python
- Page level commands in Playwright Python
- Element State with Playwright Python