Mapping element with site prism with repeated correspondences - ruby

I have the following problem: I want to map these elements but there are repeated correspondences.
I don't want to use xpath!.
IF I map using input name or class there will be repeated matches
class CriaScreenCadastro < SitePrism::Page
set_url 'https://quasar-flash-staging.herokuapp.com/#boxCadastro'
#input fields
element :cnpj, :xpath, '//*[#id="boxCadastro"]/div/div[2]/div[1]/div/input'
element :cpf, :xpath, '//*[#id="boxCadastro"]/div/div[2]/div[2]/div/input'
<input name="cnpj" class="sc-fzpisO jHQZJv" value="">
<input name="cpf" class="sc-fzpisO jHQZJv" value="">
end

Related

Selenium Webdriver / XPath: How to find element by attribute and the text of its children

Given html:
<div class="class1">
<div class="class2">1 2 3</div>
<div class="class3">a b c</div>
</div>
As i have several div elements in my html which uses the class "class1" and none has a id i want to find/fetch this parent element by the text of its children.
I tried different variants like
By.xpath("//div[contains(#class, 'class1') "
+ "and text()[contains(.,'1 2 3')] "
+ "and text()[contains(.,'a b c')]]"));
but nothing seems to work yet.
In the example above i guess the text of the class1 element is checked but not of its children.
Can anybody help?
So you're looking for a div with class class1 that has children with texts 1 2 3 and a b c. From your example of what you've tried, I'm assuming there are no further conditions (eg class) on the children:
//div[#class='class1' and div/text()='1 2 3' and div/text()='a b c']
You can make those children node names into * if you don't care whether they are divs or not. You can make the children node names prefixed by descendant:: if you don't require them to be direct children.
Try any of these below mentioned xpath.
Using class attribute of <div> tag.
//div[#class='class2']/..//div[#class='class3']/..//parent::div[#class='class1']
Explanation of xpath: First locate both child elements using the class attribute of <div> tag and then move ahead with parent keyword with <div> tag along with class attribute.
OR
Using text method along with <div> tag.
//div[text()= '1 2 3']/..//div[text()= 'a b c']/..//parent::div[#class='class1']
Explanation of xpath: First locate both child elements using the text method of <div> tag and then move ahead with parent keyword with <div> tag along with class attribute.
These above xpath will locate your parent element <div class="class1">

How can I wrap a div around existing elements?

I'm trying to parse an existing document and modify it by wrapping a div around some existing form elements.
HTML form looks a bit like this:
<form>
<label for="username">Username:</label>
<input name="username" type="text" />
<label for="password">Password:</label>
<input name="password" type="password" />
</form>
I can parse the document OK with Nokogiri and i'm aware of the wrap method but i'm struggling to grasp how to select both the label and input tags in one go and then wrap a div around these. So the result I am looking for is:
<form>
<div class="form-group">
<label for="username">Username:</label>
<input name="username" type="text" />
</div>
<div class="form-group">
<label for="password">Password:</label>
<input name="password" type="password" />
</div>
</form>
I have tried various XPaths / CSS selectors and can create a nodeset of just labels/inputs or all of the elements of the whole form. Is there any way to achieve this modification?
A single XPath expression can only return a single collection of nodes, so in order to achieve what you want you will need to make several queries, one for each label – input pair.
You can select an individual pair with something like this, assuming the markup is well behaved (i.e each input has a label before it):
//label[1] | //label[1]/following-sibling::input[1]
This will select the first label and the following input. However you want to select all such pairs. One way would be to first select all the label nodes, and then for each label select it and the following input.
labels = doc.xpath("//form/label")
labels.each do |l|
nodes = l.xpath(". | ./following-sibling::input[1]")
# nodes now contains a label-input pair...
end
I don’t think the wrap method will work to add a div element as an ancestor to each pair, as it will add the element to each member of the nodeset. You will probably have to move them manually, something like
labels = doc.xpath("//form/label")
labels.each do |l|
# Select this node and its neighbour.
nodes = l.xpath(". | ./following-sibling::input[1]")
# Create the new element, and add it before the label.
div = Nokogiri::XML::Node.new('div', l.document)
l.before(div)
# Move each of the pair onto this new element.
nodes.each do |n|
div.add_child(n)
end
end
Note that this method doesn’t move any text nodes, so you may find the whitespace of your document changes a bit.

