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!
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 am creating integration tests for my rails application.
The application I am working to is a little slow. In my test, I execute a certain action within the website (a "saving" - which reloads in the end the page) and the following capybara action runs before the page is actually reloaded.
I cannot use "sleep (seconds)" as this freezes the "reloading" itself.
So I wanted to give a try to this github idea: https://gist.github.com/metaskills/1172519
but I get the following error:
NameError: uninitialized constant Capybara::TimeoutError
Can someone tell me why I get this error and what does it mean?
As you posted, you're trying to make a method which waits for the ajax requests to finish.
But there's a better way to do this:
You have a view, which loads a modal (remote, with ajax). You should not do something like the wait_until method. Or even though not with using while true.
The best way of doing this, is to set an unique html element on the modals content:
<!-- in your modal view/partial -->
<span id="modal"></span>
... modal code
When you then use Capybara like this:
find("#modal")
The find method automatically waits for all ajax requests to finish.
See https://www.varvet.com/blog/why-wait_until-was-removed-from-capybara/ for more inputs.
The reason you're getting the error is because the Capybara::TimeoutError class was removed in Capybara v2, along with the #wait_until method. As the answer by #RaVeN states you should just be telling Capybara to expect some content or elements on the page which will make Capybara wait for it to appear automatically (as long as you're using a JS capable driver)
expect(page).to have_content("Some content that appears after the page has loaded") # will wait up to Capybara.default_max_wait_time seconds for the content to appear
or if the path of the page changes you could do
expect(page).to have_current_path('<the new path you want to wait to load>')
As an aside - there is no reason sleep in tests should pause a page loading since the tests, app, browser each run in separate threads/processes assuming you're running a JS capable driver. If you're not running a JS capable driver and are instead using the default rack_test driver then waiting/sleeping for anything is pointless because every action occurs synchronously.
I have hidden input inside of label:
<label for="upload">
<input class="hidden" type="file" name="file[picture]">
</label>
When I click on the label, I attach a file and then confirm.
After that modal window pops up and I need to find appropriate div class.
How can I test this with the help of capybara?
Update: Capybara 2.12 added a make_visible option to attach_file so if using 2.12+ you can first try
attach_file('file[picture]', 'path/to/file.png', make_visible: true)
before directly using execute_script yourself
File inputs are a special case since they are so often hidden for styling reasons and use a system modal for interaction. Capybara makes it hard to fill in hidden fields on a page because users generally can't interact with them, so for file inputs normal practice is to use execute_script to make them visible and then fill them in.
execute_script("$('input[name=\"file[picture]\"]').removeClass('hidden')") # assumes you have jQuery available - if not change to valid JS for your environment
attach_file('file[picture]', 'path/to/file.png') # takes id, name or label text of field not a random selector
Using Capybara '2.7.1':
attach_file('file[picture]', 'path/to/file.png', visible: false)
You can do something along the lines of:
find('label[for=upload]').click
attach_file('input[name="file[picture]"]'), 'path/to/file.png')
within '.modal-popup' do
expect(page).to have_content '.divclass'
end
the html like this
<input type='file' id='ok' class='lalalalala'>
my code is
attach_file("ok","./fileset/publisher/upload_pic.jpg")
but I got fail:
Failures:
Capybara::ElementNotFound:
Unable to find file field "ok"
so what is file field?
how can I upload file?
From the Capybara docs:
The file field can be found via its name, id or label text.
And you clearly have this already, which is a bit puzzling. Is there a modal window blocking this and/or is the page.driver switched to something else by chance? I ask because it's actually not completely necessary to interact with a form button and cause a modal or file/Explorer/Finder window to appear, and sometimes this can confuse the page.driver (ex: switches focus to the window instead of the page).
Try removing any click_button actions just prior to this step, and then try this:
attach_file('ok', File.absolute_path('./fileset/publisher/upload_pic.jpg'))
This has worked for me previously, in a super deeply parented div which ended up with a button class.
I've been reading about adding Honey pot fields to my forms for combating bots/spam. Only problem is theirs no guides or anything on where to start. Many sites say to make a field that is hidden that only the spam bot would fill out. But as I'm new to this, don't know where I would start in my application. Could anyone give me the advice on how to set this up? I am trying to make my Devise registration page use honey pot fields.
The basic idea behind honeypot captchas is that you have a hidden (via CSS) field named something like "form" or "email" or "content" that (to a bot just reading the field name) looks like it should be filled in. Then, when the server looks at the submission, you make sure these hidden fields are blank. If they aren't, then you flag the post as a bot.
Here's a well explained example (with some code in ASP), and here's a Rails Gem that provides honeypot captchas.
That Rails Gem I linked looks like it's very easy to use once installed:
<% form_tag comments_path, :honeypot => true do -%>
...
<% end -%>
Although if you're interested in learning about the approach rather than just having it implemented, I'd recommend you roll your own. If you're rolling your own, it's important to make sure that the field is hidden by CSS (or some other style/positioning trick) and not input type="hidden" - as otherwise the bot might not fill out the field.
As Michael Mior pointed out in the comments, it's important to have a message next to the hidden field telling the user to leave it blank - otherwise users with screen readers might erroneously fill it in. This feature is missing from the gem I linked to - so if you're making an accessible website (which you almost certainly should be) you may need to modify it or roll your own.
Keep in mind that this trick isn't foolproof - there's nothing stopping a bot from rendering the page and determining which fields are actually visible to the user before filling any in - but that kind of bot would be considerably more complex than one that just looked at the form html. A honeypot captcha is likely to be very effective at stopping simple bots.
Try invisible_captcha (supports Rails 3, 4 and 5).
It works pretty well for small and medium (in terms of traffic) sites, with a simple and flexible approach. It also provides time-sensitive submissions.
Basic usage
In your form:
<%= form_for(#topic) %>
<%= invisible_captcha %>
...
<% end %>
In your controller:
class TopicsController < ApplicationController
invisible_captcha only: [:create, :update]
...
end
HTML -
<input type="text" name="verifyEmail" id="verifyEmail">
PHP Validation -
if(strlen($_POST['verifyEmail']) > 0){
header('location: {some redirect URL here..}'); //Send them way away from your form :)
die(); //Stop execution of the script
}
CSS -
#verifyEmail{
position:fixed;
visibility: hidden;
top:-500px; left:-500px;
}
dislplay: none; does not show to a bot in HTML (try it with view source)
visibility: hidden; left:-500px; top:-500px; (displays when you view source)
I used display:none honey pots for a while, then switched to visibility option when it occurred to me that the field didn't show in the source code. Do it with a class or id in CSS, not inline style. Notify users with label is good idea, and name is not so important because most bots generally fill in all fields.
Definitely not a catch all but very effective if used with a basic math captcha.
I will share what works 100% for my site right now.
For almost a week we have been testing ways to prevent the high number of fake users called "Spam Bots" as well as "Brute Force Registrations" both are FAKE USERS.
You can find on the internet many ways to apply what is called a honeypot or a hidden field in the registration form.
The purpose of this trick is we fool the FAKE REGISTRATION as it will always fill data in the hidden field thus causing the registration process to DIE preventing the fake registrations.
Now we mentioned many variations of this trick can be found on the internet, and we will explain why our code is quoted as 100% working as for 2 days now it stopped all SPAM BOTS, and all Brute force registrations.
The secret is how we hide the field with a name like "field1" as bots will catch on if we use a common name like password or zip code etc. Using a name like field1 and autocomplete = off force the BOTS to fill in the field and prevents it from determining what the field is for, so it will keep filling it in with data killing the registration attempt.
This image below shows the code we used in the registration form.
<input type="text" name="field1" style="display:none !important" tabindex="-1" autocomplete="off">
This image below shows the code we placed in the PHP form that processes the command to kill the registration if data is entered into the field
if(!empty($_POST['field1'])) die();
For the past 48 hours this code has yielded ZERO SPAM BOTS and ZERO Brute Force Registrations. Enjoy from all of us at AFFA Social
If you wish to manually test this code simply remove the style="display:none from the registration form code above. Try to register putting data in the hidden field, and then registration dies, and if you remove the data from the field the registration will continue.
<div id="honeypotdiv">
If you see this, leave it blank. Only bots should see this
<input type="text" name="body" value="" />
</div>