xpath can't find anything in rspec test - ruby-on-rails

UPDATE 2 - adding before(:each) block
before(:each) do
...
post_via_redirect(
sub_domain(
path: login_view_path(
#p
),
subdomain: #account.subdomain
),
first_name: "Capybara",
last_name: "RSpec"
)
expect(response).to render_template(:show)
end
UPDATE
response.body has the data I'm looking for
but page.html only has a doctype.
ORIGINAL
I'm trying to do some integration testing. I have this:
require "spec_helper"
feature "Tracking without Javascript" do
...
scenario "when navigating to a content" do
...
# this passes so I know there is a body
expect(response.body).to include(c.name)
find(:xpath, "/html") # see if xpath works...
end
end
and I get the error:
Failure/Error: find(:xpath, "/html")
Capybara::ElementNotFound:
Unable to find xpath "/html"
any ideas?

post_via_redirect doesn't involve Capybara - that directly (more-or-less) interacts with Rails, but not through a browser. That would be why Capybara never points an actual page.
Original (misguided) answer for posterity:
If you are using the newest versions of everything, then Capybara will not find invisible elements by default. Could you try find(:xpath, "/html", visible: false) and see if that works? The other thing I can think of is that your XPath may not be matching as you expect. Try //html instead.
On the meta level, you'll almost never see this kind of assertion, as the end-user doesn't care if you have an html element or not. You will get more useful tests by checking for things that the user can see. I also prefer to use CSS selectors, as other things (JavaScript and CSS) tend to care about them as well. Our whole test suite has maybe one or two XPath selectors for very ugly cases.

Related

Capybara issue inside a within block

require "spec_helper"
require "rails_helper"
include Capybara::RSpecMatchers
include Capybara::DSL
Capybara.javascript_driver = :webkit
feature "Course", :type => :feature do
scenario "Get index and search for course types", js: true do
visit "/courses"
within("//body") do
find(:xpath, "//input[#id='course_type_id_1']").click
find(:xpath, "//div[#class='course-right-sec']")
expect(page).to have_content('65,171 courses')
expect(page).to have_content('Fundamentals of Design')
end
end
end
The problem i am facing with the above code is that when i find a specific div inside a within block, whether the id of the div is correct or not it passes the test.
Whoa, this is a lot of expectations for one test. While some testing purists would say one expectation per test, feature tests that need to load js are time consuming, but you might be doing too much here. One logical way would be to have one feature test for each of your courses; that way you have a couple expectations for each.
This isn't the exact solution to your problem, but it will help debugging in the future.
All of your expectations are based on the have_content matcher. Basically, if it's in the DOM on load, all of these expectations are going to pass, regardless of your find and click events. Ie. find(id).click
I might be able to help more if you give more context to whats going on here and what you are trying to test for and against. Attaching your js would help also.

Error when using RSpec's `all` matcher with Capybara's `have_css` matcher

I'm just getting started with feature specs using RSpec (and Capybara). I'm testing my ActiveAdmin dashboard and I want to check that all panels have an orders table as shown in this snippet:
feature 'admin dashboard', type: :feature do
def panels
page.all('.column .panel')
end
describe 'all panels' do
it 'have an orders table' do
expect(panels).to all(have_css('table.orders tbody'))
end
end
end
I've used the all matcher a lot in my unit tests but it doesn't appear to work when wrapping Capybara's have_css matcher because I'm getting the following error:
Failure/Error: expect(panels).to all(have_css('table.orders tbody'))
TypeError:
no implicit conversion of Capybara::RackTest::CSSHandlers into String
Am I correct in my assumption that RSpec's built-in all matcher should work with other matchers as well?
Note: I'm using describe and it instead of feature and scenario in this instance because I'm testing output rather than user interaction scenarios (see my other question).
Unfortunately there is a conflict between RSpec's all and Capybara's all see Capybara Issue 1396. The all that you are calling is actually Capybara's all.
Solution 1 - Call BuiltIn::All Directly
The quickest solution would be to call RSpec's all method directly (or at least that code that it executes.
The expectation will work if you use RSpec::Matchers::BuiltIn::All.new instead of all:
expect(panels).to RSpec::Matchers::BuiltIn::All.new(have_css('table.orders tbody'))
Solution 2 - Redefine all
Calling the BuiltIn:All directly does not read nicely so might get annoying if used often. An alternative would be to re-define the all method to be RSpec's all method. To do this, add the module and configuration:
module FixAll
def all(expected)
RSpec::Matchers::BuiltIn::All.new(expected)
end
end
RSpec.configure do |c|
c.include FixAll
end
With the change, the all in the following line will behave like RSpec's all method.
expect(panels).to all(have_css('table.orders tbody'))
Note that if you want to use Capybara's all method, you would now always need to call it using the session (ie page):
# This will work because "page.all" is used
expect(page.all('table').length).to eq(2)
# This will throw an exception since "all" is used
expect(all('table').length).to eq(2)
I used a very similar approach to the accepted answer, but in a Cucumber environment I was getting errors about RSpec.configure not existing. Also, I wanted to call the matcher something besides all so that I could use them both without conflicts. This is what I ended up with
# features/support/rspec_each.rb
module RSpecEach
def each(expected)
RSpec::Matchers::BuiltIn::All.new(expected)
end
end
World(RSpecEach) # extends the Cucumber World environment
Now I can do things like:
expect(page.all('#employees_by_dept td.counts')).to each(have_text('1'))

