Is it possible to click the Nth element with SitePrism? - capybara

I was just starting with SitePrism and I've got a site where elements get added dynamically to the page and don't have any kind of id, name, etc... the easiest I could think of is finding them by their 'text', eg. a CONTINUE button
I was trying:
element :continue_b, :button, 'Continue'
and SitePrism fails with the following:
Capybara::Ambiguous: Ambiguous match, found 4 elements matching button "Continue"
Is there a way for me to specify with SitePrism the element I want to click? I've found a few ways to do it with Capybara itself but I didn't manage to see the equivalent with SitePrism.

If there really is no difference between any of the button elements and you can't/don't want to move the element definition to a scoped section of the page, you can use the match argument to return just the first. Since all the parameters after the SitePrism element name are passed through to Capybara as find arguments it would be
element :continue_b, :button, 'Continue', match: :first

Related

Capybara Select2 Help w/Firefox

For some reason im running into problems with Select2 and Firefox w/Geckodriver.
Select2 fields I used to be able to just say page.select 'Text', from: 'Label' however that no longer works I just get a Element <option> could not be scrolled into view (Despite being scrolled into view). Right now im doing something similar to this:
select2Fields = page.all('.select2-selection')
select2Fields[0].click
page.find('.select2-search__field').set('Text To Set')
within('.select2-results') do
page.find('li', text: 'Text To Click').click
end
It's ugly and doesn't fit with my Page Object Model method, since I have to sorta know which select2 field it is. It doesn't seem to be when finding it with a label.
Any ideas? It's very frustrating since it worked with Chrome but the latest chromedriver has issues with the newest capybara versions.
Not sure what you were using that you were able to ever use select with a select2 widget, it never should have worked, and the fact it did would have been a bug. The reason is the actual <select> element (which is what Capybaras select method works with) is non-visible on the page, and select2 replaces it with a JS driven widget. You need to do exactly what a user would do, which is click to make the widget show up then click on the <li> element which represents the correct entry. This can all be moved into a helper method and potentially some custom selectors which boils down to something like this
Capybara.add_selector(:select2) do
xpath do |locator, **options|
xpath = XPath.descendant(:select)
xpath = locate_field(xpath, locator, options)
xpath = xpath.next_sibling(:span)[XPath.attr(:class).contains_word('select2')][XPath.attr(:class).contains_word('select2-container')]
xpath
end
end
Capybara.add_selector(:select2_option) do
xpath do |locator|
# Use anywhere to escape from the current scope since select2 appends
# the choices to the end of the document
xpath = XPath.anywhere(:ul)[XPath.attr(:class).contains_word('select2-results__options')][XPath.attr(:id)]
xpath = xpath.descendant(:li)[XPath.attr(:role) == 'treeitem']
xpath = xpath[XPath.string.n.is(locator.to_s)] unless locator.nil?
xpath
end
end
def select_from_select2(value, from: nil, **options)
select2 = if from
find(:select2, from, options.merge(visible: false))
else
select = find(:option, value, options).ancestor(:css, 'select', visible: false)
select.find(:xpath, XPath.next_sibling(:span)[XPath.attr(:class).contains_word('select2')][XPath.attr(:class).contains_word('select2-container')])
end
select2.click
find(:select2_option, value).click
end
That should let you call select_from_select2 just like you would call select and it will find the select2 widget associated with the given <select> element (hidden by select2) and choose the correct entry from it.
I had tested the Thomas´ answer but it doesn´t work for me. When Capybara click in the desired option, the select2 box close itself and set the 0 option. Finnaly, I made a walkaround as I check the option I want as selected and trigger the change.select2 event. I know that I dont really test the select2 box.
def self.select2 (page, datos)
page.execute_script("$('##{datos[:from]}').select2('open')")
if page.find(".select2-results li", text: datos[:texto]).click
page.execute_script("$('##{datos[:from]} option[value=\"#{datos[:valor]}\"]').prop('selected', true)")
page.execute_script("$('##{datos[:from]}').trigger('change.select2')")
end
page.find(:css, '#' + datos[:from]).value
end
As I keep my module Helper without include it in tests, I needed to include self in the name of the method and take 'page' from the capybara´test as parameter.
The variable 'datos' is a hash with the selector, the text of the option and its value.
As select2 box close when capybara click on it, I wrap the walkaround inside the if clause to be sure that some parts of the select2 box were working.
Finally, I returned the current value of the select to test it (really, it doesnt needed as I set the option with that value as 'selected')
I hope it would help anyone.

