Rspec with capybara facing issue with page.should have_content - ruby-on-rails

I am new to Rspec and here I am trying to test one of the integration test. When I (Using Capybara) clicks button then page content gets replaced with post response. Now i am checking for page content, it present on the page but still my test is failing.
Below is the spec
it "get force check takeover1" do
visit('/las_xml')
select('Showroom', :from => 'urlVal')
fill_in('fpHeaderForce', :with => 'PSD.L.Syn')
fill_in('currentDate', :with => '2013-09-11')
click_button('submit')
page.should have_content('Buy 2013 Labor Law Posters')
end
But Result is,
1) las box get force check takeover1
Failure/Error: page.should have_content('Test page')
expected #has_content?("Buy 2013 Labor Law Posters") to return true, got false
# ./integration/las_box_spec.rb:19:in `block (2 levels) in <top (required)>'
and resulted response html contains,
<div class="las-link" id="">
<div class="laborLink_actual labor_01">
<span class="laborLink"></span>
Buy 2013 Labor Law Posters
</div>
</div>

Possible causes
Provided you're indeed testing a html page as your result excerpt seems to show, there may be several possibilities, here :
the text you're trying to test is not the actual one. Double check there's no typo and you use the same locale if multiple language website
the element containing the text may be hidden. Capybara won't consider the content to exist if it is not visible
the previous click on button may simply not work or may lead to an other page
Ways to isolate problem
As often with capybara since fails are just symptoms of real problem, we can deal with all problems using debugging features :
page.save_screenshot( filename ) : saves a screenshot to given file name (for example, 'shot.png')
save_and_open_page : open an inspectable html page in your browser
binding.pry : from pry gem, always useful to inspect context
For an example of screenshot creation through poltergeist, see this.
Edit : dynamic page problems
As mentioned in comments, problem seems to be tied to javascript content editing.
You should still use debugging mentioned before to be sure to isolate your problem. Once done, you can use capybara's #synchronize method to wait for your content :
page.document.synchronize( 10 ) do
result = page.evaluate_script %($(':contains("Post")').length)
raise Capybara::ElementNotFound unless result > 0
end
The synchronize method will be retried for 10 seconds, as long that the containing block raises a Capybara::ElementNotFound exception (any other exception is not catched). So basically, here, we check (using jQuery) if the "Post" content is present in page, and retry until it is or until ten seconds have passed.
If element was not found, test will fail by raising an uncaught exception.
If element is found, you can now proceed with your regular test. This allow to add timing condition in specs.

Related

When clicking update button with Capybara gem, my instance is not being updated [Rails / Rspec/ Testing]