mvc3 model bind to multiple lists of the same type

suppose...
in the razor view we have a model of Object A
#Model Object A
Object A is an instance of class A, which has multiple instances of Class B, which have a list of type C
Class A
public B Object1
public B Object2
Class B
public List<C> List1
There is an EditorView for Object B. There is a larger view for Object A in which each object B use an object B EditorView.
#Model Object B
...do things with list in object B
So, when the view renders, there are multiple Editor Views of class B present. What code can I use in the editorview to add additional C to the list List1 in B Object1 without affecting the list List1 in B Object2.
#Model Object B
... would like to add to this list and have it post back, and only effect the b it in. I don't know how to reference this object's master to add to this list exculsively
I looked at http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx a few times. Its like having multiple...
<input type="text" name="[0].Title" value="Curious George" />
<input type="text" name="[0].Author" value="H.A. Rey" />
<input type="text" name="[0].DatePublished" value="2/23/1973" />
<input type="text" name="[0].Title" value="Curious George" />
<input type="text" name="[0].Author" value="H.A. Rey" />
<input type="text" name="[0].DatePublished" value="2/23/1973" />
on the same page and wanting them to post back to their correct masters on the postback. Thats what I want to do. I want to dynamically add
<input type="text" name="...." value=""/>
but I'm running into problems with what do I put in the name spot to make it happen. Doing the #Html.EditorFor, and #Html.HiddenFor seem to work in that they modelbind to the correct parent, but they do not allow me to add additional input to the list.
My intuition direction - do something with reflection (or not) to get the name of the object of the master, use this to create a proper name?
Ideas or Code samples please. Thanks.
Using Html.EditorFor should give you the format to use. They need to be sequential and you'll need to either count the existing items (-1) to get your new [x] index to use in the name or parse the last elements name using a regex or string parse and extract its index, assuming you are allowing gaps in the sequence, which means you'll also need a custom binder if you want gaps.
okay, solution may be to use the editorfor a different variable to get the general name of the parent variable, and use jquery/js to parse this name and then dynamically insert the input type=file with a name based on this.

XPath / Selenium can't locate an element using a partial id with contains / start-with

