Location>code7788 >text

The Latest Out series - Python+Playwright Automated Testing - 66 - Waiting for elements to reach a specified state (appear, remove, show and hide)

Popularity:635 ℃/2024-08-08 08:23:55

1. Introduction

When performing UI automation tests in our daily work, it is crucial to ensure the stability of the tests. One of the key aspects is the correct positioning and manipulation of elements in a web page. In a web page, elements may be in different states, some may not be in the DOM until the page has finished loading and require some manipulation to appear, while other elements may have been present in the DOM all along but initially in a hidden state and require manipulation to make them appear and then become visible. Therefore, if the state of an element is not taken into account when executing a script, it is likely that the script will fail. To ensure the stability of automated tests, we need to make sure that the required elements have reached the specified state before executing the action.

The following macro will introduce and analyze to explain three common ways to wait for elements: wait_for_timeout(),wait_for(), wait_for_selector() and wait_for_element_state() and the advantages and disadvantages between the four.

2. Forced waiting

2.1wait_for_timeout()

wait_for_timeout() method will wait for the time specified when the method is called.

This method is used to set a wait timeout, which allows the program to wait a specified amount of time before performing certain operations. If the operation is not completed within the set time, a timeout error may be thrown. This mechanism is very useful in programming, especially when you need to wait for a condition to be met or a resource to become available. For example, when using theplaywrightWhen performing automated testing of web pages, thewait_for_timeout()method can be used to ensure that a web page element has finished loading or is in an actionable state before proceeding with the operation. If the element is not loaded within the given time, the situation can be handled by catching a timeout error, thus preventing the operation from failing. The officially defined function is as follows:

    def wait_for_timeout(self, timeout: float) -> None:
        """Page.wait_for_timeout

        Waits for the given `timeout` in milliseconds.

        Note that `()` should only be used for debugging. Tests using the timer in production are going
        to be flaky. Use signals such as network events, selectors becoming visible and others instead.

        **Usage**

        ```py
        # wait for 1 second
        await page.wait_for_timeout(1000)
        ```

        ```py
        # wait for 1 second
        page.wait_for_timeout(1000)
        ```

        Parameters
        ----------
        timeout : float
            A timeout to wait for
        """

        return mapping.from_maybe_impl(
            self._sync(self._impl_obj.wait_for_timeout(timeout=timeout))
        )

3. Automatic waiting

3.1.wait_for()

wait_for() locates the element first and then waits for the element to reach the specified state. Positioning the element first and then using the wait_for() method also waits for the element to reach the specified state.

If the element has satisfied the condition, it returns immediately. Otherwise, it waits until the timeout period is reached.

This method accepts two keyword arguments:
timeout: Specify the maximum wait time in milliseconds. The default is 30000 (30 seconds), but can be changed.
state: specify the state to wait for. Defaults to 'visible'. Can be one of 'attached', 'detached', 'hidden' or 'visible '.

The officially defined functions are as follows:

    def wait_for(
        self,
        *,
        timeout: [float] = None,
        state: [
            Literal["attached", "detached", "hidden", "visible"]
        ] = None
    ) -> None:
        """Locator.wait_for

        Returns when element specified by locator satisfies the `state` option.

        If target element already satisfies the condition, the method returns immediately. Otherwise, waits for up to
        `timeout` milliseconds until the condition is met.

        **Usage**

        ```py
        order_sent = (\"#order-sent\")
        await order_sent.wait_for()
        ```

        ```py
        order_sent = (\"#order-sent\")
        order_sent.wait_for()
        ```

        Parameters
        ----------
        timeout : Union[float, None]
            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can
            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.
        state : Union["attached", "detached", "hidden", "visible", None]
            Defaults to `'visible'`. Can be either:
            - `'attached'` - wait for element to be present in DOM.
            - `'detached'` - wait for element to not be present in DOM.
            - `'visible'` - wait for element to have non-empty bounding box and no `visibility:hidden`. Note that element
              without any content or with `display:none` has an empty bounding box and is not considered visible.
            - `'hidden'` - wait for element to be either detached from DOM, or have an empty bounding box or
              `visibility:hidden`. This is opposite to the `'visible'` option.
        """

        return mapping.from_maybe_impl(
            self._sync(self._impl_obj.wait_for(timeout=timeout, state=state))
        )