Ruby on Rails - RSpec Javascript Test with Capybara (new to RoR)

New to Ruby, Rails and TDD. I'm using RSpec with Capybara and Capybara webkit.
Trying to test if a div element exists on a page.
Test Code:
require 'spec_helper'
describe "Login module" do
before do
visit root_path
end
it "should have a module container with id mLogin" do
page.should have_css('div#mLogin')
end
it "should have a module container with id mLogin", :js => true do
page.evaluate_script('$("div#mLogin").attr("id")').should eq "mLogin"
end
end
The first test passes but the second test fails with:
Login module should have a module container with id mLogin
Failure/Error: page.evaluate_script('$("div#mLogin").attr("id")').should eq "mLogin"
expected: "mLogin"
got: nil
Ran the JS in browser dev tools and get "mLogin" rather than nil.
Any ideas? Thanks.
find('div#mLogin')[:id].should eq 'mLogin'
See this from doc:
#evaluate_script
Evaluate the given JavaScript and return the result. Be careful when using this with scripts that return complex objects, such as jQuery statements. execute_script might be a better alternative.
evaluate_script always return nil, as far as I remember.
Anyway, your second test seems like is testing if capybara works, because your first test is enough.
One likely problem is that the have_css matcher supports Capybara's synchronization feature. If the selector isn't found right away, it will wait and retry until it is found or a timeout elapses.
There's more documentation about this at http://rubydoc.info/github/jnicklas/capybara#Asynchronous_JavaScript__Ajax_and_friends_
On the other hand, evaluate_script runs immediately. Since this is the first thing you do after visiting the page, there's a race condition: it's possible that it executes this script before the page has finished loading.
You can fix this by trying to find an element on the page that won't appear until the page is loaded before you call evaluate_script.
Alternately, you can wrap your call in a call to synchronize to explicitly retry, but this is not generally recommended. For situations like this, you're much better off using Capybara's built-in matchers. The evaluate_script method should only be used as a last resort when there is no built-in way to accomplish what you need to do, and you need to take a lot of care to avoid race conditions.

Capybara with RSpec returning empty body for page object

When working with Capybara and Rspec in my features spec, after calling "visit", page.body returns:
"<html><head></head><body></body></html>"
This, of course, causes all my "find"s to fail, as there is nothing there. save_and_open_page care of launchy shows me the complete, accurate page, chock full of HTML tags.
Any thoughts on why Capybara is not setting the page element correctly?
turns out this was due to a conflict between webrat and capybara. Diving into the source for where "visit" and "page" are referenced, I discovered that visit is declared in both Webrat and Capybara; however, the effect of "visit" in each differs. Capybara sets the page variable, while webrat sets a response variable. I don't yet know enough about how to use both of them, as they seem to both be useful for different purposes - if anyone wants to leave some comments with some resources I certainly would appreciate it!
I was getting this too.
When I puts out the markup from the visit call, I found that the page was actually throwing a 404, but I wasn't getting a Capybara 404 error.
If you run something like the following, it will print out the markup so you can debug more easily:
When /^I view the front page$/ do
#visit = get "#{host}/frontpage"
puts #visit
end
Hope that helps someone.

Capybara click_button works, but how do I evaluate the JSON response?

Soo, if Capybara can interface with the DOM and my app makes a form submit via AJAX and returns some JSON, can Capybara see that at all? I'm not finding any way to get to the AJAX response in the Capybara API.
Here's an example of kinda what I'm doing:
# Cucumber step_definition
Then(/^I should be able to create a household$/) do
click_link 'Next'
page.should have_selector 'form#household-form'
fill_in 'Name', with: name = Faker::Name.last_name
click_button 'Create'
page.wait_until do
page.evaluate_script('$.active') == 0 # really awesome hack to wait for ajax
end
#user.reload.households.first.name.should eq name
end
I'd rather not hit the database again to test the result, but instead do something like:
xhr.response_data.should be_json
Verifying that my json response is what I thought it should be.
I'm just experimenting here, trying to get used to Cucumber and Capybara.
You should mark your scenario / feature to run as a JavaScript feature by tagging it with #javascript:
#javascript
Scenario: Title goes here
What this will do is to tell Cucumber to run the selenium-webdriver gem which will launch a real browser (Firefox) and run the test inside that browser. The browser will of course evaluate the JavaScript and then Capybara will be able to see what it outputs.

Resources