There are several elements on the page, and I need an array of them, but without an element with particular text. What I do now is
tabs_quantity = page.all(:css, 'div.class1 ul.tabs li.tab.exist', visible: true)
tabs_quantity.each { |x|
if x.text != 'Main'
...blah-blah-blah...
I`ve seen only ":text => 'Text' everywhere but what I need is opposite to equality, so I've searched and tested but have not find if there is something simple like
tabs_quantity = page.all(:css, 'div.class1 ul.tabs li.tab.exist', visible: true, :text !=> //or "not equal", whatever// 'Main')
Thanks.
I agree with Raza. Definitely try to set a class on the elements you're looking for.
If that turns out to be impossible for whatever reason, here are a couple more options.
1) You could try regular expressions:
# match text that doesn't contain 'Main'
page.all('li.tab.exist', text: /^(?:(?!Main).)*$/)
That's not super easy to read. But since you've scoped your class down quite a bit, it might not be too slow.
2) Another alternative is xpath:
# find li with class of 'exist' and any text except 'Main'
page.all(:xpath, "//li[contains(#class, 'exist') and not(contains(.,'Main'))]" )
That's also a bit unweildy, especially if you want to go as in depth as your original css matcher. I'd definitely include a comment along with it.
Further info: using a css class is definitely faster and easier. Always avoid text matches whenever possible.
Just add a class to the group of elements you want to select and the find with:
page.find('.class_name')
Related
I have 3 divs with class variant_container. How would I select the last one so that I could do
within(last_variant_div) do
...
end
?
Depending on your preferences, here are two solutions.
Using XPath
XPath has a last() function to find the last matching node. You can use this in the within locator:
within(:xpath, '(//div[#class="variant_container"])[last()]') do
# do stuff
end
Using all
The within method can also be passed a node to search within. This means that you can locate the last node by find or all, which can sometimes give more flexibility.
For example, in this case, we can now use a CSS-selector to find the last div (by combining it with Capybara's all method). Note that CSS-selectors by themselves do not have the ability to find the last element of a certain class.
last_div = all('div.variant_container').last
within(last_div) do
# do stuff
end
I'm fairly new to Interface Builder (I'm using RubyMotion, btw), and would like to see how others organize their view tags. I have a lot of view components that really should be grouped together, like a UILabel on top of a UIView.
My initial thoughts are to have a single function that takes care of all the mapping of view tags to view objects. Something like this:
def mapTags
#awesomeButton = AwesomeButton.new(
:view => view.viewWithTag 1,
:label => view.viewWithTag 2
)
end
def viewDidLoad
self.mapTags
end
This still isn't very satisfactory, which is why I'm hoping someone much more experienced/clever can offer a superior solution.
well if you need to tag all your views and you have alot of em, i suggest you do it whenever you initialize a view instead of separating it in a function, that is the safest way. In objective-c, i usually create specific macro's for each tag and then whenever i initialize the view, i assign the macro for that tag, ex:
#define kMyFirstViewTag 2
#define kMyLabelTag 3
...
[myFirstView setTag:kMyFirstViewTag];
This way you can group certain views by name (kMyGroup1LabelTag) and you can access them easily by tag name.
I dont know much about ruby, but im pretty sure there should be a way to achieve the same result.
Hope this answer is helpful.
I am trying to put the values of some xml elements into an array using rexml. Here is an example of what I am doing:
doc = Document.new("<data><title>This is one title</title><title>This is another title</title></data>")
XPath.each( doc, "*/title") { |element|
puts element.text
}
However, that outputs:
[<title> ... </>, <title> ... </>]
How can I get it to output an array containing "This is one title" and "This is another title"?
Moving my comment to an answer, per request:
While puts may convert its argument its argument to a string anyway, you can have the XPath return the text node in the first place:
XPath.each(doc, "*/title/text()") {...
Are you sure about that? Here's a complete program:
#!/usr/bin/ruby
require 'rexml/document'
include REXML
doc = Document.new("<data><title>This is one title</title><title>This is another title</title></data>")
XPath.each( doc, "*/title") { |element|
puts element.text
}
Output:
This is one title
This is another title
Edit: It sounds like the OP has moved on, but I think there should be some clarification added here for future visitors. I upvoted #LarsH's good answer, but it should be noted that, given the OP's specific input, element.text should produce exactly the same output as would result from selecting the text() nodes in the first place. From the docs:
text( path = nil )
A convenience method which returns the String value
of the first child text element, if one exists, and nil otherwise.
The sample input given in the original question shows <title> elements containing only one text node in each case. Therefore, these two methods are the same (in this case).
However, pay attention to this important note:
Note that an element may have multiple Text elements, perhaps
separated by other children. Be aware that this method only returns
the first Text node.
You can get all of an element's child text nodes using texts() (plural).
What I suspect a lot of people are really looking for is an equivalent of the DOM's textContent (or its illegitimate cousin innerText). Here's how you might do that in Ruby:
XPath.each(doc, "*/title") { |el|
puts XPath.match(el,'.//text()').join
}
This joins all of the text descendants of each element into a single string.
The short answer is that there's no short answer. Which one of these you want, if any, is highly context-specific. The only requirement in the original question is to "put the values of some xml elements into an array", which isn't really much of a specification.
In a view file I have:
= link_to 'View', post
= link_to 'View', comment
In a spec file (I'm using Capybara):
click_on 'View'
It clicks on the first link, but I want it to click on the second one. How can I do it?
You could try to find all entries and deal with an array:
page.all('a')[1].click
Would help to have a class or use within to scope your search ;)
There's probably a few ways but I usually scope something like this.
within(".comment") do
click_on("View")
end
There's quite possibly/probably alternatives as well. I usually do my acceptance testing from cucumber, so my steps typically look like
When I follow "View" within the comment element
Where I have a step that translates within the comment element to a scoped call to the step itself (which I think is built into the latest capybara web_steps)
The worst thing about "the second" link is that it can become the third or the first or even the twenty fifth someday. So, scoping with a within block is the best way. Example:
within(".comment") do
click_on("View")
end
But if it is difficult to specify the link with a within scope (which sometimes it really is), I guess the way to click the second link with a certain text is:
find(:xpath, "(//a[text()='View'])[2]").click
In later versions of capybara (2.0.2, for example) both click_on 'View' and click_link 'View' will raise an ambiguous match error:
Failure/Error: click_on 'View'
Capybara::Ambiguous:
Ambiguous match, found 2 elements matching link or button "View"
So this won't do even if you want to click the first link (or if any link would be ok, which is my case).
As far as I understand this is made to force people write more specific tests where particular links are clicked.
It definitely could be tricky to debug the code if you accidentally placed two or more links with identical text and try to see what is happening. It's good to rely on something that is unlikely to change and specifying a link with a within block is a nice way to do this.
There are many ways for solving this type of problems.
Do it like this
if(page.find("a")[:href] == "comment")
click_on("View")
or
page.find("a:eq(2)").click
Remember javascript indexing starts with 0 while In Capybara, indexing starts with 1. So use a:eq(2) here for second href.
For capybara 2 solution:
within(".comment") do
click_on("View")
end
would not help if you have a few .comment. So simple use: page.first(:link, "View").click
This works for me if you have several rows of identical classes and you want to find the second row. Like a previous author mentioned, capybara indexing starts at 1.
within all(".trip-row")[2] do
assert page.has_content?("content")
end
If you use capybara-ui you could define the widget, or reusable DOM reference, for each widget.
# define your widget. in this case,
# we're defining it in a role
widget :view_post, ['.post', text: 'View']
widget :view_comment, ['.comment', text: 'View']
# then click that widget in the test
role.click :view_post
role.click :view_comment
I'm working with many jQuery plugins, that often create DOM elements without id or other identification properties, and the only way to get them in Capybara (for clicking for example) - is to get their neighbor (another child of its ancestor) first. But I didn't find anywhere, does Capybara support such things for example:
find('#some_button').parent.fill_in "Name:", :with => name
?
I really found jamuraa's answer helpful, but going for full xpath gave me a nightmare of a string in my case, so I happily made use of the ability to concatenate find's in Capybara, allowing me to mix css and xpath selection. Your example would then look like this:
find('#some_button').find(:xpath,".//..").fill_in "Name:", :with => name
Capybara 2.0 update: find(:xpath,".//..") will most likely result in an Ambiguous match error. In that case, use first(:xpath,".//..") instead.
I found the following that does work:
find(:xpath, '..')
Capybara has been updated to support this.
https://github.com/jnicklas/capybara/pull/505
There isn't a way to do this with capybara and CSS. I've used XPath in the past to accomplish this goal though, which does have a way to get the parent element and is supported by Capybara:
find(:xpath, '//*[#id="some_button"]/..').fill_in "Name:", :with => name
If you stumbled across this trying to figure out how to find any parent (as in ancestor) node (as hinted at in #vrish88's comment on #Pascal Lindelauf's answer):
find('#some_button').find(:xpath, 'ancestor::div[#id="some_div_id"]')
This answer pertains to how to manipulate a sibling element which is what I believe the original question is alluding to
Your question hypothesis works with a minor tweak. If the dynamically generated field looks like this and does not have an id:
<div>
<input></input>
<button>Test</button>
</div>
Your query would then be:
find('button', text: 'Test').find(:xpath, "..").find('input').set('whatever')
If the dynamically generated input does come attached with an id element (be careful with these though as in angular, they are wont to change based on adding and deleting elements) it would be something like this:
find('button', text: 'Test').find(:xpath, "..").fill_in('#input_1', with: 'whatever')
Hope that helps.
I'm using a different approach by finding the parent element first using the text within this parent element:
find("<parent element>", text: "text within your #some_button").fill_in "Name:", with: name
Maybe this is useful in a similiar situation.
As mentioned in comment by #Tyler Rick Capybara in these days have methods[
ancestor(selector) and sibling(selector)
I needed to find an ancestor with a css class, though it was indeterminate if it the target ancestor had one or more css classes present, so I didn't see a way to make a deterministic xpath query. I worked this up instead:
def find_ancestor_with_class(field, cssClass)
ancestor = field
loop do
ancestor = ancestor.find(:xpath, '..')
break if ancestor.nil?
break if ancestor.has_css? cssClass
end
ancestor
end
Warning: use this sparingly, it could cost you a lot of time in tests so make sure the ancestor is just a few hops away.
Here it is
http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Base:parent
There is a parent method present;)