invzbl3
invzbl3

Reputation: 6450

Usage of double dots via Selenium

I'm using Selenium for getting an element of DOM. And I confused a bit with double dots using xpath.

Literally, it means:

.. (double dots) - this will select the parent of the current node.

Also I checked the article that explains "Using double dots(..)" like:

Suppose you have a form where you need to type email id. To find email textbox, first you need to search email label and then you need to go back to input node to type.

For example: //label[text()=’Email’]/../input

Then I tried to use .. by the same logic in my use case for DOM:

<li class="b-online-edit b-product-gallery__item  js-rtb-partner" data-qaid="product-block" data-product-id="97423911" data-tg-chain="{&#34;view_type&#34;: &#34;preview&#34;}">
    <a class="b-online-edit__link" data-edit-role="productInfo" data-edit-id="97423911" title="Редактировать товар" style="display:none"></a>
    <a class="b-online-edit__horizontal-borders" data-edit-role="productInfo" data-edit-id="97423911" style="display:none"></a>
    <a class="b-online-edit__vertical-borders" data-edit-role="productInfo" data-edit-id="97423911" style="display:none"></a>
    <div class="b-product-gallery__sku" title="Код:">
        <span title="A2VG0Y0">A2VG0Y0</span>
    </div>
    <a class="b-product-gallery__image-link" href="..." title="DU-104, фотобарабан,  Drum Unit,  Konica Minolta Bizhub C6000 C7000" data-tg-became-visible="[{&#34;ns&#34;: &#34;ontheio&#34;, &#34;method&#34;: &#34;pageviewsProduct&#34;, &#34;args&#34;: [{}], &#34;payloads&#34;: {&#34;oi_common&#34;: 97423911}, &#34;uuid&#34;: &#34;61796d2f952eaa08f1d5a4a94125f864f0899cf8e7e3e470b25b382432ff0d3b&#34;}]" data-tg-clicked="[{&#34;ns&#34;: &#34;ontheio&#34;, &#34;method&#34;: &#34;productClick&#34;, &#34;args&#34;: [{}], &#34;payloads&#34;: {&#34;oi_common&#34;: 97423911}, &#34;uuid&#34;: &#34;e57184c4caed5461dd02a34817916b6351bbdb4f7c2b6f340368df85678d4ef6&#34;}]">
        <img class="b-product-gallery__image" src="https://images.ua.prom.st/178769613_w200_h200_du-104-fotobaraban-drum.jpg" alt="DU-104, фотобарабан,  Drum Unit,  Konica Minolta Bizhub C6000 C7000"/>
        <i class="b-product-gallery__no-image"></i>
    </a>
    <a class="b-product-gallery__title" href="..." id="link_to_product_97423911" data-tg-clicked="[{&#34;ns&#34;: &#34;ontheio&#34;, &#34;method&#34;: &#34;productClick&#34;, &#34;args&#34;: [{}], &#34;payloads&#34;: {&#34;oi_common&#34;: 97423911}, &#34;uuid&#34;: &#34;e57184c4caed5461dd02a34817916b6351bbdb4f7c2b6f340368df85678d4ef6&#34;}]">DU-104, фотобарабан,  Drum Unit,  Konica Minolta Bizhub C6000 C7000</a>
    <div class="b-product-gallery__prices">
        <span class="b-product-gallery__current-price">6 690&nbsp;<span class="notranslate">грн.</span></span>
    </div>
    <div class="b-product-gallery__data">
        <span class="b-product-gallery__state">Ожидается</span>
        <i class="b-product-gallery__data-hider"></i>
    </div>

Using code-snippets:

driver.findElement(By.xpath("//*[contains(text(),'A2VG0Y0')]/../../a[5]"))

driver.findElement(By.xpath("//*[contains(text(),'DU-104, ... C6000 C7000')]/../div[1]/span"))

It works fine, but I want to understand fully, can I change .. to parent explicitly in the same way if .. relates to parent?

Because if I change, e.g. like:

driver.findElement(By.xpath("//*[contains(text(),'DU-104')]/ul/li[2]/div[1]/span"))

or

driver.findElement(By.xpath("//*[contains(text(),'DU-104')]/li[2]/div[1]/span"))

it doesn't work.

Is .. the parent that defines implicitly the method contains()? And can I somehow combine these two queries via OR operator?

Upvotes: 0

Views: 1562

Answers (1)

JeffC
JeffC

Reputation: 25597

As you saw, replacing /../ with /li[2]/ doesn't work. If you don't use .. then it's assumed you are moving down the DOM. You can probably imagine a scenario where there might be an /li[2] above AND below the current element... how would the XPath parser be able to determine which direction you want to move?

There's another way to create XPath locators to find these elements without using ... They should be less brittle (prone to break) than locators using .. but get you the same result given the provided HTML.

Given the HTML provided, I'm assuming that the product name is "DU-104, фотобарабан, Drum Unit, Konica Minolta Bizhub C6000 C7000" and the product code is "A2VG0Y0". If I've assumed wrong, you'll have to either let me know and I can fix the labels or just rename them in your head as you read them... :)

If you want a generic locator to find the product name from the product code, you can use the below.

//li[contains(@class,'b-product-gallery__item')][.//span[@title='A2VG0Y0']]//a[@class='b-product-gallery__title']
^ find an LI tag that contains that specific class
                                                ^ that has a descendant SPAN that contains the desired product code
                                                                           ^ where the LI has a descendant A that contains the specific class

I wrapped it in a function so that it's reusable and you just pass in the product code and get back the title

public String getProductNameFromProductCode(String productCode)
{
    return driver.findElement(By.xpath("//li[contains(@class,'b-product-gallery__item')][.//span[@title='" + productCode + "']]//a[@class='b-product-gallery__title']")).getText();
}

and call it like

String productName = getProductNameFromProductCode("A2VG0Y0");

If you want to use the product name and get the product code, you can use the locator below

//li[contains(@class,'b-product-gallery__item')][.//a[.='DU-104, фотобарабан,  Drum Unit,  Konica Minolta Bizhub C6000 C7000']]//span[@title]
^ find an LI tag that contains the specific class
                                                ^ that has a descendant A that contains the product name
                                                                                                                               ^ where the LI has a descendant SPAN that has a title attribute

Putting this locator into a reusable function gives you...

public String getProductCodeFromProductName(String productName)
{
    return driver.findElement(By.xpath("//li[contains(@class,'b-product-gallery__item')][.//a[.='" + productName + "']]//span[@title]")).getText();
}

and call it like

String productCode = getProductCodeFromProductName("DU-104, фотобарабан,  Drum Unit,  Konica Minolta Bizhub C6000 C7000");

Upvotes: 1

Related Questions