RSpec Capybara unable to accept alert - ruby-on-rails

I've got below form:
<%= form_with url: kyc_document_upload_path, multipart: true, data: { turbo_confirm: "Are you sure to upload file?" } do |f| %>
<%= f.hidden_field :document_type, value: item %>
<%= f.label :file, 'Upload', name: "upload_button" %>
<%= f.file_field :file, class: "inputfile", accept: ".pdf", onchange: 'this.form.requestSubmit()' %>
<% end %>
Now I want to test this using Capybara. So what I did was:
it 'show message' do
login_as user
visit profile_path
expect(page).not_to have_css '#Upload'
attach_file("#{Rails.root}/spec/fixtures/files/sample.pdf") do
accept_alert "Are you sure to upload file?" do
first(:label, 'Upload').click
end
end
expect(page).not_to have_table
end
But it produces me an error:
Capybara::ModalNotFound:
Unable to find modal dialog with Are you sure to upload file?
How to accept these alert? If I remove accept_alert block to be:
it 'show message' do
login_as user
visit profile_path
expect(page).not_to have_css '#Upload'
attach_file("#{Rails.root}/spec/fixtures/files/sample.pdf") do
first(:label, 'Upload').click
end
expect(page).not_to have_table
end
I've got an error:
Selenium::WebDriver::Error::UnexpectedAlertOpenError:
unexpected alert open: {Alert text : Are you sure to upload file?}
(Session info: headless chrome=107.0.5304.87)

As mentioned in my comment, it looks like the alert is being triggered by the onchange handler of the file input. That means it wouldn't be triggered until the file selection is done, which is when attach_file returns - therefore you've likely got the accept_alert in the wrong place, and instead need
accept_alert "Are you sure to upload file?" do
attach_file("#{Rails.root}/spec/fixtures/files/sample.pdf") do
first(:label, 'Upload').click
end
end

Related

capybara and click_on method + simple_form [javascript error]