Macro or according to the previous toast of the message that demo to demonstrate, here do not write demo HTML code, do not know you can look at Macro's previous articles:portal

3.1.1 Code design

3.1.2 Reference code
# coding=utf-8πŸ”₯

# 1. First set the encoding, utf-8 can support Chinese and English, such as the above, generally placed in the first line

# 2. Notes: including record creation time, creator, project name.
'''
Created on 2024-07-16
@author: Beijing, capital of People's *-* businessman
Beijing Hongge (search: Beijing Hongge, follow Hongge, unlock more testing dry goods in advance!)
Project: "The latest out of the box" series into a small chapter - Python + Playwright automated testing - 66 - waiting for the element to the specified state
'''

# 3. Import module
from playwright.sync_api import Playwright, sync_playwright, expect

def run(playwright: Playwright) -> None:

    browser = (headless=False)
    context = browser.new_context()
    page = context.new_page()
    ("E:/Desktop/test/")
    # Click the Click to follow button
    ("#hongge").click()
    # Wait for an element to appear in the dom
    ('//html/body/div').wait_for(state="attached")
    # Get element text
    print(('//html/body/div').inner_text())
    ('//html/body/div').wait_for(state="detached")
    print("element has been removed from the DOM")
    page.wait_for_timeout(1000)
    ()
    ()

with sync_playwright() as playwright:
    run(playwright)
3.1.3 Running the code

1. Run the code, right click Run 'Test', you can see the console output, as shown below:

2. Run the code after the browser action on the computer side. As shown in the figure below:

3.2wait_for_selector()

page.wait_for_selector() is a method in Playwright that waits for an element matching the specified CSS selector to appear on the page.

This method accepts a selector parameter and an optional option parameter. Commonly used option parameters include.

  • visible: Specifies that the element must be visible, defaults toFalseγ€‚β€Œ
  • hidden: Specifies that the element must be hidden, defaults toFalseγ€‚β€Œ
  • state: can be set tovisibleγ€β€Œhiddenγ€β€Œattached maybedetached, which is used to wait for an element to reach a specific state.
  • timeout: Sets the timeout for waiting, in milliseconds. If the element does not reach the waiting state within the specified time, a timeout exception is thrown.

The officially defined functions are as follows:

    def wait_for_selector(
        self,
        selector: str,
        *,
        state: [
            Literal["attached", "detached", "hidden", "visible"]
        ] = None,
        timeout: [float] = None,
        strict: [bool] = None
    ) -> ["ElementHandle"]:
        """ElementHandle.wait_for_selector

        Returns element specified by selector when it satisfies `state` option. Returns `null` if waiting for `hidden` or
        `detached`.

        Wait for the `selector` relative to the element handle to satisfy `state` option (either appear/disappear from dom,
        or become visible/hidden). If at the moment of calling the method `selector` already satisfies the condition, the
        method will return immediately. If the selector doesn't satisfy the condition for the `timeout` milliseconds, the
        function will throw.

        **Usage**

        ```py
        await page.set_content(\"<div><span></span></div>\")
        div = await page.query_selector(\"div\")
        # waiting for the \"span\" selector relative to the div.
        span = await div.wait_for_selector(\"span\", state=\"attached\")
        ```

        ```py
        page.set_content(\"<div><span></span></div>\")
        div = page.query_selector(\"div\")
        # waiting for the \"span\" selector relative to the div.
        span = div.wait_for_selector(\"span\", state=\"attached\")
        ```

        **NOTE** This method does not work across navigations, use `page.wait_for_selector()` instead.

        Parameters
        ----------
        selector : str
            A selector to query for.
        state : Union["attached", "detached", "hidden", "visible", None]
            Defaults to `'visible'`. Can be either:
            - `'attached'` - wait for element to be present in DOM.
            - `'detached'` - wait for element to not be present in DOM.
            - `'visible'` - wait for element to have non-empty bounding box and no `visibility:hidden`. Note that element
              without any content or with `display:none` has an empty bounding box and is not considered visible.
            - `'hidden'` - wait for element to be either detached from DOM, or have an empty bounding box or
              `visibility:hidden`. This is opposite to the `'visible'` option.
        timeout : Union[float, None]
            Maximum time in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can
            be changed by using the `browser_context.set_default_timeout()` or `page.set_default_timeout()` methods.
        strict : Union[bool, None]
            When true, the call requires selector to resolve to a single element. If given selector resolves to more than one
            element, the call throws an exception.

        Returns
        -------
        Union[ElementHandle, None]
        """

        return mapping.from_impl_nullable(
            self._sync(
                self._impl_obj.wait_for_selector(
                    selector=selector, state=state, timeout=timeout, strict=strict
                )
            )
        )

