Capybara and dropzone.js (Rails) - ruby-on-rails

I am trying to follow the answers that I found elsewhere but for some reason, while there aren't any basic code errors, the following test fails anyway. if I look at the screenshot taken during the test, I can see that there is no "book cover" attached. So it looks like that the actual attachment of the jpg is not happening...? I am not quite sure how to go from here?
Test:
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'creating a book', type: :system do
scenario 'displays a cover image if there is one', js: true do
visit new_book_path
fill_in 'Book Title', with: 'Catcher in the Rye'
el = find('#book_cover', visible: false)
el.attach_file('db/sample/images/cover-1.jpg')
click_on 'Create Book'
expect(page).to have_css('div.cover-large img')
end
end
The expected code that I have on my webpage - confirmed in a dev environment is something like
<div class="cover-large">
<img src="...">
</div>
(and if there is no real image, the img tag is replaced with an svg tag that renders a dummy image).
EDIT: Here is the HTML source of the form in question around the #book_cover element:
<div
class="form-col col-wide dropzone dropzone-default dz-clickable"
data-controller="coverdrop"
data-coverdrop-max-file-size="3"
data-coverdrop-max-files="1"
>
<input data-coverdrop-target="input" data-direct-upload-url="http://127.0.0.1:3000/rails/active_storage/direct_uploads" type="file" name="book[cover]" id="book_cover" />
<div class="dropzone-msg dz-message">
<h3 class="dropzone-msg-title heading4 color-primary-dark">Drag cover image here or click to browse</h3>
<span class="dropzone-msg-desc small color-primary-dark">3 MB file size maximum. Allowed are only image file types.</span>
</div>
</div>
The corresponding CSS sets display=none for the input element with type=file.

I was able to get the upload to complete on dropzone's official website here: https://www.dropzone.dev/js/
image = './spec/fixtures/images/space_sloth.jpg'
page.attach_file(image) do
page.find('.dropzone').click
end
That works for me on the Dropzone site. Is there something in the DOM that you can click that invokes the Windows Explorer / MacOS Finder file chooser? You should use that element in the code block.

If you are working with a newer version of capabraya you can try:
find(".dropzone").drop(Rails.root.join("path/to/image"))

Related

Rails project, testing with capybara a hidden field: Selenium::WebDriver::Error::ElementNotVisibleError: element not visible

(the other stackoverflow answers do not answer my question)
I have a form with a hidden_field_tag:
<form action="/score" method="post">
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
<%= hidden_field_tag :letters, #letters, id: "idgrid" %>
<input type="text" name="word" placeholder="type world">
<input type="submit" value="send">
</form>
I want to run following test with capybara (minitest)
require "application_system_test_case"
class GamesTest < ApplicationSystemTestCase
test "word in the grid but not english" do
visit new_url
fill_in "word", with: "car"
find('#idgrid', visible: false).set("N e w t e x t")
click_on "send"
assert_text "Sorry, can't build from grid"
end
end
I get following error
Error:
GamesTest#test_word_in_the_grid_but_not_english:
Selenium::WebDriver::Error::ElementNotVisibleError: element not visible
(Session info: headless chrome=68.0.3440.106)
(Driver info: chromedriver=2.41.578706 (5f725d1b4f0a4acbf5259df887244095596231db),platform=Mac OS X 10.13.6 x86_64)
test/system/games_test.rb:22:in `block in <class:GamesTest>'
I have read the other stackoverflow questions related to the topic, and not find the right answer. Also github issues. I understand that the element is not visible, but I have read that capybara can set the value, if I use the visible: false.
Could someone please help me?
Thanks
With Capybara you can find and inspect non-visible elements but you can't directly set their values because a normal user can't and therefore it doesn't really make a lot of sense to be directly changing them in feature/system tests. If you really need to change a hidden fields value you will need to do so via JS using something like
find('#idgrid', visible: false).execute_script("this.value = "N e w t e x t")
Since this doesn't replicate behavior a user would normally do, it's generally considered bad practice in feature/system tests and you really should think about whether there's better way of setting up those fields values that actually replicates a user (or if it makes more sense as a different type of test).

Capybara failed to find position for element

I'm using Capybara with a webkit driver and when I'm running tests with js: true it raises error listed below. When I do same things in other tests without js: true everything works fine.
PS. There is no need for js :true in this test. This code is actually inside a helper but I put it here like test , so it will be easier to understand.I'm using js: true in the other test that invokes this helper method.
Code below raises Capybara::Webkit::ClickFailed:
Failed to find position for element /html/body/div/div/div/div[2]/a[12]
scenario "adding logged days", js: true do
visit '/logged_days'
find(:xpath, "//a[contains(.,'12')]").click
# click_link("12") raises same error
expect(current_path).to eq("/logged_days/new")
fill_in "Опис виконаної роботи", with: "Some description"
fill_in "Кількість відпрацьованих годин", with: 40
click_button "Додати"
expect(current_path).to eq("/logged_days")
expect(page).to have_content("40")
end
/logged_days:
<div class="page-header">
<h2>Logged Days <small>March</small></h2>
</div>
<div class="conteiner-fluid logged_days_container">
<% for i in 1..31 %>
<%= link_to new_logged_day_path(:cal_date => "#{i}"), method: :get do %>
<div class="calendar_cell">
<p class="cell_date"><%= i %></p>
<p class= "cell_text"></p>
</div>
<% end %>
<% end %>
</div>
I think I had this error exactly once in all my years and it was about an element being covered or otherwise not being clickable.
A thorough investigation with your browser's webinspector is necessary here: You have to figure what your js does to that click target.
When using js:true your page not only runs JS but also has CSS processed. This means you can end up with elements that are are non-visible, overlapped, or moving. You need to look at what is done to the element in a real browser and make sure the element is actually clickable, or what other actions a user would have to do first to make it clickable.
Secondly, don't use .eq with current_path - it'll lead to flaky tests as you use js capable drivers. Instead use the has_current_path matcher
expect(page).to have_current_path('/logged_days/new')

What causes the failure to click buttons that are not in forms when using capybara?

I have searched this for some good time in SO, and on Google. I am a newbie(only 7 months in development), but I'd like an explanation as to why capybara's click_button does not respond to buttons that are not in forms
take and example of this
<div id="btn-div">
<%= button_tag('Attach new file', id: "attach-file-btn", class: "btn btn-info") %>
</div>
in this case when you are writing tests you get an error of the type
undefined method `node_name' for nil:NilClass
however when you put this in a form like this it works
<form id="btn-form">
<%= button_tag('Attach new file', id: "attach-file-btn", class: "btn btn-info") %>
</form>
if you may, please provide an explanation as to why this happens.
and take the following into consideration
the button is not submitting anything, it is only causing JavaScript to show a form
when I try to find the button by using xpath or find it is available, however click_button does not work for it
update
code for click button is similar to this:
describe "Getting to the deployable tools page" do
before do
#tool = FactoryGirl.create(:tool)
#tool.set_status(1)
end
before { visit assigned_tools_path }
before { click_button('attach-file-btn') }
it { should have_content('select tools to add') }
it { should have_css('#non-successful-tool-adding') }
end
Try using a capybara finder, and then invoke click on it, like so:
page.find('#attach-file-btn').click
If you just want to click the file show at broswer with text Attach new file, use:
click_button 'Attach new file'

