How to test jQuery TokenInput field using Selenium - ruby-on-rails

I'm unable to test a Tokeninput field in a form using selenium. The situation is when we type something, it gives a list to options to select but those options aren't part of the DOM. The text fills the field but doesn't select the item.
The code which I have written is:
Given admin user is on schedule interview page
And he select "obie[1]" and "DHH[1]" from the candidate name(s) auto sugget field
**step defination**
Given /^he select "([^"]*)" and "([^"]*)" from the candidate name\(s\) auto sugget field$/ do |arg1, arg2|
within(:css, "#interview_template_candidate_names_input") do
fill_in('tmp',:with => arg1) --tmp is name of the token input field
find("li:contains('obie[1])'").click
save_and_open_page
end
end

I finally succeeded in making this work. Here's the gist: https://gist.github.com/1229684
The list is part of the dom (div.token-input-dropdown), it's added as the last child of the body element, which is probably why you didn't see it.
If you understand what the tokeninput plugin is doing, you can get a better idea of what you need to do. For each tokeninput you create, the plugin:
creates a ul.token-input-list (immediately before input#your_input_id)
creates a ul.token-input-list input#token-input-your_input_id
hides the input#your_input_id
creates a div.token-input-dropdown
So the most challenging part is finding the correct ul.token-input-list, because you have to find it based on its position relative to the original input, and the selenium webdriver doesn't let you navigate the dom.
After that, you just fill in the input#token-input-your_input_id and "click" on the div.token-input-dropdown li option that matches what you're looking for.

Related

Capybara not seeing updates to the DOM made by Javascript

I'm running Rails 5.x, with, Cucumber, Siteprism and Capybara through chromedriver. Most things work except..
I have a tiny bit of javascript that changes the class on an element in response to an event. But Capybara never sees the change. It only ever sees the class the element has when the page initially loaded.
Using Chrome, and debugging my Cucumber steps, I can see the element has the new class, but Capybara doesn't see it.
This must be an issue other people have encountered and solved, though I can't find the right subject title.
example coffeescript
$(document).on('focus', 'tbody#item-entry > tr > td > input', (e) ->
$(#).closest('tr').addClass('focused-row')
$(#).closest('td').addClass('focused-cell')
)
example html after the focus event has been triggered
<tr class="focused-row">
<td>ignore this </td>
</tr>
The purpose is to change the background colour of the row containing an input element that has focus. It works.
But Capybara, can't see the class, but it can see any classes added when the page is loaded. e.g.
expect(siteprism_stuff.root_element['class']).to match(/focused-row/)
Ignore the SitePrism stuff, that just gets the right element. root_element is the Capybara class for the dom node.
Now I know it's getting the right Capybara element because if I change my view to put stuff in the class for each row, then it sees that perfectly OK. What it can't see is the any new class added via Coffeescript. Although it's visible in the Chrome inspector, and changes the background color of the focused row as required.
You're specifying an "ends with" CSS attribute selector ($=)
input[class$='form-control']
which since the class attribute for the element you're interested in
<input type="search" class="form-control form-control-sm" placeholder="" aria-controls="universitiesTable">
doesn't end with 'form-control' is correctly not matching. You probably just want to use a normal CSS class selector input.form-control if continuing to do it the way you are. Any of the following options should find the search field and fill in the data you are trying to fill in.
fill_in 'Search:', with: string
fill_in type: 'search', with: string
find(:field, type: 'search').set(string)
find('input.form-control').set(string)
Note: Your question is still unclear as to whether you are seeing the class added in the inspector in test mode, and whether the line color is changing while the tests are running (or whether you're only seeing that in dev mode) - This answer assumes the JS is actually running in test mode and you're seeing the line color change while the tests are running.
You don't show how you're actually triggering the focus event but I'll assume you're clicking the element. The thing to understand when working with Capybara is that the browser works asynchronously, so when something like click has been done, the actions triggered by that click have not necessarily been done yet. Because of that, whenever doing any type of expectation with page elements you should always be using the matchers provided by Capybara rather than the basic matchers provided by RSpec. The Capybara provided matchers include waiting/retrying behavior to handle the asynchronous nature of dealing with the browser. In this case, assuming siteprism_stuff.root_element is the row element then you could be doing something like
expect(siteprism_stuff.root_element).to match_css('.focused-row')
or depending on exactly how your siteprism page objects are setup you could pass the class option to the siteprism existence checker
# `page_section` and `have_row` would need to be replaced with whatever is correct for your site prism page object
expect(page_section).to have_row(class: ['.focused-row'])

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 an option in Rails/Capybara/Selenium

The 'select' method (part of Capybara DSL) doesn't work under ':js => true', but it works otherwise. I am trying to test adding/removing items between two select boxes in Rspec, so need to trigger javascript associated with the clicking of 'Add' and 'Remove' buttons.
It's not stated anywhere in Capybara's document that its DSL will not work with ':js => true' (which enables a javascript driver - in my case the default Selenium). Another way to tackle the issue might be to do the clicking through Selenium, but I could not find any document, especially in the Rails/Rspec/Capybara context.
Can somebody share some examples/links? A complete syntax reference of Capybara/Selenium would be great. Many thanks!

Using Capybara w/ Selenium, how would I click a table row?

Would simply like to know if there's an alternative to click_link / click_button that can be used with any element as some of my clickable elements are not standard html clickables such as tr elements, but they still contain href attributes.
Javascript enabled.
Use Javascript then:
page.execute_script("$('whatever_you_want').click()");
I had the same situation with a html month view, I had to choose a day of month. I kept it as simple as I could and this is only one way of doing this.
# Choose July 22 (at this point in time)
assert page.has_css? '#calendar'
within '#calendar' do
find('td', :text => '22').click
end
Are you using cucumber? Not sure if it's any use to you, but here's a cucumber step definition for clicking anything with selenium:
Then /^I click "(.+)"$/ do |locator|
selenium.click locator
end
I have tried the javascript solution in past, it works in the sense that the button do gets clicked, but cucumber fails to recognise it as a completed step.

Resources