Capybara test case for Login page authentication - ruby-on-rails

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)

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 !

Cannot reach the password of the user after assigning it - RSpec

I am trying to write system tests. I want to run a classic login page test.
I have a field for email, password, and submit button.
It is working in production env and alive without any problem.
my test file is like this:
it "can login" do
user = User.create(email: 'mail#mail.com', password: 'password', role:1, name: 'test user')
user.save!
visit '/'
fill_in(:user_email, with: user.email)
fill_in(:user_password, with: 'password')
find(:button, 'Sign in').click
expect(page).to have_content('Signed in.')
end
Whenever I tried to create a user and try to use it in the system testing, it is not working. It is visiting the page, filling the places and clicking the button as it should but it cannot log in, giving error that email or password is not correct.
I believe there is a problem with password encryption or somehow I cannot match the passwords properly.
I have printed out the user after creation in the test case, I have a valid user but somehow I cannot reach its password. I checked the model, there is not a 'password' field. ( I am working on a company project, that is why I am having difficulty to find the problem )
I can assign a password with using user.password = ... but I cannot call it back it seems. (I tried this in rails console, assigning worked, calling back did not and I could use the user and the password for logging in manually)
EDIT:
I found out that the problem is database matching. I could create the user but the test is not using that user...
I found the problem. It was because of data transaction. I changed it truncation for system testing and it worked! It was basically flushing all the data before using it. That was why I was getting error.

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

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

Devise user creation fails during Capybara integration testing (and Selenium webdriver)

I'm trying to create a user during an integration test to use for some operations. I'm using devise with :confirmable. The code is the following:
user = User.create({username: "user1", password: "pass1234", password_confirmation: "pass1234", email: "test#email.com"})
user.confirm!
fill_in "Username", :with => user.username
fill_in "Password", :with => user.password
click_button "Sign in"
The problem is that the login fails every time I try it. There are no errors about the user creation, but for some reason the user doesn't seem to "be there" when I try to login. I just get 'Invalid username or password' when I try to sign in. This seems like something to do with the fact that maybe Capybara/Selenium webdriver isn't waiting properly for the database operation to take place before it tries to sign in. If that's the case, how could I test it or fix it?
Is it "wrong" to even be trying to insert into the database during an integration test?
I don't use devise myself so can't really comment on the specifics of the problem you're encountering, but this question caught my eye:
Is it "wrong" to even be trying to insert into the database during an integration test?
Yes, I would say it generally is.
Your integration tests should test your code from the point of view of the user:
Expectations should only depend on what the user can actually see.
Actions should correspond only to what the user can actually do.
Inserting something into the database goes beyond the range of actions that the user has at their disposal. It is something for a unit test perhaps, but not for an integration test.
That being said, you could argue that seeding database data is a bit of an exception to this rule, since you're setting up context for your test (see my comments below).

Resources