1. Wait for the element to appear in the DOM

page.wait_for_selector("Positioning methods", state='attached')

2. Waiting to be removed from the DOM

page.wait_for_selector("Positioning methods", state='detached')

3. Wait for the element to be visible

page.wait_for_selector("Positioning methods", state="visible")

4. Wait for the element to be invisible (hidden)

page.wait_for_selector("Positioning methods", state='hidden')

If no state parameter is passed, the default is to wait for the element to be visible.

page.wait_for_selector("Positioning methods")
3.2.1 Waiting for elements to appear and be removed

By default, there is no such element inside the DOM node of the HTML page, and this element appears only through certain actions (clicking the [Click to Follow] button), as shown in the following figure:

Macro or according to the previous toast of the message that demo to demonstrate, here do not write demo HTML code, do not know you can look at Macro's previous articles:portal

3.2.1.1 Code design

3.2.1.2 Reference code
# coding=utf-8πŸ”₯

# 1. First set the encoding, utf-8 can support Chinese and English, such as the above, generally placed in the first line

# 2. Notes: including record creation time, creator, project name.
'''
Created on 2024-07-16
@author: Beijing, capital of People's *-* businessman
Beijing Hongge (search: Beijing Hongge, follow Hongge, unlock more testing dry goods in advance!)
Project: "The latest out of the box" series into a small chapter - Python + Playwright automated testing - 66 - waiting for the element to the specified state
'''

# 3. Import module
from playwright.sync_api import Playwright, sync_playwright, expect

def run(playwright: Playwright) -> None:

    browser = (headless=False)
    context = browser.new_context()
    page = context.new_page()
    ("E:/Desktop/test/")
    # Click the Click to follow button
    ("#hongge").click()
    # Wait for an element to appear in the dom
    loc_msg = page.wait_for_selector('//html/body/div', state="attached")
    # Get element text
    print(loc_msg.inner_text())
    page.wait_for_selector('//html/body/div', state="detached")
    print("element has been removed from the DOM")
    page.wait_for_timeout(1000)
    ()
    ()

with sync_playwright() as playwright:
    run(playwright)
3.2.1.3 Running the code

1. Run the code, right click Run 'Test', you can see the console output, as shown below:

2. Run the code after the browser action on the computer side. As shown in the figure below:

3.2.2 Waiting for elements to show and hide

By default, an element in an HTML page is in the DOM itself, but it changes state through certain operations: hidden and shown. The following prompt is in the DOM, but it is hidden by default. Macro also found one such scenario in the previous demo demo, which was just used for the demo.

3.2.2.1 Code design

3.2.2.2 Reference code
# coding=utf-8πŸ”₯

# 1. First set the encoding, utf-8 can support Chinese and English, such as the above, generally placed in the first line

