Has anyone ever tested best_in_place test helper methods with RSpec? - ruby-on-rails

I found this in one demo app:
it "should be able to use bip_text to update a text field" do
#user.save!
visit user_path(#user)
within("#email") do
page.should have_content("lucianapoli#gmail.com")
end
bip_text #user, :email, "new#email.com"
visit user_path(#user)
within("#email") do
page.should have_content("new#email.com")
end
end
https://github.com/dougc84/best_in_place/blob/master/spec/integration/js_spec.rb
Seems easy.
So I copied it to my Capybara spec:
before (:each) do
#report = FactoryGirl.create(:report)
visit report_path(#report)
end
it "name", :focus do
within("#name") do
page.should have_content #report.name
end
bip_text #report, :name, "new name"
visit report_path(#report)
within("#name") do
page.should have_content "new name"
end
end
It's so fast I can barely see anything, but it looks like it does do something with #name field. Then page reloads and it's still old value.
Any help?
Oh btw it does work in the browser. Just can't get tests pass.

I've added sleep 1 before and after bip_ helper, and it worked.

The problem here is the Javascript run by bip_text is asynchronous, but in your next line you immediately go to a different page, cutting off that Javascript from ever finishing. This is why your sleep 1 fixes it. You could also fix it by making Capybara wait for some new content before visit report_path, but then you'd need to write something like a success message to the page (e.g. with the ajax:success JS callback).

Related

Capybara::ElementNotFound: Unable to find visible link

I'm having trouble with my test for a method which lets an admin user promote other users to admin by the click of "Promote to Admin". It lies in my controller, I'm writing a feature test for it. I'm using Rails 5.1.4.
def promote
#user = User.find(params[:user_id])
if #user.toggle!(:admin)
flash[:success] = "User is promoted to admin."
redirect_to root_path
else
flash[:notice] = "Can't promote."
redirect_to root_path
end
end
This is the test:
describe "Promotion" do
before do
login_as(User.create!(name: "lala", email: Faker::Internet.email,
password: "lalala", admin: true))
visit users_path
end
context "to admin" do
it "promotes user to admin" do
click_link("Promote to Admin", :match => :first)
expect(current_path).to eq user_promote_path
end
end
end
It gives me the error: Capybara::ElementNotFound:
Unable to find visible link "Promote to Admin"
which I think is because I'm not accessing the right page, trying to log in as admin is perhaps not working.
Any suggestion would be very appreciated!
The most likely reason for your test failing is that you don't appear to have created any other users beyond the one you're logging in as. If you haven't then there wouldn't be any users to show "Promote to Admin" links for. You can always save the page or just do puts page.html to make sure what you think is on the page actually is.
A second issue with your test is that you should never use the eq matcher with current_path. You should be using the Capybara provided path matcher of you want stable tests
expect(page).to have_current_path(user_promote_path)
If you want to be sure that you're on the right page, you can do some debugging with:
save_and_open_page (opens your browser)
save_and_open_screenshot (takes a screenshot and opens it)
If it's all good, maybe Capybara can't find the link : Is it a screen size/responsive issue ? If yes, you can configure the window size that Capybara uses (see this link)
If the test still does not pass, maybe the link is not visible by default ?
You can add to option visible: false in click_link to precise that.

Why do these tests fail when run simultaneously, yet each passes individually?

I am experiencing strange very test behavior, with logged in state being handled inconsistently.
The spec logs a user in, visits a (nested or un-nested) index page, and checks that the correct content is displayed. Records are fetched asynchronously, though I don't think this should have an impact.
When each spec is run individually, they each pass. When all specs are run together, they fail because the expected content is missing. Using save_and_open_page reveals this is because the login page is being rendered, rather than the expected index page.
Why does rspec think the user is not signed in when all specs are run together, yet each spec passes individually?
The tests look something like this
let(:user) {create :user}
let(:team) {create :team}
let(:country) {create :country}
before :each do
login_as( user, scope: :user )
end
describe 'unnested' do
it 'should have the expected content', :js do
visit users_path
is_expected.to have_content "some content on the page"
end
end
describe 'nested by team' do
it 'should have the expected content', :js do
visit team_users_path(team)
is_expected.to have_content "some content on the page"
end
end
describe 'nested by nationality' do
it 'should have the expected content', :js do
visit country_users_path(country)
is_expected.to have_content "some content on the page"
end
end
The specs all require javascript (I don't know whether that is important here).
Authentication is handled by Devise, and my rails_helper.rb includes
config.append_after(:each) do
DatabaseCleaner.clean
Warden.test_reset!
end
Why does rspec think the user is not signed in when all specs are run together, yet each spec passes individually?
It took a long time to get to the bottom of this. Posting this hear in case it is of help to anyone else encountering the same issue.
After much searching I eventually found this small mention that login_as may not work with Poltergeist when js is enabled on your test scenarios.
I tried the suggested fix to deal with shared DB connections. Unfortunately this resulted in the following errors:
PG::DuplicatePstatement at /session/users/signin
ERROR: prepared statement "a1" already exists
I tried using the Transactional Capybara gem, but this did not seem to work well with Poltergeist.
Eventually I abandonned login_as completely, and instead wrote a short method that visits the login page, fills in email and password, and logs in that way.
This solution appears to be working. It adds a little overhead, so I'm only using it for tests with JS.
If you are using Capybara gem then there is no need to use :js with test cases
What I did if this helps-
scenario "visit with user signed in" do
user = FactoryGirl.create(:user)
login_as(user, :scope => :user)
visit "/"
expect(current_path).to eq('/')
expect(page).to have_title "Some Random Title"
end
The other way you can login user using feature specs like-
feature 'User signs in' do
before :each do
#user = FactoryGirl.create(:user)
end
scenario "Signing in with correct credentials" do
visit "/"
fill_in "Email", with: #user.email
fill_in "Password", with: #user.password
click_button "Log In"
expect(current_path).to eq("/login/useremail/verification")
expect(page).to have_content "Signed in successfully"
end
end
If your pages are ajax then refer to this https://robots.thoughtbot.com/automatically-wait-for-ajax-with-capybara

Unable to find css best_in_place with test helpers

I am trying to get best in place's
test helpers
working in my app. I am using
Capybara with Test::Unit
I have written a test like this.
class CompaniesTest < ActionDispatch::IntegrationTest
include BestInPlace::TestHelpers
def setup
#company = companies(:public)
end
test "should have best in place fields on show" do
get company_path(#company)
assert_response :success
assert_match #company.name, response.body
bip_text( #company, :name, "new name" )
end
end
I am getting an error like this
Capybara::ElementNotFound: Unable to find css "#best_in_place_company_1001664029_name"
I tried adding a sleep(0.5) before bip_text to see if it is a timing issue this did not change the error.
EDIT:
<span data-bip-type="input"
data-bip-attribute="name"
data-bip-object="company"
data-bip-original-content="Lightbulbs Corp."
data-bip-skip-blur="false"
data-bip-url="/companies/1001664029"
data-bip-value="Lightbulbs Corp."
class="best_in_place"
id="best_in_place_company_1001664029_name">Lightbulbs Corp.</span>
Here is what the css element for the page in the test env looks like. It looks like the id is correct. Any help would be appreciated.
I just reread your question and noticed you're using get. That's not a Capybara method and you can't mix the two different APIs and expect things to work. To implement what you're doing using Capybara it would be something like
test "should have best in place fields on show" do
visit company_path(#company)
assert_text #company.name
bip_text( #company, :name, "new name" )
end

Determining Form Submission with RSpec/Capybara

I have a project in which I am using RSpec and Capybara to do unit testing. I have fully flushed out model and controller tests which are passing nicely and handle the heavy lifting for pre-database validation.
I am now testing user experience and front end items and want to know how i could verify that a form did NOT submit. If a user mismatches passwords or some other erred data, I have script to set the error and prevent submission.
I know i could search for error text but where there a way to check that the 'submit' never happened and feel confident that no server trip was made.
I want something like:
it "should not sumbit if user name is less than 3 characters" do
visit /edit_account_settings(#user)
fill_in "username", :with => "fo"
click_button "SAVE"
# HOW DO I DO THIS?
expect( ... ).not_to submit_to_server
end
This is not something that you should be testing in an integration test. In integration tests you are taking the point of view of the end user, and therefore you should only be testing what the user can actually see. If the only evidence the user sees that the form has not been submitted is an error message, then that is what you should test for.
In integration tests we most test what the user would see like if an empty field is given then there must be error message according to validations. But still if you want you check as follow
describe "User Registration" do
before do
visit /edit_account_settings(#user)
fill_in "username", :with => "fo"
click_button "SAVE"
end
it "should not sumbit if user name is less than 3 characters" do
page.should have_content "your error message"
end
it "should create the user and redirect to blah_path" do
current_path.should eq blah_path
end
it "should add user in users table" do
expect { user.create }.to change(User, :count).from(0).to(1)
end
end

Trying to test sending of email in rSpec but ActionMailer::Base.deliveries.empty? is true

I have no idea why it's not working. After hours of trying to figure this out I wrote a small test to check if the ActionMailer::Base.deliveries was empty and it was.
When I test my reset form it works and mail is sent but when I run test it doesn't seem to store anything in the array.
require 'spec_helper'
describe "Passwords" do
describe "reset password" do
it "emails user when requesting password reset" do
visit login_path
click_link "Forgot Password?"
response.should render_template "passwords/new"
response.should have_selector :h1, :content => "Reset Password"
fill_in "password_reset[email]", :with => "foobar#gmail.com"
click_button "Reset Password"
response.should render_template "users/new"
response.should have_selector :div, :id => "flash_notice", :content => "Email sent with password reset instructions."
ActionMailer::Base.deliveries.empty?.should be_true
# mail = ActionMailer::Base.deliveries.last
end
end
end
Just found out a great gem to test emails with rspec: https://github.com/bmabey/email-spec
Hope it will help.
I always seem to do silly things like this:
Because I created the passwords_spec file manually and not using the rspec generator I forgot to add "require 'spec_helper'" at the top of the file. Something that the generator would have done automatically.
The last few days I have been figuring out my silly mistakes. Funny how all this happened when I got lazy and decided to build my app first then test after. Well I've learnt from my mistake. Test first!

Resources