Xpath: How to select following siblings until a similar current node - html

<tr><strong>dynamic title</strong></tr>
<tr>1</tr>
<tr>2</tr>
<tr>3</tr>
<tr><strong>static title</strong></tr>
<tr>4</tr>
<tr>5</tr>
<tr><strong>dynamic title</strong></tr>
<tr>6</tr>
<tr>7</tr>
<tr><strong>dynamic title</strong></tr>
<tr>8</tr>
<tr>9</tr>
Given the above scenario, I want to select the values 4 and 5 using the static title as a marker.
//tr[preceding-sibling::tr[strong][contains(.,"static title")]] or
//tr[strong[contains(.,"static title")]]/following-sibling::tr
This will select 6,7,8 and 9 too.
Is there a way to make it select the first preceding-sibling with strong and then check for the contains? Or maybe we can use count() somehow?

You can insert [1] after strong which means "the first strong preceding sibling must be static":
//tr[preceding-sibling::tr[strong][1][contains(.,"static title")]]

This looks little messy, but should work:
//tr[contains(strong,"static title")]/following-sibling::tr[strong][1]/preceding-sibling::tr[preceding-sibling::tr[contains(strong,"static title")]]

Related

select only and element on a list of xpaths

$x("//div[#class='card-info__container']/div[3]/a")
using this I've found the container, but now I can't get the specific element I need.
when I enter this the response is:
⯆(3) [a.cta.btn, a.cta.btn, a.cta.btn]
⯈0: a.cta.btn
⯈1: a.cta.btn
⯈2: a.cta.btn
how can I take only the first one?
You can use the at() method to select the first item from the array of a that is returned from executing the XPath:
$x("//div[#class='card-info__container']/div[3]/a").at(0)
For an XPath that returns a list of elements,
xpath
you can select only the first element via indexing:
(xpath)[1]

Html selector using Regex

So there is a page that I want to perform some action on with puppeteer. The problem is that there is a text area in which I want to type in something however the id of it is :
id="pin-draft-title-13a10e18-5a1e-49b9-893c-c5e028dc63e1"
As you might have guess for some reason only pin-draft-title remains the same but the whole number part changes for every refresh so puppeteer can't find it. I tried deleting the id and copying the selector itself the whoe #_Root div>div etc but that seems to be changing after sometime as well. So the main question is is there any way i can just select it using the pin-draft-title part and no matter what numbers follow it still selects it ?
You can use [id^=pin-draft-title-]
In case of javascript, if you want to select all the elements whos ID starts with a specified string or pattern, in your case "pin-draft-title", then consider using the following syntax.
document.querySelectorAll('[id^="pin-draft-title"]');

How to select from a selection box with a variable in the name?

I am having trouble using selecting from this select element.
<select name="vehicle_attrs[position_count]" class="mb1"><option>Position / Quantity</option><option>Front</option><option>Rear</option></select>
I have tried
select('Front', :from=>'mb1')
select('Front', :from=>'vehicle_attrs[position_count]')
select('Front', :from=>'vehicle_attrs[1]')
All of them result in a can not find selection box error
I've never liked how restrictive Capybara's concept of a 'locator' is (i.e. must have a name/id/label), but if you dig into the source code, those helpful methods like select, click_on, and fill_in are just wrappers for find and some native method of Element, which takes arbitrary CSS, and works in almost all situations. In this case, you could use:
find('[name="vehicle_attrs[position_count]"]').find('option', text: 'Front').select_option
Since dropdowns often have multiple similar options, where one is a substring of the other, you might consider using an exact string match, like
find('[name="vehicle_attrs[position_count]"]').find('option', text: /\AFront\z/).select_option
From the docs for select - https://www.rubydoc.info/github/teamcapybara/capybara/Capybara/Node/Actions#select-instance_method - we can see that the from option takes "The id, Capybara.test_id atrtribute, name or label of the select box".
Neither 'mb1' or 'vehicle_attrs[1]' are any of those so they would be expected to fail.
'vehicle_attrs[position_count]' is the name so assuming the box is actually visible on the page (not replaced with a JS driven select widget, etc), that should work. If it doesn't, then edit your question and add the full exact error message you get when trying to use it. Of course if there is only one select box on the page with an option of 'Front' then you don't need to specify the from option at all and can just do
select 'Front'

how to use both (and) and (or) in xpath