# 2. Notes: including record creation time, creator, project name.
'''
Created on 2024-07-16
@author: Beijing, capital of People's *-* businessman
Beijing Hongge (search: Beijing Hongge, follow Hongge, unlock more testing dry goods in advance!)
Project: "The latest out of the box" series into a small chapter - Python + Playwright automated testing - 66 - waiting for the element to the specified state
'''

# 3. Import module
from playwright.sync_api import Playwright, sync_playwright, expect

def run(playwright: Playwright) -> None:

    browser = (headless=False)
    context = browser.new_context()
    page = context.new_page()
    ("/demo/")
    #Click the Hide button
    ("//html/body/form/input[4]").click()
    page.wait_for_selector("#uv", state="hidden")
    print("Element has been hidden")
    page.wait_for_timeout(1000)
    # Click the Show button
    ("//html/body/form/input[5]").click()
    loc_msg = page.wait_for_selector("#uv", state="visible")
    print("element has been displayed")
    # Get element text
    print(loc_msg.inner_text())
    page.wait_for_timeout(1000)
    ()
    ()

with sync_playwright() as playwright:
    run(playwright)
3.2.2.3 Running the code

1. Run the code, right click Run 'Test', you can see the console output, as shown below:

2. Run the code after the computer side of the browser's actions (Note: using visibility show and hide, you can lengthen the waiting time, see more clearly). As shown in the figure below:

3.3Β wait_for_element_state()

wait_for_load_state(), wait for the event to be triggered. Wait for the event triggered by the previous button to finish loading before doing the following.

In Python's Playwright library, thewait_for_load_state()method is used to wait for the page to reach a specific loading state. The method accepts three parameters.

  • state: the state in which the page should load.loadγ€β€Œdomcontentloadedmaybenetworkidle. Each of these states represents a different level of loading of the page, with theloadindicates that the page is fully loaded.domcontentloadedindicates that the document content has been loaded, and thenetworkidleThen it means that the network has almost no connection, i.e., page loading is complete.

  • timeout: the maximum time to wait, in milliseconds. The default value is 30 * 1000, i.e. 30 seconds. This parameter is used to set the maximum timeout for the wait operation, in order to avoid the program waiting for a long time and unable to continue execution.

  • wait_until: the type of event to wait for, which can beloadγ€β€Œdomcontentloadedγ€β€Œnetworkidle0maybenetworkidle2One of the following. This parameter is used to specify the specific type of event to wait for in order to more precisely control the conditions of the wait.

By using thewait_for_load_state()method, which ensures that the page is fully loaded before proceeding to subsequent operations, thus avoiding operation failures or errors caused by page elements not being fully loaded.

The officially defined functions are as follows:

    def wait_for_load_state(
        self,
        state: [
            Literal["domcontentloaded", "load", "networkidle"]
        ] = None,
        *,
        timeout: [float] = None
    ) -> None:
        """Page.wait_for_load_state

        Returns when the required load state has been reached.

        This resolves when the page reaches a required load state, `load` by default. The navigation must have been
        committed when this method is called. If current document has already reached the required state, resolves
        immediately.

        **Usage**

        ```py
        await page.get_by_role(\"button\").click() # click triggers navigation.
        await page.wait_for_load_state() # the promise resolves after \"load\" event.
        ```

        ```py
        page.get_by_role(\"button\").click() # click triggers navigation.
        page.wait_for_load_state() # the promise resolves after \"load\" event.
        ```

        ```py
        async with page.expect_popup() as page_info:
            await page.get_by_role(\"button\").click() # click triggers a popup.
        popup = await page_info.value
        # Wait for the \"DOMContentLoaded\" event.
        await popup.wait_for_load_state(\"domcontentloaded\")
        print(await ()) # popup is ready to use.
        ```

        ```py
        with page.expect_popup() as page_info:
            page.get_by_role(\"button\").click() # click triggers a popup.
        popup = page_info.value
        # Wait for the \"DOMContentLoaded\" event.
        popup.wait_for_load_state(\"domcontentloaded\")
        print(()) # popup is ready to use.
        ```

        Parameters
        ----------
        state : Union["domcontentloaded", "load", "networkidle", None]
            Optional load state to wait for, defaults to `load`. If the state has been already reached while loading current
            document, the method resolves immediately. Can be one of:
            - `'load'` - wait for the `load` event to be fired.
            - `'domcontentloaded'` - wait for the `DOMContentLoaded` event to be fired.
            - `'networkidle'` - **DISCOURAGED** wait until there are no network connections for at least `500` ms. Don't use
              this method for testing, rely on web assertions to assess readiness instead.
        timeout : Union[float, None]
            Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can
            be changed by using the `browser_context.set_default_navigation_timeout()`,
            `browser_context.set_default_timeout()`, `page.set_default_navigation_timeout()` or
            `page.set_default_timeout()` methods.
        """

        return mapping.from_maybe_impl(
            self._sync(self._impl_obj.wait_for_load_state(state=state, timeout=timeout))
        )

