I have an element like so:
<span class="myTest">Estimates</span>
How do I use XPath to select this? This is what I have:
x("//*[contains(#class,'myTest')][normalize-space(text())='Estimates']")
Your selector is actually correct, here's a short test using casperjs master branch:
var x = require('casper').selectXPath;
casper.test.begin('your selector is okay', 1, function(test) {
casper.start().setContent('<span class="myTest">Estimates</span>');
test.assertExists(x("//*[contains(#class,'myTest')][normalize-space()='Estimates']"));
test.done();
});
Demo:
$ casperjs test test-xpath.js
Test file: test-xpath.js
# your selector is okay
PASS Found an element matching: xpath selector: //*[contains(#class,'myTest')][normalize-space()='Estimates']
PASS 1 tests executed in 0.118s, 1 passed, 0 failed.
Related
I would like to test some dash plotly UI functions with cypress but I have some problems with ids based on dash Pattern Matching, e.g.:
Trying to use the id, as found in the DOM, I get the following error:
I've tried the test with the following sytax:
cy.get('[id="{"index":0,"type":"dynamic-dropdown"}"]').should('exist')
cy.get('[id={"index":0,"type":"dynamic-dropdown"}]').should('exist')
EDIT:
I've also found this based on the browser developer mode, which will not throw a syntax error but the element can not be found:
cy.get('[id="{"index":0,"type":"dynamic-dropdown"}"]').should('exist')
If you have a look at the element
cy.get('.dash-dropdown')
.then($el => console.log('attributes', $el[0].attributes))
you see a NameNodeMap with three items
0. id
1. index":0,"type":"dynamic-dropdown"}"
2. class
so (at least in chrome) the browser has split the id because of the internal double quotes. The id value is {, the remainder becomes an attribute key with no value.
You can apply a filter based on this pattern
cy.get('.dash-dropdown')
.filter((index, el) => {
const id = el.getAttribute('id')
return id === '{' && el.hasAttribute('index":0,"type":"dynamic-dropdown"}"')
})
.should('have.length', 1)
.and('have.text', 'one') // passes
Tested with
<body>
<div id="{"index":0,"type":"dynamic-dropdown"}" class="dash-dropdown">one</div>
<div id="index-type-dynamic-dropdown" class="dash-dropdown">two</div>
<div id="dynamic-dropdown-type-index" class="dash-dropdown">three</div>
</body>
Single Selection
By changing the outer quotations marks from "single" to "double" (cy.get("")), using "single" quotations marks for the id definition ([id='']) and escaping the inner "double" quotations marks (\"index\") cypress can detect the correct element:
cy.get("[id='{\"index\":0,\"type\":\"dynamic-dropdown\"}']").should('exist')
Selector Approach
Another solution is based on cypress Selector and looks like this:
cy.get('[id*=index][id*=type][id*=dynamic-dropdown]')
It will match multiple elements, which include the words "index", "type" and "dynamic-dropdown" but without any order.
So, ids like the following would also match:
<div id="index-type-dynamic-dropdown"></div>
<div id="dynamic-dropdown-type-index"></div>
I have an HTML element I would like to select that looks like this:
<button data-button-id="close" class="modal__cross modal__cross-web"></button>
Now clearly I can use this XPath selector:
//button[(contains(#data-button-id,'close')) and (contains(#class,'modal'))]
to select the element. However, I would really like to select buttons that have both close and modal contained in any attributes. So I can generalize the selector and say:
//button[(contains(#*,'close')) and (contains(#class,'modal'))]
and that works. What I'd love to do is extend it to this:
//button[(contains(#*,'close')) and (contains(#*,'modal'))]
but that doesn't return any results. So clearly that doesn't mean what I'd like it to mean. Is there a way to do it correctly?
Thanks,
Craig
It looks like you're using XPath 1.0: in 1.0, if you supply a node-set as the first argument to contains(), it takes the first node in the node-set. The order of attributes is completely unpredictable, so there's no way of knowing whether contains(#*, 'close') will succeed or not. In 2.0+, this gives you an error.
In both 1.0 and 2.0, #*[contains(., 'close')] returns true if any attribute contains "close" as a substring.
This expression works:
//button[attribute::*[contains(.,"close")] and attribute::*[contains(.,"modal")]]
Given this html
<button data-button-id="close" class="modal__cross modal__cross-web"></button>
<button key="close" last="xyz_modal"></button>
Testing with xmllint
echo -e 'cat //button[attribute::*[contains(.,"close")] and attribute::*[contains(.,"modal")]]\nbye' | xmllint --html --shell test.html
/ > cat //button[attribute::*[contains(.,"close")] and attribute::*[contains(.,"modal")]]
-------
<button data-button-id="close" class="modal__cross modal__cross-web"></button>
-------
<button key="close" last="xyz_modal"></button>
/ > bye
Try this one to select required element:
//button[#*[contains(., 'close')] and #*[contains(., 'modal')]]
I have a very long website with many script nodes, how can I access to the one that has 'var config' in the text with xpath?
<script>
var config = {
locale: 'es',
userAuthenticated: false
}
<script>
In case this is the only node with script tag name having config attribute you can use the following XPath to locate it:
"//script[#config]"
In case there is some unique value inside the config values, like userAuthenticated here XPath like this could be used:
"//script[contains(#config,'userAuthenticated')]"
UPD
The element you are looking for can be located with the following XPath:
"//script[contains(.,'userAuthenticated')]"
Let's say I want to scrape the "Weight" attribute from the following content on a website:
<div>
<h2>Details</h2>
<ul>
<li><b>Height:</b>6 ft</li>
<li><b>Weight:</b>6 kg</li>
<li><b>Age:</b>6</li>
</ul>
</div>
All I want is "6 kg". But it's not labeled, and neither is anything around it. But I know that I always want the text after "Weight:". Is there a way of selecting an element based on the text near it or in it?
In pseudocode, this is what it might look like:
require 'selenium-webdriver'
require 'nokogiri'
doc = parsed document
div_of_interest = doc.div where text of h2 == "Details"
element_of_interest = <li> element in div_of_interest with content that contains the string "Weight:"
selected_text = (content in element) minus ("<b>Weight:</b>")
Is this possible?
You can write the following code
p driver.find_elements(xpath: "//li").detect{|li| li.text.include?'Weight'}.text[/:(.*)/,1]
output
"6 kg"
My suggestion is to use WATIR which is wrapper around Ruby Selenium Binding where you can easily write the following code
p b.li(text: /Weight/).text[/:(.*)/,1]
Yes.
require 'nokogiri'
Nokogiri::HTML.parse(File.read(path_to_file))
.css("div > ul > li")
.children # get the 'li' items
.each_slice(2) # pair a 'b' item and the text following it
.find{|b, text| b.text == "Weight:"}
.last # extract the text element
.text
will return
"6 kg"
You can locate the element through pure xpath: use the contains() function which returns Boolean is its second argument found in the first, and pass to it text() (which returns the text of the node) and the target string.
xpath_locator = '/div/ul/li[contains(text(), "Weight:")]'
value = driver.find_element(:xpath, xpath_locator).text.partition('Weight:').last
Then just get the value after "Weight:".
I am trying to convert the following xpath //div[#id='sb-search']/form/span[2] to a css selector in order to use it in Selenium.
I have tried the following but no luck in Internet Explorer 11:
search_icon = driver.find_element_by_css_selector('div#sb-search > form > span:nth-child(2)')
You probably meant :nth-of-type(2), not :nth-child(2), since span[2] finds the second span, not the second child when it is a span:
search_icon = driver.find_element_by_css_selector('div#sb-search > form > span:nth-of-type(2)')