Ruby, Cucumber, & Capybara: selecting using regex

I am trying to select an input node using Capybara:
When /^the shopper clicks the "(.+)" button$/ do |button|
first(:css, text: /^#{button}/).click
end
The scenario that triggers the above step is:
And the shopper clicks the "PAY" button
The button in the view will always start with the text 'PAY' but it is followed by a space and then the total price of the products e.g. 'PAY $150'
The dollar amount will change but the PAY part won't. If I try the above code I get the following error:
And the shopper clicks the "PAY" button # features/step_definitions/shared_steps.rb:33
unexpected '$' after '' (Nokogiri::CSS::SyntaxError)
How can I properly grab the PAY button using Capybara and Regex?
The problem you're having is that you have specified a css selector type but not actually specified a CSS selector. Since it's an <input> element you could do
find(:css, 'input', text: /^#{button}/).click
However, since it's an input being used as a button I'm assuming it's of type submit, reset, image, or button. This means you can use the :button selector type (rather than :css) to make your clearer as to what you're actually doing. When finding a button Capybaras default behavior is to match on substrings so you should just be able to do
click_button(button)
which is the same as either of
find(:button, button).click
find_button(button).click
If that doesn't work for you please add the relevant HTML to your question.
As an aside, you should stay away from first whenever possible - it has a number of downsides.

How to select 3rd 'a' tag in a list for RSpec test?

How do I select a link in a list to click on for an rspec acceptance test. The code for the view is (in html):
%ul.exports
%li.model
.name
Model 1
.control
link_to 'Export', export_model_path(:model1)
I've tried
(page.find("a")[:href] = "/admin/export/users").click
and the response is 'Capybara Ambiguous match'. I've also tried variations on
page.find(".exports li:nth-child(3) control a").click
and the response is 'unable to find css'. All the items in the list are identical except for the href value.
You've only got a single a tag in your Haml example, so I'm guessing your example is incomplete. If you want to retrieve a link by it's href value you could try this:
find(:xpath, "//a[#href='/admin/export/users']").click

click on second ambiguous link with Capybara

I want to click on the second of the two following links:
photo
photo
As you can see, the URLs are actually different, but the link title, "photo", is the same in both. I am not using IDs on my links, and there is no nesting class context by which to distinguish the links.
Ideally, I would like to be able to click even a third, or nth ambiguous link.
An example of a DSL for this purpose would be something like:
click_link photo.title, match: 2
I find that using all is easier to read than locating by xpath:
all('a', :text => 'photo')[1].click
However, it not as fast as using xpath.
You should be able to use xpath for this. Something like:
find(:xpath, '//a[text()='photo'][2]').click

In rspec/capybara, I need a version of "within" that checks all descendants, not just children

I have a snippet of code designed to count the number of list items in a dropdown menu:
within ('#campaign_duration_in_days_input') do
page.all('li').count.should eql(4)
end
That returns 0, although there are four list items as descendants of that div (not direct children). How can I get a count of all descendants?
I can't see anything wrong with your code. within works with indirect descendants as well as direct children. It would also throw an error if the argument #campaign_duration_in_days_input so it looks like that element exists.
So looks like it is just not finding any li elements. If this is a plain old HTML select dropdown should you be looking for option elements? If not then try save_and_open_page immediately before the within to inspect the DOM to see what is happening.

Resources