I'm using minitest, capybara and poltergeist and this is the simple_form to test:
<%= simple_form_for :search, url: ads_path , wrapper: :inline_form, html: {class: 'form-inline'}, :method => :get do |f| %>
<%= f.error_notification %>
<%= f.input :type_id, collection: #types, label_html: {class: 'form_home'}, selected: 'house'%>
<%= f.input :city, label: 'Where?', placeholder: 'What is the city ?'%>
<br>
<br>
<%= f.submit "Search", :class => "btn btn-primary btn-xl page-scroll" %>
<% end %>
The problem relies when I add the click_on 'Search'
test "search correctly" do
visit "/"
select "house", :from => "search_type_id"
fill_in 'search_city' , with: ""
click_on 'Search'
end
The javascrip error on the terminal:
Capybara::Poltergeist::JavascriptError:
Capybara::Poltergeist::JavascriptError: One or more errors were raised
in the Javascript code on the page. If you don't care about these
errors, you can ignore them by setting js_errors: false in your
Poltergeist configuration (see documentation for details).
Error: Bounds are not valid.
Error: Bounds are not valid.
at http://127.0.0.1:43411/assets/application-1442915127e4fa1072f69aa9aa4d07ce85cdc5115df9b3a40da528ee05ebfe94.js:43537
at http://127.0.0.1:43411/ads?utf8=%E2%9C%93&search%5Btype_id%5D=1&search%5Bcity%5D=&commit=Search:135
test/integration/home_test.rb:16:in `block in <class:HomeTest>'
Is there other alternative ?
You have two options, fix your JS or disable JS error reporting as mentioned in the error message.
To fix your JS either start by looking at the combined JS line 43537 to see why it's logging "Bounds are not valid" or do the same behavior in dev mode to figure out which individual JS file is producing the error and start debugging from there.
If on the other hand you don't care about fixing the cause of the error (you should care about it though) then you can configure your driver to not report JS errors with
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, js_errors: false)
end
as mentioned in the Poltergeist readme - https://github.com/teampoltergeist/poltergeist#customization

No route matches [POST] "/" only in testing

Using Simple Form For, I have created a custom auth system.
routes.rb
get '/users/sign_in' => 'sessions#new', as: :sessions_new_user
post '/users/sign_in' => 'sessions#create', as: :sessions_create_user
match '/users/sign_out' => 'sessions#destroy', as: :sessions_destroy_user, via: :delete
get '/users/process_login' => 'sessions#process_login', as: :sessions_process_login
new.html.erb
<%= simple_form_for :user do |f| %>
<%= f.input :login, placeholder: "Login", label_html: { class: 'form-control' }%>
<%= f.input :password, label_html: { class: 'form-control' } %>
<%= f.submit "Log in", class: 'btn btn-lg btn-primary btn-block' %>
<% end %>
This issue is when i run the tests to simulate a sign in, I get this error.
user visits root of application
Failure/Error: click_on 'Log in'
ActionController::RoutingError:
No route matches [POST] "/"
user_spec.rb
scenario 'user visits root of application' do
visit root_path
expect(page).to have_content("Sign in to continue")
fill_in ' Login', with: ENV['NTUSERNAME']
fill_in ' Password', with: ENV['NTPASSWORD']
click_on 'Log in'
end
This works fine in development. I cant figure it out.
Firstly, if you are learning Rails I'd recommend you start out with the simple form helpers, as you can more clearly define what you're looking for. Since you are not using RESTful routes, you'll have to define everything by hand and are really missing out on Rails's approach of convention over configuration.
However, if you really want to make Simple Form work, here's how you would do it:
<%= simple_form_for :user, url: sessions_create_user, method: :post do |f| %>
<%= f.input :login, placeholder: "Login", label_html: { class: 'form-control' }%>
<%= f.input :password, label_html: { class: 'form-control' } %>
<%= f.submit "Log in", class: 'btn btn-lg btn-primary btn-block' %>
<% end %>
Also be aware that since you are not using an ActiveRecord-backed object (just a label), you'll have to be careful to match your expected params to what Simple Form is sending in. I'm also curious as to how you have SF render out the correct action path for a user session in development without specifying it out...
because simple_form_for don't have url, it will use the current url to submit data
Not tested but I think as below:
In development, when you visit root, it will redirect to /users/sign_in, posting to /users/sign_in is acceptable
In rspec, redirecting not change the current url, it still root_path, so posting to / is invalid
Nested forms are not allowed...
I had accidentally nested a form tag with an action of "#". It all worked in development as the browser spotted it was a nested form, and it was ignoring the tag. But when I ran a rack test it failed with No route matches [POST] “/” - it also did work when I used JS/selenium test as it was being run in a real browser.
<%= simple_form_for #user do |f| %>
<form action="#" method="POST">
<%= f.input :login %>
<%= f.input :password %>
<%= f.submit "Log in" %>
</form>
<% end %>

Uncheck check_box only not working with rspec / capybara tests

I have a nested form that has 4 checkboxes. Currently, everything is working in browser, but I can't get the capybara tests to uncheck the checkbox and save.
Using Rails 4.2.2 and latest versions of capaybara-webkit and rspec
settings.html.erb
<%= f.fields_for :preferences do |f| %>
<div class="email-notifications-holder">
<div class="email-holder">
<%= f.label :new_match, "Getting a new match each week" %>
<%= f.check_box :new_match, class: "checkbox new_match_email" %>
</div>
<div class="email-holder">
<%= f.label :match_reminder, "New matches Thursday reminder", class: "match_reminder_email" %>
<%= f.check_box :match_reminder, default: true, class: "checkbox" %>
</div>
<div class="email-holder">
<%= f.label :accepted_match, "A Glassbreakers accepted a match", class: "accepted_match_email" %>
<%= f.check_box :accepted_match, default: true, class: "checkbox" %>
</div>
<div class="email-holder">
<%= f.label :new_message, "Received a new message", class: "new_message_email" %>
<%= f.check_box :new_message, default: true, class: "checkbox" %>
</div>
</div>
<% end %>
edit_account_spec.rb
it "allows the user to opt out of new match email", :js do
user = create(:user)
preferences = create(:preference, user: user)
sign_in(user)
visit edit_user_path(user)
click_tab(t("edit_user.tabs.settings"))
find(:css, "#user_preferences_attributes_0_new_match").set(false)
within "#button-container" do
page.find('.save.main-save-button-edit').trigger('click')
end
visit edit_user_path(user)
click_tab(t("edit_user.tabs.settings"))
user.preferences.reload
new_match_email_checkbox = find(".new_match_email")
expect(new_match_email_checkbox.checked?).to be_falsey
end
I've tried clicking it, unchecking it, checking it, trigger clicking it, wrapping it around a within block, reloading the db, etc.
new_match_email_checkbox = find(".new_match_email")
within(".email-notifications-holder") do
page.uncheck('Getting a new match each week')
end
new_match_email_checkbox.set(false)
Right now when you save a user's profile, you must have onboard skills saved or else it will throw an error message when you're trying to click the save button.
part of the user controller
def update
if update_current_user?(user_params)
redirect_to user_path(current_user)
else
flash["notice"] =
"Please choose 3 industries, fields and years of experience."
redirect_to edit_user_path(current_user)
end
end
private
def update_current_user?(update_params)
skills_chosen?(update_params[:user_onboard_skills_attributes]) &&
current_user.update(update_params)
end
Using save_and_open_page, the error alert wasn't appearing so it was unclear what was happening. I was able to debug this by trailing the logs while running the tests using:
tail -f log/test.log
Just using this will uncheck the checkbox
within(".email-notifications-holder") do
page.uncheck('Getting a new match each week')
end
But you then have to grab the element to test it.
new_match_email_checkbox = find(".new_match_email")
expect(new_match_email_checkbox.checked?).to be_falsey
Note:
One thing I am unclear about. Are you trying to make this line work?:
find(:css, "#user_preferences_attributes_0_new_match").set(false)
or are you trying to uncheck the checkbox after you call user.preferences.reload ?

testing cloudinary direct image upload with capybara rspec

I'm trying to test cloudinary direct uploading through capybara but capybara is saying that it cannot find the image upload field. How do you test direct uploading?
picture_spec.rb
require 'spec_helper'
feature "User Pictures" do
let(:user) { create(:user) }
describe "Uploading an image" do
describe "for profile photo" do
it "raises an error when clicked outside cropping image so image is not cropped" do
visit new_user_picture_path(user)
attach_file('#photo upload', "#{Rails.root}/spec/support/images/PRATIQUE_BIG.png")
click_on "Valider Photo"
expect(page).to have_content "SVP crop votre image et celle-ci doit mesurer au moins 100 x 100px"
end
end
end
end
new picture page
= form_for(#photo, :url => url, role: "form") do |f|
.form_line.form-group
.upload_button_holder
= link_to "Parcourir", "#", class: "btn btn-default upload_button form-control"
#photo-upload= f.cl_image_upload :image, class: "form-control"
%span.status
.form-group
.form_control
.preview
= f.hidden_field :crop_x
= f.hidden_field :crop_y
= f.hidden_field :crop_w
= f.hidden_field :crop_h
.form-group
= f.submit "Valider Photo", class: "btn btn-lg btn-success"
= hidden_field_tag :direct, params[:direct]
%p= link_to "Retour à Mon Profil", profile_path(current_user.user_code)
// Configure Cloudinary jQuery plugin
= cloudinary_js_config
Capybara then gives back Unable to find file field "photo-upload"
The name of the field in case of cl_image_upload is always file. photo-update is the id of the surrounding div. You can assign an id to the file input field by passing html: {id: "my-id"} to cl_image_upload. BTW. class also belongs inside the html hash parameter.

Test a form text_field presence with capybara

I have the following form and I want to check if the text field is present or not. How can I do that ?
<%= form_for(ownership, remote: true) do |f| %>
<div>
<%= f.text_field :confirm, value: nil %>
<%= f.hidden_field :start_date, value: Time.now %>
</div>
<%= f.submit t('button.ownership.take.confirmation'), class: "btn btn-small"%>
<% end %>
Here my test :
describe "for not confirmed ownership" do
before do
FactoryGirl.create(:agreed_ownership, user: current_user, product: product)
be_signed_in_as(current_user)
visit current_page
end
# it { should_not have_text_field(confirm) }
it { should_not have_button(t('button.ownership.take.confirmation')) }
end
You'd use a has_css? expectation:
it "should have the confirm input field" do
visit current_page
expect(page).to have_css('input[type="text"]')
end
You can use additional jQuery-style selectors to filter for other attributes on the input field, too. For example, 'input[type="text"][name*="confirm"]' would select for confirm appearing in the input field's name attribute.
To set an expectation the field isn't present, you'd use to_not on your expectation: expect(page).to_not have_css('input[type="text"]')
Bonus: Here's the older, should-style syntax:
it "should have the confirm input field" do
visit current_page
page.should have_css('input[type="text"]')
end
it "shouldn't have the confirm input field" do
visit current_page
page.should_not have_css('input[type="text"]')
end

Resources