This question already has answers here:
RSpec: Expect to change multiple
(4 answers)
Closed 3 years ago.
It's easy to use Capybara Matcher on an element with single check like
expect(element).to have_selector('#selector')||
BUT
How do I achieve the same when asserting on an element that is part of a list and multiple checks need to be done to assert.
I want to do something like:
<products>
<product>
<product>
<product>
</products>
def have_product(name,price)
products.any? {|product| product.have_text(name) && product.have_text(price)} # pseudo code
end
I want to be able to check whether a product with given name and price(both need to match) exists in the list.
Simplest solution assuming you have already found the <products> element and don't need it to be the same product would be
expect(products).to have_css('product', text: name).and(have_css('product', text: price))
if it needs to be both texts in the same product element then use a regex
expect(products).to have_css('product', text: /#{product}.*#{price}/)
More advanced would be creating custom selector types for dealing with the types of objects in your UI.
UPDATE:
With the clarification of the original issue provided, the best solution is probably to use an optional filter block
expect(products).to have_css('product', text: name) { |node|
node.has_field?(with: price)
}
Related
This question already has answers here:
How to select first element via XPath?
(2 answers)
How to select the first element with a specific attribute using XPath
(9 answers)
Closed 3 years ago.
I'm trying the following XPath:
//*[local-name()='SN102'][1]
Using XPathTester, I saved my scenario
http://www.xpathtester.com/xpath/94ee37e08960247a7bf0619d38c52bee
Not every HL1Loop has a SN102.
Otherwise, I could this:
//*[local-name()='HLLoop1'][1]//*[local-name()='SN102']
I have simplified the sample data down to the following:
<ns0:X12_00401_856 xmlns:ns0="http://schemas.microsoft.com/BizTalk/EDI/X12/2006">
<ns0:HLLoop1>
<ns0:SN1>
<SN102>1</SN102>
<SN103>EA</SN103>
<SN108>AC</SN108>
</ns0:SN1>
</ns0:HLLoop1>
<ns0:HLLoop1>
<ns0:SN1>
<SN102>2</SN102>
<SN103>EA</SN103>
<SN108>AC</SN108>
</ns0:SN1>
</ns0:HLLoop1>
</ns0:X12_00401_856>
The result is coming back with all nodes, not just the first one:
<?xml version="1.0" encoding="UTF-8"?>
<result>
<SN102>1</SN102>
<SN102>2</SN102>
</result>
How do I select the first node only. Seems simply, and I'm sure I've done it before, but not working today.
I have a "Vendor Simulator" program that is building fake 856 data to send back, and I want to increase the first quantity only to force some error handling logic.
Just select the first element of the whole nodelist
(//*[local-name()='SN102'])[1]
The original query //*[local-name()='SN102'][1] would have selected the first SN102 if there had been several siblings of the same name.
I want to extract only the body node/tag from an XML file using doc.xpath in Ruby
The node to extract from the XML file:
<wcm:element name="Body"><p>A new study suggests that <a href="ssNODELINK/SmokingAndCancer">tobacco</a> companies may be using online video portals, such as YouTube, to get around advertising restrictions and market their products to young people.</p>
</wcm:element>
I have tried the following:
page_content = doc.xpath("/wcm:root/wcm:element").inner_text
But this extracts every node everything
Then I tried this:
page_content = doc.xpath("/wcm:root/wcm:element/Body")
But does not work.
Anyone has any suggestions how to extract exactly the body section of an XML file using doc.xpath in Ruby?
I'm not 100% certain I've understood what you mean but… let's not let that stop us. You want to get the content of a particular node from the input. Your first XPath statement:
/wcm:root/wcm:element
is extracting every element with name wcm:element that is a child of the wcm:root element which is the root element.
Your second:
/wcm:root/wcm:element/Body
is similar but looks for elements with name Body which are children of the wcm:element.
What you need to is to get the values of the wcm:element element where the attribute name is set to the value Body. You access attributes in XPath by prefixing them with an # sign and to express a where condition you use [...] - a predicate. You XPath statement needs to be:
/wcm:root/wcm:element[#name = 'Body']
I'm assuming that your XPath execution environment is fine the namespace prefixes (wcm) because you say that your first query returned content.
Is there a way to apply XPath's starts-with function to a node's name instead of its value? I want to select the FOObar and FOObaz nodes from the following XML document without selecting the notFOO node:
<?xml version="1.0" encoding="UTF-8" ?>
<RootNode>
<FOObar xmlns="http://sample.example.com">
<value>numOne</value>
</FOObar>
<FOObaz xmlns="http://sample.example.com">
<value>numTwo</value>
</FOObaz>
<notFOO xmlns="http://sample.example.com">
<value>numThree</value>
</notFOO>
</RootNode>
I get that it's possible to use starts-with to search based on text nodes, e.g.
//sample:value[starts-with(.,'num')]
Is there a way to write the following that is syntactically valid?
//sample:[starts-with(node(),'FOO')]
This question originally came with an SSCCE, but now that the question is answered, all that code is just clutter. It's still available in the revision history, of course.
Use the name() or local-name() functions to refer to nodes by name:
//*[starts-with(local-name(), 'FOO')]
I'm using Nokogiri to parse a large XML file. Say I've got the following structure:
<menagerie>
<penguin>Pablo</penguin>
<penguin>Mortimer</penguin>
<bull>Ferdinand</bull>
<aardvark>James Cornelius Madison Humphrey Zophar Handlebrush III</aardvark>
</menagerie>
I can count the non-penguins like this:
xml.xpath('//menagerie//*[not(penguin)]').length // 2
But how do I get a list of the tags, like this? (The exact format isn't important; I just want to visually scan the non-penguins.)
bull
aardvark
Update
This gave me the list I wanted - thanks Oded and TMN and delnan!
xml.xpath('//menageries/*[not(penguin)]').each do |node|
puts node.name()
end
You can use the name() or local-name() XPath function.
See the examples on zvon.
I know it's a bit outdated but you should do: xml.xpath('//meagerie/*[not(penguin)]/name()') as the expression. Note the slash, not the dot. This is how you call methods on the current node in XPath.
Alright I have an xml document that looks something like this:
<xml>
<list>
<partner>
<name>Some Name</name>
<status>active</status>
<id>0</id>
</partner>
<partner>
<name>Another Name</name>
<status>active</status>
<id>1</id>
</partner>
</list>
</xml>
I am using ruby's lib-xml to parse it.
I want to find if there is a partner with the name 'Some Name' in a quick and ruby idiomatic way.
How can I do this in one line or ruby code, assuming i have a the document parsed in a variable named document.. Such that i can call document.find(xpath) to retrieve nodes. I have had to do this multiple times in slightly different scenarios and now its starting to bug me.
I know i can do the following (but its ugly)
found = false
document.find('//partner/name').each do |name|
if (name.content == 'Some Name')
found = true
break
end
end
assert(found, "Some Name should have been found")
but i find this really ugly. I thought about using the enumeration include? mixin method but that still won't work because I need to get the .content field of each node as opposed to the actual node...
While writing this, I though of this (but it seems somewhat inefficient albeit elegant)
found = document.find('//partner/name').collect{|name| name.content}.member?("Some Name")
Are there any other ways of doing this?
What about this?
found = document.find("//partner[name='Some Name']").empty?
I tried this solution:
found = document.find("//partner[name='Some Name']") != nil
but I got an error saying the xpath expression was invalid.
However, i was reading some xpath documentation it it looks like you can call a text() function in the expression to get the text node. I tried the following and it appears to work:
found = document.find("//partner/name/text()='Some Name'")
found actually is not a xml node but a true/false object so this works.
I would use a language that natively operates on XML (XQuery for example). With XQuery it is possible to formulate this sort of queries over xml data in a concise and elegant way.