I've got a page containing multiple elements of class .block. In Capybara, I want to be able to loop through and refer to each of the elements with this class before completing an action.
However, none of the code I've tried so far has worked. Here's what I've tried:
within('.block:nth-child(1)') do
find('.Button').click
end
page.find('.block').all.first.find('Button').click
page.find('.block').all[1].find('Button').click
Any ideas?
You want to use the all method (see http://rubydoc.info/github/jnicklas/capybara/Capybara/Node/Finders#all-instance_method).
An example of outputting the text of each element (ie iterating) with class 'block' would be:
page.all(:css, '.block').each do |el|
puts el.text
end
page.all returns an array of matching elements. So if you just want the second matching element, you can do:
page.all(:css, '.block')[1] #Note that it is 0-based index
Related
I am testing a website form using clj-webdriver. I want to know how to use (input-text) function if the form fields have same class.
From the (input-text) definition it gives "Type the string s into the first form element found with query q". Since every field has same class and when I give,
(input-text ".class")
It only fills the first field. Is there any way to differentiate all fields with the same class?
The fields of the form has only class and type as selectors.
Thank you
input-text only fills the first match.
Use quick-fill to fill them all.
E.g.,:
(quick-fill {".class" "s"})
/edit
You say "for 2 fields of same class I have to enter 2 and 3 as values. and also if the class is "object object-done" can I consider class as ".object". I am not exactly sure what you mean with the latter, but what I understand is that you want to add different values to different elements.
If you want to find specific elements you can use find-elements. These will return a collection of elements:
(find-elements {:class ".class"})
This will find all elements with the class ".class" in order which they appear on the page.
If the collection is stored in a variable text can be added to every element via input-text based on index. So for example if you want to add an increasing index to them you can use map-indexed to add the index of every value to the element as follows (doall is called to walk every element in the lazy sequence - function calls are only made when the elements are accessed and doall makes that happen):
(defn fill!
"Fills all elements with class class with increasing numbers."
[class]
(let [elements (find-elements {:class class})]
(doall
(map-indexed (fn [index element]
(input-text element (str index)))
elements))))
This function is called like (fill! ".class").
Hope this helps.
You should use the (find-elements [webelement by]) function, which returns a list of 'webelementsmatching a givenby`.
From the project documentation, that can be found at https://github.com/semperos/clj-webdriver/wiki/Introduction%3A-Taxi , an example is:
(defn css-finder
"Given a CSS query `q`, return a lazy seq of the elements found by calling `find-elements` with `by-css`. If `q` is an `Element`, it is returned unchanged."
[q]
(if (element? q)
q
(core/find-elements *driver* {:css q})))
So I have this situation where I need to delete something from an array conditionally, meaning that I want to go through the items in the array and do a test and delete the one that passes the test, and then I want to get that deleted item back. If I exclude the conditional aspect of this then Array#delete does what I want basically--returns the deleted item, but can't delete conditionally. On the other hand delete_if removes the item conditionally, but returns the remaining items back in an array.
For the purposes of this question assume the following class:
class Foo
def test?
#returns true or false
end
end
I can easily reproduce this behavior by doing:
index = my_array.index {|a| a.test?}
item_to_delete = my_array[index]
my_array.delete item_to_delete
But I was hoping to find something like:
deleted_item = my_array.delete_if_and_return_deleted {|a| a.test?}
I'm not thrilled about having to go through the array multiple times to get this done :/ first to find the target's index, second to get the target and third to remove the target. Ick.
Any thoughts or suggestions?
What you want is the partition method:
deleted_items, kept_items = my_array.partition {|a| a.test?}
I have an array that I collected:
array= #browser.ul(:class => 'parent').links
I am then trying to access the text on a specific element in the array iterating over the array like this:
array.each_with_index do |i, index|
If I do something like:
array[index]
I get back the anchor element object. And if I do:
array[index].element
I will get back an HTML element object. But if I try to get anything element specific such as:
array[index].text
array[index].value
then I get an "unable to locate element" error.
I am using Watir, Page Object, and Ruby.
Here is the scope of the entire array iteration, it's fairly simple:
array= #browser.ul(:class => 'parent').links
array.each_with_index do |i, index|
if index == array.length-1
sleep 1
#browser.button(:text => 'Complete').when_present.click
else
sleep 1
#browser.button(:text => 'Complete').when_present.click
#browser.a(:text => 'Next').when_present.click
end
end
I am trying to add an elsif that checks for the text of a link, so that if it's on that particular page/link it does something specific while on that page.
For example, in pseudo-code:
If array element text = "Instructions", then dont click the complete button, just click next.
I suppose I would be open to solving this in any way that lets me identify the link it is currently on, so that I can perform a set action, but I figured grabbing the text of the current link would be easiest, hence the question.
How can I access the text or specific attributes of an element in this array?
Most probably the DOMs of your elements are changing after some interaction (like clicking 'Complete' button).
My suggestion is to find all the elements once again after each interaction.
Try something like that:
array = #browser.ul(:class => 'parent').links
index = 0
array.length.times do
sleep 1
if index == array.length-1
#browser.button(:text => 'Complete').when_present.click
array = #browser.ul(:class => 'parent').links #We are finding new array after possible change
elsif array[index].text == 'Instructions'
#browser.a(:text => 'Next').when_present.click
else
#browser.button(:text => 'Complete').when_present.click
#browser.a(:text => 'Next').when_present.click
end
array = #browser.ul(:class => 'parent').links #And once again
index = index+1
end
Attention: I can not guarantee that the code above will work because I've got no page to test it on to be sure. If it is not working - try to modify it using the idea
Iterating over a list of links and just pulling out information on them is fairly simple and should work reliably as long as the DOM is not changing. Once you start clicking things, or taking actions that cause some or all of the page to update, all bets are off as your collection may contain references to elements in the UI that have been deleted or replaced.
You also seem a bit confused over how the .each iterator works. The basic form of that method returns you each object in the collection in turn. The with_index version returns each object plus its index within the array. In most cases, unless you care what the index is, you would not use the with_index form.
For example, to walk your collection and output the text of each link, you could simply do
links_list = #browser.ul(:class => 'parent').links
links_list.each do |link|
puts "the link text is: #{link.text}"
end
If you wanted to indicate the index of each link, then you could do
links_list = #browser.ul(:class => 'parent').links
links_list.each_with_index do |link, index|
puts "the text for link number #{index} is: #{link.text}"
end
If you want to work your way through a list of links like that and do things with them that might cause page updates, then you are better off to use a tactic like that presented in the other answer where you create a loop based off the size of the list, but re-fetch the list inside the loop so that it will always be 'fresh' and not potentially contain references to objects that are no longer in the DOM.
I have a JSP that populates an int[] and string[] via Spring controller. When a string value for an index is "", the JSP renders all of the values from the string[].
Controller populates string[] ids & values.
form.setIds(ids);
form.setValues(values);
The JSP loops through and populates a table.
<c:forEach items="${form.ids}" varStatus="status" var="id">
<form:input path="values" value="${form.values[status.index]}" />
When string[]:values contains: {"a","b","","d"}, elements 0,1,3 show as expected: 0="a", 1="b", 3="d". Element 2 shows: "a,b,,d" for the output to the JSP for that line.
I've tried several ways to render, such as wrapping output in JSTL taglib, and changing to array lists instead of primitive string[].
I have a feeling I'm overlooking something :)
I think your bug lies elsewhere. ${form.values[status.index]} will indeed print nothing if the content of your array is really is an empty string.
This one took a while.
The issue was that using path="values" on the form input, when we hit a empty element it would show all of "values" instead of the intended blank value. The fix was to use path="values[${status.index}]" for the form input.
Also, in order to use an element, I had to switch my underlying form from using String[] objects to List objects. This was due to the form not having an initialized String[] value upon loading, whereas I could init as ArrayList without having to specify a specific length to the string array.
Usually in protractor you can select singular element with:
element(protractor.By.css('#fdfdf'));
Occasionally you get something like this:
element(protractor.By.css('.dfdf'));
which potentially has more than one element. What's the correct way to select an index from a locator that locates multiple elements, and still contain the protractor's methods for sending Keys?
You can get an indexed element from an array returned with
// Get the 5th element matching the .dfdf css selector
element.all(by.css('.dfdf')).get(4).sendKeys('foo');
If you want to get the first element then
element.all(by.css('.dfdf')).first();
element.all(by.css('.dfdf')).get(0);
Try this one. It will work:
element.all(by.css('.dfdf')).get(4).getText();
I don't know why xpath is so much underestimated but you can solve thousands of problems with it, including this one
let elem = element(by.xpath('(//div//a)[3]'))
You can specify the number of element to use. Keep in mind the numbers start from 1, not 0 as usually in js