I have the following HTML generated with an AjaxFormLoop.
<div id="phones">
<div class="t-forminjector tapestry-forminjector" id="rowInjector_13b87fdd8b6">
<input id="number_13b87fdd8b6" name="number_13b87fdd8b7" type="text"/>
<a id="removerowlink_13b87fdd8b6" href="#" name="removerowlink_13b87fdd8b6">remove</a>
</div>
<div class="t-forminjector tapestry-forminjector" id="rowInjector_13b87fdda70" style="background-image: none; background-color: rgb(255, 255, 251);">
<input id="number_13b87fdda70" name="number_13b87fdda70" type="text" />
<a id="removerowlink_13b87fdda70" href="#" name="removerowlink_13b87fdda70">remove</a>
</div>
</div>
I'm trying to access the second input field in child 2 using a partial ID, however I have not been successful in getting this to work.
What I've tried thus far.
String path = "//input[contains(#id,'number_')][2]";
String path = "(//input[contains(#id,'number_')])[2]";
I can't even access input 1 using 1 instead of 2, however if I remove [2] and only use
String path = "//input[contains(#id,'number_')]";
I'm able to access the first field without issue.
If I use the exact id, I'm able to access either field without issue.
I do need to use the id if possible as there is many more fields in each t-forminjector row that are not present in this example.
Implementation with Selenium.
final String path = "(//input[starts-with(#id,'quantity_')])[2]";
new Wait() {
#Override
public boolean until() {
return isElementPresent(path);
}
}.wait("Element should be present", TIMEOUT);
Resolved
I'm noticing I can't seem to use the following starts-with / contains to locate any element within to dom, however if I use a complete id, it works.
//Partial ID - fails
//*[starts-with(#id,"quantity_")]
//Exact ID - works
//*[starts-with(#id,"quantity_-112409575185705")]
The generated output you pasted here simply does not contain the string number_ anywhere in it. It does contain Number_ -- note the capital N -- but it's not the first part of the string. Perhaps you meant something like this (which at least selects something):
(//input[contains(#id, 'Number_')])[2]
Or:
(//input[starts-with(#id,'catalogNumber_')])[2]
As Iwburk stated, this was a namespace issue. According to the Selenium API,
http://release.seleniumhq.org/selenium-remote-control/0.9.0/doc/java/com/thoughtworks/selenium/Selenium.html
while using an xpath expression, I needed to used xpath=xpathExpression changing my query string to:
String path = "xpath=(//input[starts-with(#id,'quantity_')])[2]";
I found a related post here,
Element is found in XPath Checker but not in Selenium
you can't access it because you are not locating the element as to be unique in the page.
use an xpath that makes it unique ,
- you're xpath look ok .
more info here
http://www.seleniumhq.org/docs/appendix_locating_techniques.jsp
Besides the selenium syntax problem there's an xpath issue related to markup structure.
xpath 1: //input[starts-with(#id,'number_')][1]
xpath 2: (//input[starts-with(#id,'number_')])[1]
In the sample below xpath 1 will return 2 nodes (incorrect) and xpath 2 will be correct because input nodes are not siblings so surrounding parenthesis are needed to refer to the resulting nodeset
<div id="phones">
<div>
<input id="number_1" name="number_1" type="text"/>
</div>
<div>
<input id="number_2" name="number_2" type="text" />
</div>
</div>
Result without parenthesis
/ > xpath //input[starts-with(#id,'number_')][1]
Object is a Node Set :
Set contains 2 nodes:
1 ELEMENT input
ATTRIBUTE id
TEXT
content=number_1
ATTRIBUTE name
TEXT
content=number_1
ATTRIBUTE type
TEXT
content=text
2 ELEMENT input
ATTRIBUTE id
TEXT
content=number_2
ATTRIBUTE name
TEXT
content=number_2
ATTRIBUTE type
TEXT
content=text
In this next sample, parenthesis will not make a difference because nodes are siblings
<div id="other">
<input id="pre_1" type="text"/>
<input id="pre_2" type="text" />
<div>a</div>
</div>
With parenthesis
/ > xpath (//input[starts-with(#id,'pre_')])[1]
Object is a Node Set :
Set contains 1 nodes:
1 ELEMENT input
ATTRIBUTE id
TEXT
content=pre_1
ATTRIBUTE type
TEXT
content=text
Without parenthesis
/ > xpath //input[starts-with(#id,'pre_')][1]
Object is a Node Set :
Set contains 1 nodes:
1 ELEMENT input
ATTRIBUTE id
TEXT
content=pre_1
ATTRIBUTE type
TEXT
content=text
Testing was done with xmllint shell
xmllint --html --shell test.html

what does this xpath expression mean?

//input[#type="hidden" and #name="val" and position() = 1]/#value
does this mean get the text typed inside the input box ?
Read from right to left, it means "Get the value attribute of all of the input tags whose type attribute is 'hidden', whose name attribute is 'val', and which appears as the first element in its enclosing (form) tag".
I think it means grab the value attribute of an input whose type attribute is 'hidden' in addition its name attribute is 'val' and its position amongst its siblings is 1 ( first I believe, not sure if 0 is the start in xpath ).
<input type="hidden" name="val" value="test">
<input type="hidden" name="foo">

Resources