Why isn't Capybara filling in these form elements?

I have been trying to set up Capybara to test a form but I keep getting the error
cannot fill in, no text field, text area or password field with id, name, or label 'Name' found
Here is what I have in my view:
<%= form_for(#user) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
...
<%= f.submit "Create Account", class: "btn btn-large btn-primary" %>
<% end %>
which renders the following html:
<label for="user_name">Name</label>
<input id="user_name" name="user[name]" size="30" type="text" />
So it seems like it should be finding the field based on the label.
Here is what my user_pages_test.rb file has (I am using Test::Unit and shoulda-context):
context "sign up page" do
should "add user to database when fields are filled in" do
fill_in "Name", with: "Bubbles"
...
click_button "Create Account"
end
end
Here is what I've tried so far:
1) changing the call to fill_in to match the id with fill_in "user_name", with: "Bubbles"
2) changing the call to fill_in to page.fill_in "Name", with: "Bubbles" to match the example in the documentation
3) changing the view to manually add the id "Name" with <%= f.text_field :name, id: "Name" %> (this answer)
4) changing the call to get sign_up_path to get "/sign_up" (in case it was an issue with the routing)
All these still give me the same error, which makes me think that the page isn't being loaded correctly for some reason. However, I have a different (passing) test in the same context that asserts the page has the correct title, so I know the page does get loaded correctly (in the setup).
Based on this (and according to this answer), it seems like the problem might just be that the fill_in method isn't waiting for the page to load before trying to access the fields. According to this suggestion, I added the line puts page.body in my test to see that the HTML was being loaded completely before it was trying to fill in the fields, and got the following output:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
So, I was pretty sure that fill_in just wasn't waiting for the page to load. I then tried
5) changing the Capybara.default_wait_time according to this answer, but this had no effect. I tried setting it in the ActionDispatch::IntegrationTest class in the test_helper file (where Capybara is included), and also in the test itself.
Then I tried adding puts page.body in the passing test (both before and after it asserts the correct title), and I got the same output. THEN, I found this answer, and finally got the console to print out the page's HTML. I tried one more thing to get Capybara to fill in the fields:
6) changed the call to fill_in to say #response.fill_in, since #response seems to do what I thought the page variable was supposed to do.
So, I have two questions about this:
1) What does the page variable actually refer to? The doctype declaration for my app is just <!DOCTYPE html>, so I have no idea where it gets the old one from.
2) Why isn't Capybara able to find/fill_in these fields?
You need to use the visit method to get the page object setup properly. The capybara documentation indicates that '/' is visited by default and if you want to visit some other page you need to do an explicit call. It may also be helpful to read a bit about the difference between visit and get.

How to test what image is being displayed using rspec?

Depending on what page is being viewed I want to use a different image for my logo; the logo on the homepage is bigger. I like using request specs to test behaviour, so I would like to do something like this:
describe 'Visit "advertentie/1"' do
it 'contains add details' do
add = create(:add_with_photos)
visit add_path add
page.should have_selector( 'img[alt="logo-small"]' ) # CHECK IMAGE ALT
page.should have_content( add.name )
end
end
and the test runs agains some haml generated html:
<div class='logo-wrapper'>
<h1>
<a href="/"><img alt="Logo-big" src="/assets/logo-small.png" />
<br>
<span>UpMarket</span>
</a>
</h1>
</div>
however this selector doesn't work. Is this possible, and how?
Did you try the have_css method?
have_css("img[src*='w3schools']")
(Selects every <img> element whose src attribute value contains the substring "w3schools")

Resources