Failed to confirm email via integration tests (rspec/email-spec/capybara/devise) - ruby-on-rails

I've been trying to simulate devise's confirmation through integration tests, but whenever I try to simulate clicking the confirmation link(i.e., from an email message), no confirmation was done, even though the email was successfully sent.
tests:(registration done earlier, and is successful)
open_email user[:email]
#Simulate confirmation
click_first_link_in_email#Only one link in the email message i.e. confirmation link
sleep 0.2
expect(new_user.confirmed?).to eq true# FAILS
..Results in:
Failure/Error: expect(new_user.confirmed?).to eq true
expected: true
got: false
(compared using ==)
Even though the email was sent and the confirmation process is actually working outside the tests, why's the confirmation process not being performed in that snippet?

I think the reason is the time delay at click taking effect.
To fix, do not use sleep at first.
Then, test it in UI way, aka, do not expect user to have something. Instead, expect UI to have something.
click_first_link_in_email
expect(page).to have_content(/confirmed/)
If you really need to test user, try reload, but still should after UI thing - to have the wait effect.
click_first_link_in_email
expect(page).to have_content(/confirmed/)
user.reload
expect(user).to be_confirmed

Related

Rspec, Capybara, selenium_chrome_headless. Wait for response after submit remote form

In use
Rails 6, Rspec rspec-rails (3.9.0), Capybara capybara (3.31.0), selenium_chrome_headless
I try to submit ajax form with remote: true. How can I wait for response?
Now works sleep 0.2 but I really don't like approach like this.
I found another way:
Timeout.timeout(Capybara.default_wait_time) do
loop do
active = page.evaluate_script('jQuery.active')
break if active == 0
end
end
but it doesn't work.
My recodr should dissapear after request:
expect(page).to_not have_content('User name')
Any suggestion? Thanks for advance
You should set an expectation for whatever visibly changes in the page when the request is done. If that means the text "User name" disappears from the page when the request has finished then your expectation of
expect(page).not_to have_content('User name')
or
expect(page).to have_no_content('User name')
will wait until that content (same as have_text) isn't visible on the page anymore (up to Capybara.default_wait_time seconds), which would imply the request has completed. If that text is on the page multiple times and only being removed from one location then you could use a count option in have_content or scope your not have content expectation to the specific section of the page where the text is being removed from.

Capybara test case for Login page authentication