To make it short but clear, I'm working on a Rails app and I'm now testing my controllers.
I use FactoryBot to generate instances to work with, database cleaner (truncation) to clean up my database after each test, and Capybara to test my controllers, emulating the user actions.
Factory bot is working properly and I have my instances being generated correctly and I can interact with / test them.
Buyt here's my problem:
Whenever I test the "Update" method of my controller, I expect the value of the selected attribute of my instance to be updated when clicking my "Save button". But when I use the p tag to check the value (Before and after clicking my "Save" button) nothing changed !
I've been spending the afternoon on this issue and wish someone could help me out to understand what I'm doing wrong...
Here's my code:
require 'rails_helper'
RSpec.describe "UserDetails", type: :controller do
render_views
describe "PATCH /update", focus: true do
before do
#user = create(:user, :amin)
#user_detail = create(:user_detail, :user_detail_user_2)
login_as(#user)
end
it "should be possible to update the user details" do
visit "/user_details/#{#user_detail.id}/edit"
expect(response).to have_http_status(:ok)
expect(page).to have_text("Edit your personal information")
expect(page.current_url).to match("/user_details/#{#user_detail.id}/edit")
p #user.user_detail.mobile_number # nil
p #user_detail.mobile_number # nil
expect(#user.user_detail.mobile_number.nil?).to be true
fill_in "user_detail[mobile_number]", with: "07 123 123"
expect(page).to have_button("Save")
click_link_or_button('Save')
expect(#user.user_detail.mobile_number).to eq("07 123 123") **#Failure/Error here**
end
end
end
Here's the Failure / Error I get:
Failures:
1) UserDetails PATCH /update should be possible to update the user details
Failure/Error: expect(#user.user_detail.mobile_number).to eq("071111111")
expected: "07 123 123"
got: nil
(compared using ==)
# ./spec/requests/user_details_controller_spec.rb:56:in `block (3 levels) in <top (required)>'
My user and user_detail instances both are valid with an id. There's no validation on the mobile_phone attribute, but I still want this value to get updated when clicking my "Save" button.
By the way, I tried running the test with js: true to checkCapybara cycle. Everything is happening properly. I even see the view being rendered with the new phone number being displayed. But when i checked the value in the _spec file, I still get nil as a value.
How can I fix that? Does anyone has the explanation of why it's not working?
Thank you very much in advance!
I just found the solution... I post it here rather than deleting the post as I'm sure It'll help other people in the future (Including myself).
We need to use "your_instance.reload" between the moment you click the update / create button via Capybara, and the moment we want to test if the value of an attribute changed or not.
click_link_or_button('Save')
#user_detail.reload
expect(#user.user_detail.mobile_number).to eq("071111111")
Now the result in the console:
UserDetails
PATCH /update
nil
nil
"071111111"
"071111111"
should be possible to update the user details
Finished in 5.01 seconds (files took 8.98 seconds to load)
1 example, 0 failures
Literaly one simple word. And no one ever talked about it... I feel ridiculous.
Have an amazing day guys !

Rails, Capybara - capybara cannot find added html elements sometimes

I have a function below
def fill_in_sources_details
click_button 'New Source'
assert_text 'Sources'
fill_in 'text', :with => #source.text
fill_in 'url', :with => #source.url
end
After New Source clicked, new fields added to the page. Sometimes the test fails because the test doesn't wait for new elements to be added so new fields cannot be found be Capybara. I tried to add assert_text 'Sources' which is obvious but it still fails sometimes. Any idea how to fix it?
A number of things could be going on here, some of which depend on which driver and driver version you're using.
You don't actually have a field that matches 'text' - That's not likely here since it works sometimes.
The initial click_button 'New Source' is missing the actual button. This could happen depending on which driver you're using if the button animates on to the page (slide in, zoom in, etc). You can tell if that's what's happening by putting a sleep 5 before the click_button. If that fixes the issue then you should look into disabling animation in test mode. It will speed up your tests and make them more reliable.
The click_button is happening but it takes too long for the fields to show on the page (slow ajax request, etc). Putting the sleep 5 after the click_button should have diagnosed that, however maybe it's taking longer than 5 seconds. The assert_text 'Sources' is a good attempt to diagnose that, as long as the text 'Sources' would only be on the page when the input is visible but that text seems too generic for that (it's probably on the current page already). You can always pass a wait option to fill_in, which will make it wait longer for the element to appear
fill_in 'text', :with => #source.text, wait: 20
You have an intermittent error in your app that's preventing the fields from showing. Diagnose that by catching the ElementNotFound, pausing the app and looking at the errors in your browser
begin
fill_in 'text', :with => #source.text, wait: 20
rescue Capybara::ElementNotFound
binding.pry # byebug, etc - whatever debugging you use
end

Rspec testing, Capybara unable to find link or button

This the part of my requests test that fails:
scenario 'Admin destroys a job posting + gets notified' do
parent = create(:parent)
create(:assignment, user_id: #user.id, role_id: 1)
demand = create(:demand, shift_id: 4)
sign_in(#user)
visit demands_path
click_on 'Destroy'
expect(page).to have_content('successfully')
end
This is the error:
Failure/Error: click_on 'Destroy'
Capybara::ElementNotFound:
Unable to find link or button "Destroy"
And here is the corresponding index view, including a "Destroy" link in the app:
Any idea why this test fails??
Odds are the data you assume is on the page actually isn't. This could be for a number of reasons.
Your page requires JS and you're not using a JS capable driver - see https://github.com/teamcapybara/capybara#drivers
Your sign_in method is defined to fill in user/pass and then click a button, but doesn't have an expectation for content that confirms the user has completed login at the end. This can lead to the following visit occurring before login has completed and therefore not actually logging in. Verify that by inspecting the result of page.html or calling page.save_and_open_screenshot before the click.
Your 'Destroy' "button" is neither an actual <a> element or <button> element. Fix that by either using semantic markup or swapping to find(...).click
You are using a JS capable driver but your records aren't actually visible to the app - this would affect all your tests though so I assume it's probably not this. If this was the case the login would fail and you'd probably need to install database_cleaner and configure for use with RSpec & Capybara - https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example

Multiple expects within scenario test

I have a scenario test:
scenario 'displays the form' do
# Build the setup for user coming from an external site.
expect(page).to have_selector('#braintree-dropin-frame', count: 1)
# User reloads the page
expect(page).to have_selector('#braintree-dropin-frame', count: 1)
# User visits page from within the website.
expect(page).to have_selector('#braintree-dropin-frame', count: 1)
end
First off, is this the proper usage of a scenario test? I'm essentially testing the same thing but in different scenarios. I feel like this should really be three separate scenario tests inside a context block. Am I misusing scenario?
If this isn't one user flow then they should be separate features/scenarios. You also need to be careful when setting expectations for the same selector multiple times in a test that you've checked it has gone away between (if you are checking it reappears again), otherwise you can end up with tests passing when they shouldn't due to timing and asynchronous behavior issues.
Additionally - since you're checking for a CSS selector you probably want to be using have_css rather than have_selector since it reads nicer and will mean the tests keep working if the default selector type is ever changed from :css

how to test a stale link click (or link I can't get to) using capybara/rspec

Artists cannot rate their own artworks
it "should display error if voting on own artwork", :js => true do
sign_in
visit "/upcoming"
click_link "like_post_1"
page.should have_content("Can't vote on your own artwork")
end
This was passing just fine.
However, I can't click on like_post_1 anymore because I added a feature to prevent voting links from appearing next to your own artworks.
Does this mean I no longer need test coverage for this scenario because it's extremely rare that someone can click on a voting link for their own artwork? Or should still have coverage to test the ajax response, because it's not tested anywhere else and it's possible for some stale page of links to somehow exist in a tabbed browser window. If so... how do I test it if I cannot call click_link?
I could try to create a POST request to create the vote, but capybara doesn't support posts, and I can't test the ajax response that way...
Or is there a way to simulate tabbed browsing in capybara?
Suggestions?
You can use CSS display:none or visibility:hidden for the own artwork instead of eliminating the link from the DOM. You might have to set
Capybara.ignore_hidden_elements = false
Another way is giving up Capybara and putting them into the controller/model spec. Controller/model spec might be a better place for the extremely rare case or safeguarding your app case.
Please try this:
Use sleep function.
sleep 10 use to wait process upto 10 seconds..
it "should display error if voting on own artwork", :js => true do
sign_in
visit "/upcoming"
click_link "like_post_1"
sleep 10
page.should have_content("Can't vote on your own artwork")
end

Resources