How to check if controller response has link? - ruby

What's a decent way of doing it? Preferably matching a regexp.
my current code is like this:
describe "get #show" do
context "signed in" do
it "should have a link to edit profile" do
# i sign in the user
get :show
response # i don't know what to do here
end
end
end
Thanks for the help!

It seems like you're trying to do an acceptance test, which probably means you should defer to Capybara. Then you can do magic like
page.should have_css('edit_link', text: 'Edit me')

Related

Missing a value during form checking test using rspec and capybara

I'm dealing with a bug from a spec file, specifically in a test which is related to an action of update a field value, this is the code I'm trying to use:
it "is possible to update your city destination page" do
click_link another_city.name
expect(page).to have_content(country.name)
expect(page).to have_content(country.state)
expect(page).to have_content(city.name)
end
After run the test I get the message:
Failure/Error: expect(page).to have_content(city.name) expected to find text "Melbourne" in "..."
So, I'm trying to fix it with this new line of code:
expect(page).to have_field(another_dealer.name, with: 'Voonte')
But I got a different kind of error related to another field.
So, my questions are:
is it a good approach trying to add a specific value in the spec?
what else should I try to fix the error?
Thanks a lot for your comments.

Verify a sequence of messages are sent to different objects/classes

I have an object that saves a model and runs a background job.
Class UseCase
...
def self.perform
#account.save
BackgroundJob.perform_later(#account.id)
end
end
In my spec I'd like to test separately that both messages are sent.
I started with something like
it 'saves the account' do
expect_any_instance_of(Account).to receive(:save)
UseCase.perform(account)
end
And this worked fine when I was just saving the account in the perform.
But when I have added the background job the spec doesn't pass anymore since now Couldn't find Account without an ID.
How can I verify (in RSped 3.5) separately that both messages are sent?
UPDATE
it 'runs the job' do
expect(BackgroundJob).to receive(:perform_later).with(instance_of(Fixnum))
UseCase.perform(account)
end
passes so I suppose the account is correctly saved.
However, when I try to inspect #account
def self.perform
#account.save
byebug
BackgroundJob.perform_later(#account.id)
end
In 'saves the account', I get
(byebug) #account
#<Account id: nil, full_name: "john doe" ...>
In 'runs the job', I get
(byebug) #account
#<Account id: 1, full_name: "john doe" ...>
The expectation makes #account a test double so in the first spec the job cannot get the id.
Thanks
The error Couldn't find Account without an ID is actually pretty helpful considering the code that you have inside your perform method.
The issue is mentioned in the comments but I'll elaborate a bit further.
You are using #account.save (I'm assuming #account is an ActiveRecord object) which by definition will return true/false when run (see documentation)
What you probably want is to use save! instead since it will raise a ActiveRecord::RecordInvalid error and stop execution rather than triggering the error you noted earlier. (toss a binding.pry into the method and note what #account is when attempting to call .id)
When you change to save! you can add a test for a case where save might fail (missing attribute, etc). Might look something like this
it 'should raise error when trying to save invalid record' do
# do something to invalidate #account
#account.username = nil
expect { UseCase.perform(#account) }.to raise_error(ActiveRecord::RecordInvalid)
#confirm that no messages were sent
end
Hope this helps you out! GL and let me know if you have any questions / need more help with rspec

Capybara::ElementNotFound - how do I see the source it checked against?

I'm using Capybara(v2.0.1) with RSpec(v2.11.0), and I have a spec very similar to the example on the README:
require 'capybara/rspec'
require 'capybara/dsl'
# other setup…
describe "Sign in" do
include Capybara::DSL
before do
within("#session") do
fill_in 'Login', :with => 'user#example.com'
fill_in 'Password', :with => 'password'
end
click_link 'Sign in!'
end
subject { page }
it { should have_content "logged_in? true" }
end
Unfortunately, this is the failure message I see:
Failure/Error: within("#session") do
Capybara::ElementNotFound:
Unable to find css "#session"
but it doesn't show me the output of the source it's used, and there's a redirect involved after that link is clicked so I'd like to be sure what's it's checking against.
It also seems that Capybara is suppressing warnings (which I find annoying, quite frankly) as I've tried warn page.inspect in several places and nothing in the output.
Would anyone be able to tell me how I can do this? Any help or insight given is much appreciated.
I should add I'm not using Rails or Cucumber.
I just add this line at the start of my test:
puts "#{page.html.inspect}"
I have to look through it all on the command line, but it's quick and dirty.
capybara's save_and_open_page writes the html to your app's tmp directory and opens it in your default browser.
it's mentioned in the debugging section of the section of capybara's readme these days.
I use irb as a live debugger with Selenium so I can follow along as it goes. Pretty useful for debugging weirdness that goes on with Capybara/Selenium.
You can view the entire source by using page.html. From there it's pretty easy to inspect by checking for the first match of #session.

Dynamic page URL

I have a page with URL that is dynamic. Let's call it view post page. URL for post 1 is site.com/post/1 and for post 2 is site.com/post/2.
This is what I do at the moment to check if I am at the right page.
The page:
class ViewPostPage
include PageObject
def self.url
"site.com/post/"
end
end
Cucumber step:
on(ViewPostPage) do |page|
#browser.url.should == "#{page.class.url}#{#id}"
end
Is there a better way? Do you even bother checking the entire URL, or just the site.com/post/ part?
I am using the latest page-object gem (0.6.6).
Update
Even bigger problem is going directly to the page that has dynamic URL.
The page:
class ViewPostPage
include PageObject
def self.url
"site.com/post/"
end
page_url url
end
Cucumber step:
visit ViewPostPage
What I do now is to change the Cucumber step to:
#browser.goto "#{ViewPostPage.url}#{#id}"
It would be great if there was a way for the page to know it's ID, but I have not figured out yet how to do it.
You can get the url for the page using the method current_url. On your test above are you trying to determine if you are on the correct page? If that is the case I might suggest using one of the two "expected" methods - expected_title and expected_element.
The page_url method is more than likely not the choice for you if you need to navigate to a url dynamically. What you might try instead is add a method to your page that does something like this:
class ViewPostPage
include PageObject
URL = "site.com/post/"
expected_title "The expected title"
def navigate_to_post_with_id(id)
navigate_to "#{URL}/#{id}"
end
end
And in your test
on_page(ViewPostPage) do |page|
page.navigate_to_post_with_id #id
page.should have_expected_title
end
Let me know if this helps.
There is an option to check dynamic URL in the Page Object gem.
Use the below code:
class ViewPostPage
include PageObject
expected_url "The expected URL"
def initialize
has_expected_url?
end
end
It'll help you
As far as I know, #page_url is for opening corresponding page along with page object initialization. To verify you're on correct page, you can try to use #expected_title method.
Also, maybe it'll be useful for you. When symbol is passed to #page_url, it calls corresponding method, so'd better use it. I haven't tried it, but here are few links for you.
Original issue on GitHub
Specs
Documentation of #page_url

How to handle cookies when testing with Webrat?

I'm writing Cucumber tests for a Sinatra based application using Webrat. For some tests I need to implement a scenario like
Given I am logged in as admin
When I am visiting "/"
Then I should see "Settings"
I define steps like this:
Given /^I am logged in as "(.+)"$/ do |user|
visit "/login"
fill_in "login", :with => user
fill_in "password", :with => "123456"
click_button "Login"
end
When /^I am viewing "(.+)"$/ do |url|
visit(url)
end
Then /^I should see "(.+)"$/ do |text|
response_body.should =~ /#{text}/
end
On success a cookie is created
response.set_cookie(cookie_name, coockie_value)
and then verified in views when user tries to access admin pages via helper method:
def logged_in?
request.cookies[cookie_name] == cookie_value
end
And it looks like Webrat doesn't store cookies. Tests don't report any error, but "logged_in?" in views is always false, like the cookie was not saved.
Am I doing something wrong? If this is just how Webrat works, what is the best workaround?
The real problem is the way Sinatra is treating sessions in the test environment. Search the Google group for the discussion, but the real solution is to simply use:
use Rack::Session::Cookie
and not
enable :sessions
Using Selenium is nice but it's overkill as a solution for the OP's problem.
The workaround is use Webrat with Selenium back end. It runs all tests in a separate Firefox window, so cookies or javascript is not a problem. The downside is extra time and resources required to run Firefox and do all the real clicks, rendering etc.
You could have your "Given /^I am logged in" step hack logged_in?:
Given /^I am logged in as "(.+)"$/ do |user|
visit "/login"
fill_in "login", :with => user
fill_in "password", :with => "123456"
click_button "Login"
ApplicationController.class_eval <<-EOE
def current_user
#current_user ||= User.find_by_name(#{EOE})
end
end
EOE
end
There are two downsides:
It's really hackish to mix view-level and controller-level issues like this.
It'll be difficult to mock up "logout"

Resources