I'm implementing a basic slider using the new HTML5 input type of Range.
No javascript or CSS involved.
Here's an example HTML5 input-type Range
It used to be a text box so in Capybara I could just say
fill_in "rating_scale", with: "57"
Now I need a way to tell it to fill_in the range or to click and move the range's thumb.
Has anyone done this successfully?
That is what I ended up doing...
find(:xpath, "//input[#id='rating_scale']").set 57
I assume that the slider is using Javascript so you will need a driver for Capybara to interact with Javascript, like Poltergeist.
here is a capybara helper to change an input range based on it's name,
assuming you use Poltergeist:
def range_select(name, value)
selector = %-input[type=range][name=\\"#{name}\\"]-
script = %-$("#{selector}").val(#{value})-
page.execute_script(script)
end
There is a monkey-patched drag_by implementation which could work very well for this if you are using a javascript rangeslider (although it doesn't set a specific value unfortunately, just drags by a specific amount).
Related
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'])
I have javascript feature specs that are working fine with selenium, but I'd like to switch to capybara-webkit for a number of reasons (CI, not popping up a browser window, etc).
I'm trying to switch over from selenium, but something is preventing my code from working correctly. I have the following helper:
def capybara_login(user)
visit login_path
fill_in "email", with: user.email
fill_in "password", with: user.password
click_button "Log in"
end
Here's my input field that is being outputted:
<input type="email" name="email" id="email" value="" class="form-control" placeholder="you#domain.com" autocomplete="off">
I've tested that user.email is not nil. For some reason when I use capybara-webkit, this fails and nothing is filled in (I'm checking by doing a save_and_open_page). However, this code works fine if I use regular capybara(non js) or selenium for js.
Am I missing something? Any ideas as to what could be preventing capybara webkit from filling in these fields?
EDIT: Thank you Tom for helping out. So save and open page will not show any input in those fields.
I was able to use save_and_open_screenshot to see that the page was actually just stuck on the loading image on the login screen. If I do a "sleep(1)" it logs in just fine.
Wondering what strategies people use to deal with this...waiting until the page loads to continue? Whats the reasoning behind not having Capybara do this by default? Or am I doing it wrong?
Capybara has no way of knowing when a page is "fully" loaded, since many pages will load asynchronously, dynamically as needed, etc. In fact Capybara is showing you an issue with your pages usability since a user could technically interact with elements before they're actually usable - in this case Capybara is probably quicker than any user could be, but still... Because of this you need to determine what the widgets you're using do on the page and what changes they make - For instance does a class get set on the body element when a library has finished processing the page, does an input field have a class/attribute added when it's been augmented, etc. Once you've determined that you can tell Capybara to do something like
expect(page).to have_selector('body.class_added_when_ready')
to make sure the page is fully interactable
This was totally a fluke with some javascript running on my end. At some point I thought it was a good idea to allow a 300ms delay for ladda loaders to fully animate before submitting a form. This was causing capybara to choke. I was able to isolate this problem to that specific code.
Thanks for your help, Tom!
I have a link with a specific attribute on my page. Using Rspec + Capybara how can I check for the existence of this link?
Foo
Does not work:
page.find(:css, 'a[data-content="This is a discrete bar chart."]')
Works:
page.find(:css, 'a#test')
I have the same query, but the quotes are different.
find("a[data-method='delete']").click
and works for me.
Here are some different ways:
expect(page).to have_selector("a[href='#'][data-content='This is a discrete bar chart.']")
page.has_selector?("a[href='#'][data-content='This is a discrete bar chart.']")
page.find("a[href='#'][data-content='This is a discrete bar chart.']") # returns the node found
If you don't have access to page but have access to rendered, then
expect(Capybara.string(rendered)).to have_selector("a[href='#'][data-content='This is a discrete bar chart.']")
It could be an issue of Capybara finding too many results. Maybe change 'find' to 'all' and see if you get an array of results that you could select from. Best of luck.
This worked for me when searching for a link's title attribute (tooltip):
Then(/^I should see tooltip "(.*?)" on link "(.*?)"$/) do |tip, link|
within('#results') do
link = find(:link, link)
expect(link['title']).to eql(tip)
end
end
NOTE: the within is optional and useful for reducing chances of ambiguity.
HTML Source code:
<a title="Owner: lfco Fire Department" href="/water_supplies/597df82566e76cfcae000018">Hydrant LFCO One</a>
I have a file field that has opacity: 0 and is overlaping a fake button. Its a common css technic to fake a sort of "Upload button" that displays consistently across different browsers.
Capybara doesn't allows me to call attach_file on that input. The error is Selenium::WebDriver::Error::ElementNotVisibleError: Element is not currently visible and so may not be interacted with.
Anybody knows any way to force capybara to interact with invisible elements?
The answer is still unanswered, but I've found a work around. Nothing intelligent, just make visible the element with a simple script
page.execute_script %Q{
$('#photos').css({opacity: 1, transform: 'none'});
}
I post it for the record.
You can interact with hidden elements using the visible: false property in Capybara.
If you want to click on hidden element use:
find("#photos", visible: false).click
Don't use click_button('#photo') directly
The author of Capybara recommends setting Capybara.ignore_hidden_elements immediately prior to needing to see the invisible element, and resetting it afterwards:
Capybara.ignore_hidden_elements = false
click_button 'my invisible button'
Capybara.ignore_hidden_elements = true
In general interacting with non-visible elements should not be possible when using Capybara (you can find them using the visible: false/hidden option in most finders but not actually do anything to them). However, the file input is a special case because of how common it is to hide the element and, due to security restrictions, no other way to actually add a file by interacting with the pages visible elements. Because of this attach_file has a make_visible option which can be used to have Capybara make the element visible, attach the file, and then reset the CSS to the original setting.
attach_file('photos', file_path, make_visible: true)
I ended up resolving it a different route.
execute_script() was giving me a hard time (it would freeze test execution on FireFox), so this is what I did:
I already had an appropriate javascript file. I appended the following
<% if ENV["RAILS_ENV"] == "test" %>
$('#photos').show()
<% end %>
I also had to append .erb to my javascript file for proper Rails asset handling.
And in my test file, I was already setting ENV["RAILS_ENV"] = "test"
This way I could just dumb down the UI for test, and yet maintain the look and feel for production.
Miquel, thanks for workaraund.
I have similar issue for interacting with hidden file input on C# binding for Selenium Webdriver 2.35 and Firefox 24. To make file selection working did similar trick:
((IJavaScriptExecutor)webdriver).ExecuteScript("$('#fileUploadInput').css({opacity: 1, transform: 'none'});");
IWebElement e = webdriver.FindElement(By.CssSelector("input#fileUploadInput")));
e.SendKeys("c:\\temp\\inputfile.txt");
I've done it this way with elements that has the CSS style display:none; set:
page.execute_script("$('.all-hidden-elements').show();");
all('.all-hidden-elements').first.click
If the hidden element is nested in a visible parent element (e.g. a hidden input inside a visible label), you can click on the parent instead. If you still want to find the input by ID, you can traverse to the parent like so:
find('#hidden_input').find(:xpath, '..').click
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.