Macro found a demo at this site: https:/// where the text only appears when loading is complete, and then it is positioned to print the text.

3.3.1 Code design

3.3.2 Reference code
# coding=utf-8πŸ”₯

# 1. First set the encoding, utf-8 can support Chinese and English, such as the above, generally placed in the first line

# 2. Notes: including record creation time, creator, project name.
'''
Created on 2024-07-16
@author: Beijing, capital of People's *-* businessman
Beijing Hongge (search: Beijing Hongge, follow Hongge, unlock more testing dry goods in advance!)
Project: "The latest out of the box" series into a small chapter - Python + Playwright automated testing - 66 - waiting for the element to the specified state
'''

# 3. Import module
from playwright.sync_api import Playwright, sync_playwright, expect

def run(playwright: Playwright) -> None:

    browser = (headless=False)
    context = browser.new_context()
    page = context.new_page()
    ("https:///demo/jquery-jdt20160820/")
    page.wait_for_load_state()
    print(('//*[@]/i[4]/span').inner_text())
    ()
    ()

with sync_playwright() as playwright:
    run(playwright)
3.3.3 Running the code

1. Run the code, right click Run 'Test', you can see the console output, as shown below:

2. Run the code after the browser action on the computer side. As shown in the figure below:

Knock on the blackboard! ! 1. Note that the automated test is loaded not more than the default time of this method, the timeout will report an error haha! This site needs to load 40S before the text appears, as shown below:

3.3.4 Code design

3.3.5 Reference code
# coding=utf-8πŸ”₯

# 1. First set the encoding, utf-8 can support Chinese and English, such as the above, generally placed in the first line

# 2. Notes: including record creation time, creator, project name.
'''
Created on 2024-07-16
@author: Beijing, capital of People's *-* businessman
Beijing Hongge (search: Beijing Hongge, follow Hongge, unlock more testing dry goods in advance!)
Project: "The latest out of the box" series into a small chapter - Python + Playwright automated testing - 66 - waiting for the element to the specified state
'''

# 3. Import module
from playwright.sync_api import Playwright, sync_playwright, expect

def run(playwright: Playwright) -> None:

    browser = (headless=False)
    context = browser.new_context()
    page = context.new_page()
    ("/demo/php/")

    page.wait_for_load_state()

    print(("//html/body/div").inner_text())
    print(("//html/body/span").inner_text())
    ()
    ()

with sync_playwright() as playwright:
    run(playwright)
3.3.6 Running the code

1. Run the code, right Run 'Test', you can see the console output (. Run the code console error, because loading this page takes 40s, and this method defaults to 30000ms = 30s, over the error), as shown below:

2. Run the code after the browser action on the computer side. As shown in the figure below:

4. Summary

4.1 Differences in the use of wait_for() and wait_for_selector()

Difference between wait_for() method and wait_for_selector() usage:

('locate element').wait_for() returns None, you can't continue to manipulate the element after that.

page.wait_for_selector("locator method") returns a locator object, which can be used later to manipulate the elements.

Well, it's getting late today, so Hong will explain and share here, thank you for your patience in reading, and I hope it will be helpful to you.