I have capybara test case below.
it "Testing login page with valid data" do
fill_in 'email', with: 'kiran#gmail.com'
expect(page).to have_selector("input[value='kiran#gmail.com']")#Checking values are inserted in email field
fill_in 'password', with: 'Kiran.6565'
expect(page).to have_selector("input[value='Kiran.6565']")#Checking values are inserted in password field
click_button('submit')
expect(current_path).to eql(patient_detail_path(4))
end
I am checking Login page once the email and password fields are matches it should redirect to patient_details_path with id field value. In above code i specified email and password is working fine for manual login, but problem is in test case. Expected result: it should redirect to another page(patient_details_path) but it redirecting to home page(/) again.
Failures:
1) Login Page Interface Test login page with valid data
Failure/Error: expect(current_path).to eql(patient_detail_path(4))
expected: "/patient_details/4"
got: "/"
(compared using eql?)
# ./spec/views/login_spec.rb:41:in `block (2 levels) in <top (required)>'
Finished in 1.08 seconds (files took 2.13 seconds to load)
14 examples, 1 failure
I tried different solution's from stackoverflow but nothing work for me. Below are the different solution's tried.
#expect(current_path).to eql(patient_detail_path(4))
#expect(page).to have_current_path(patient_detail_path(4))
If email and password mismatch it will throw an error and redirect to login page again. In my scenario it was throwing an error even if email and password are valid . If i add below code in my test case it will work pass the test case.
#expect(page).to have_content "Invalid username/password combination"
Any one please help me i am new to ruby on rails and capybara.
I'm guessing the test you're trying to write should be written something like
before :each do
#user = # Create the required user with whatever method you're using
#patient = # Create the required patient with whatever method you're using
end
it "Logs in with valid data" do
visit(patient_detail_path(#patient)) # gets redirected to the login path
fill_in 'email', with: 'kiran#gmail.com'
fill_in 'password', with: 'Kiran.6565'
click_button('submit')
expect(page).to have_current_path(patient_detail_path(#patient))
end
That's a general guess and might not be 100% correct (tough to guess exactly what you're trying to do with half the test missing - the before block - from your question) but the general parts should be there. Since yours isn't logging in I'm guessing you're not actually creating a valid user with the given email and password, or you don't have a patient created with an id of 4 (you really shouldn't be relying on testing specific id numbers in feature tests though).
Additionally, you should always use the have_current_path matcher when checking for a given path/url since it will prevent test flakiness and since it's not a view test it shouldn't be in spec/views/login_spec.rb, more appropriate would be spec/features/login_spec.rb.
It seems to me that the driver is capturing the URL before Rails has a chance to update the URL to reflect the new page.
Asserting on URL is hard after performing a navigation change as a race condition might appear. I would suggest:
Assert on some other piece of info that could verify the user successfully logged in.
Assert using the wait option.
expect(page).to have_current_path(patient_detail_path(#patient), wait: 3)

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

Why Capybara feature tests don't wait?

I have a small trouble with my tests suite: when I run spec which the checking ajax action on the page, sometimes I get random error
Failure/Error: expect(page).to have_content 'DHH'
This error shows very rarely (about 1/100), but this very confused me. I decided this a 'race condition' cause, and I add this config in my spec/rails_helper.rb
Capybara.default_max_wait_time = 10
but this is don't help for me, and I decided add timestamps
it 'adds new DHH', js: true do
find('#modal_new_dhh').click
fill_in('name', with: 'DHH')
p 'click button'
p Time.now.strftime('%H:%M:%S.%L')
click_button('Submit')
p 'checking content'
p Time.now.strftime('%H:%M:%S.%L')
expect(page).to have_content 'DHH'
p 'after checking content'
p Time.now.strftime('%H:%M:%S.%L')
end
and see that
"click button"
"17:34:43.083"
"before checking content"
"17:34:43.127"
"after checking content"
"17:34:43.213"
why Capybara don't wait after click button?
sorry for my bad English
The wait in your example occurs in the have_content matcher. Where you're outputting times from will never show a delay because click_button has nothing to wait for, it just clicks a button and moves on (because it has no idea what it would wait for, clicking a button could do anything), however the have_content matcher will wait up to Capybara.default_max_wait_time for the content to appear.
Note your find, 'fill_in' and click_button calls also wait for the relevant elements to appear BEFORE performing their actions
As you said this is a race condition. As to why it happens, I can't really say, the only reason I could think of would be that there is a difference between a user experience and an automated testing, because computers are very fast.
I had faces the same challenge sometime ago and I found this tutorial which showed me a way to go about resolving these kind of issues. I hope it would be useful to you too.

How to access the same DB data that Capybara uses

I have a very simple password reset form, which is just a text field to enter an email and a submit button.
There are some client-side validations using JS, so I use the Capyabara JS driver when writing spec tests for it.
This test just tests that a password reset token is added to the user's auth_info table.
describe "password reset form", js: true do
let(:email) { "foo#example.com" }
# Create existing user with an email so we can reset it's password
let!(:user) { create(:user, email: email) }
before(:each) do
fill_in email_field, with: email
click_button reset_button
end
it "generates a new token" do
# `token` is definitely getting set properly when I pause it here
# with binding.pry and inspect the object using `user.reload`
# But when running the test it always shows up as `nil`
expect(user.reload.auth_info.token).to match(/[A-Fa-f0-9]{32}/)
end
end
As the comment notes, I know for a fact the token is getting properly set when I inspect it directly using binding.pry. But RSpec and Capybara are seeing it as nil, even after refreshing the model using reload.
Is Capybara maintaining a different cache or something?
Thanks!
EDIT: Also tried different combinations of applying the reload to the User model as well as the AuthInfo model, in case I needed to refresh the latter too
You're using a JS capable browser which means click_button is asynchronous. The result of this is you're executing click_button and then immediately checking for the token before the action triggered by the button has occurred. You can verify this by putting sleep 5 before the expect and the test should pass. The correct way to make the test wait before the check is to use capybaras matchers to look for info on the page that changes once the click_button has completed, something like either of the following
expect(page).to have_text('text that appears after click_button has succeeded')
expect(page).to have_selector('div.abcde') #element that appears after click_button has succeeded
Those will make the test wait until the action has completed and then you can check for the token

Resources