javascriptautomated-testsplaywright

How to get closest parent of element in Playwright?


I am quite new in playwright and i cannot find solution for how to get parent of element? Here is what i have so far:

    const deliveryItems = await page.locator('.js-delivery-container'); //Items
    const deliveryMethod = deliveryItems.locator('input[value="50"]'); //input in items selected by value
    const example_parent = await deliveryMethod.closest('.js-delivery-name'); //name what i want to check
    await expect(example_parent).toHaveText('Osobní odběr'); //check if selected right delivery

But when i run it.. there is a Error what popup in console:

 TypeError: deliveryMethod.closest is not a function

  33 |
  34 |         const deliveryMethod = deliveryItems.locator('input[value="50"]');
> 35 |         const example_parent = await deliveryMethod.closest('.js-delivery-name');

Can anyone explain me this? Thanks

My structure looks like this:

<div class="select-content-box js-delivery-container">
   <div class="select-content delivery-icon-factory">
        <div class="extended-title transport-icon js-delivery-name">Osobní odběr</div>
        <div class="extended-price js-delivery-price"></div>
        <div class="select-btn">
            <label class="label toggle">
                <input type="radio" name="radio-delivery" value="50">
                <div class="toggle-control"></div>
            </label>
        </div>
        <div class="cleaner"></div>
        <div class="select-address" style=""></div>
    </div>
</div>

Solution

  • The ElementHandle.closest() method you are trying does not exist in the API at the moment (or is not yet available in the Playwright version you use, I only found it in this discussion #6015). That's the reason for the error.

    The problem

    You want to find a connection between the input field based on its value (input[value="50"]) and the .js-delivery-name element which is not possible, or at least any .parentElement?.parentElement?.nextSibling?.nextSibling... structure would be extremely fragile, same applies to XPath axes (they are not even in child-ancestor relation here but a kinda "second niece/nephew-second aunt/uncle" relation).

    Possible solution

    I suggest the following. "Playwright can select elements based on the page layout. These can be combined with regular CSS for better results."

    1-2.) desired name within + delivery items: using the child combinator and universal selector you can compose patterns that can grab your desired elements within a container element, e.g.: .js-delivery-container > * .js-delivery-name

    3.) above this input: when you have the exact selector then you can grab it by finding the closest element matches another CSS selector query in specific directions with :above(), :below(), :near(), :left-of() and :right-of() methods, e.g. :above(input[value="50"]). As the docs says: "Matches elements that are above any of the elements matching the inner selector".

    //                                delivery items       desired name within    above this input     
    //                           ______________________  __  _______________  ________________________
    //                          |                      ||  ||               ||                        |
    const parent = page.locator('.js-delivery-container > * .js-delivery-name:above(input[value="50"])')
    await expect(parent).toHaveText(/Osobní odběr/)
    

    Note: Layout selectors use bounding client rect to compute the distance and relative position of the elements, so these above, below etc. directions are strictly meant for layout position and not for DOM ancestry.