I am trying to create an xpath with using (and) and (or) both expression together but not getting success.
I want to grab prices of products but some prices comes as .//p/span[#class='currency-value'] while other comes as .//p/span/span[#class='currency-value'] so I want to use OR exprssion for this
And I don't want price where product is for Advertise so I am using .//span[not(contains(text(),'Ad'))]
I have tried below xpath but its not working.
.//p/span[#class='currency-value'] | .//p/span/span[#class='currency-value'] and .//span[not(.='Ad')]
Rather than saying "try XXX" I think it's useful if you understand what's wrong with your current attempts.
.//p/span[#class='currency-value'] | .//p/span/span[#class='currency-value'] and .//span[not(.='Ad')]
The "|" operator in XPath means "union" - it forms the union of two node-sets. So //x | //y selects the union of the nodes selected by //x and those selected by //y. So far so good. You can simplify the "union" part of your expression to
(.//p/span | .//p/span/span)[#class='currency-value']
if you want.
The "and" is more problematic. The operands of "and" have to be booleans, whereas in your expression both operands are node-sets. I suspect (though I can't be sure) that your intent is to exclude from the union node-set those nodes that satisfy the predicate .='Ad' but without seeing your source data it's not clear how the products and prices relate to each other. Perhaps you intended this:
(.//p/span | .//p/span/span)[#class='currency-value'][not(.='Ad')]
or perhaps this:
(.//p/span | .//p/span/span)[#class='currency-value'][not(..='Ad')]
Either way, if I'm right that your intent is to exclude some of the nodes that would otherwise be selected, then an additional predicate is the way to do it.
After looking at the page, this CSS selector will work
div.hide-lg:not([data-behat-search-results-ads-xl]):not(.prolist-row) p > span.currency-value, div.hide-lg:not([data-behat-search-results-ads-xl]):not(.prolist-row) p > span > span.currency-value
Try this one. Do not overcomplicate with //p/span
//span[#class='currency-value'][not(.='Ad')]
Try this one. Combines #Vitaliy's answer with yours:
.//p/span[#class='currency-value'][not(.='Ad')] | .//p/span/span[#class='currency-value'][not(.='Ad')]
Please find the use of AND and OR in xpath as below:
this is the Page html:
<div class="mtlist-tab">
<ul class="wwf-tab-h">
<li class="tab-head">
<li class="tab-head on">
<li class="tab-head">
</ul>
</div>
if you are using AND in xpath where the two elements have same class class name then it will return 2 matching elements.
//li[#class='tab-head' and #class='tab-head']
if you are using AND in xpath where the two elements have different class class name then it will return 0 matching elements.
//li[#class='tab-head' and #class='tab-head on']
if you are using OR in xpath where the two elements have different class class name then it will return 3 matching elements(according to the HTML).
//li[#class='tab-head' OR #class='tab-head on']

MDX Children of Several Members

The children functions returns the set of the member.
But I need the children of several members.
The problem is, that I can't use Union to make it work like that:
Union([Geography].[Geography].[USA].children,[Geography].[Geography].[Canada].children)
I don't know how many member it will be... So I actually would need all children of a set of members.
like:
([Geography].[Geography].[USA],[Geography].[Geography].[Canada],[Geography].[Geography].[GB]).children
Is there a function like that?
I couldn't answer my question and so I just edit it. With the help of DHN's answer and some brain work I found a solution I could use:
Except(DRILLDOWNLEVEL( {[Geography].[Geography].[USA],[Geography].[Geography].[Canada]},,0 ),
{[Geography].[Geography].[USA],[Geography].[Geography].[Canada]})
That does work for me.
Explanation: I drilldown the elements the tool provides me, which returns children plus parents and then I use DHN's idea and except the parents so clean the list up a bit.
Hopefully it is understandable.
You can use the Descendants method (the fourth form of the description linked uses a set as its first argument. Thus,
Descendants( {
[Geography].[Geography].[USA],
[Geography].[Geography].[Canada],
[Geography].[Geography].[GB]
},
1,
SELF
)
should deliver exactly what you want.
Well actually, you could use a Crossjoin to get the set you want.
Something like
[Geography].[Geography].[USA] * [Geography].[Geography].[Canada] * [Geography].[Geography].[GB]
But this is only a proper solution, if you have only a few different search criteria.
Alternatively, you could use Except to remove those criteria you're not interested in. E.g.
Except([Geography].[Geography].children, [Geography].[Geography].[Germany])
This would give you the whole content of the [Geography] dimension, except the one of [Germany].
Hope this helps a bit.
Edit after comment of TO
Ok, this wasn't part of your question, but I think what you need is the MemberToStr() function. Please find the doc here.
I think something like this should do the trick.
with member [Measures].[Cities]
as membertostr([Geography].[Geography].members.children)
select [Measures].[Cities] on 0
from [WhatEverYourCubeNameIs]
where (
[Geography].[Geography].[USA],
[Geography].[Geography].[Canada]
)
Please note that this query is totally untested. I also may have lost some of my skills, because it's been a while, since I used mdx. You will also have to create the query dynamically, since the selection seems to be user dependant. But I'm sure that you're aware of it. ;)