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")
Related
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"))
I have a Rails app that I've recently refactored standard flash[:success] messages to use a flash[:toast], using http://materializecss.com/dialogs.html
Here's what I'm doing with my flash partial:
<% flash.each do |type, message| %>
<% if type == "success" %>
<div class="alert alert-success alert-dismissable" role="alert">
...
</div>
<% elsif type == "toast" %>
<script>
$(function() {
Materialize.toast('<%= message %>', 3000);
});
</script>
<% else %>
<div class="alert alert-danger alert-dismissible" role="alert">
...
</div>
<% end %>
<% end %>
This works and looks awesome, especially on mobile and for complex data pages where I'm sending the user back to the middle of the page, and a standard flash success message on the top of the page would not be visible.
I can easily test if flash[:toast] is not nil in a controller test, but in capybara feature tests, I don't have access to that, and I can't use the standard code that I used to use to test flash[:success] like:
expect(page).to have_content("Your email address has been updated successfully.")
expect(page).to have_css(".alert-success")
Right now I am resorting to just testing that an alert danger is not present, like:
expect(page).to_not have_css(".alert-danger")
This works, but isn't really testing that the toast fired off. Is there any way to check that the javascript fired or that the toast appeared on the page for the 3 seconds?
As long as you're using a JS capable driver (pretty much anything but the default rack-test driver) then
expect(page).to have_content("The message shown by the toast")
should find the toast when it displays on the page. If you want to check that it appears and disappears within 3 seconds you could do something like
expect(page).to have_content("The message shown by the toast")
expect(page).not_to have_content("The message shown by the toast", wait: 3)
The first statement should wait for the text to appear and the second will wait up to 3 seconds for it to disappear. If you want to actually verify the text is being shown in the toast container then you could do
expect(page).to have_css("#toast-container", text: "The message shown") #update css selector to whatever container you are displaying the toast in
expect(page).not_to have_css("#toast-container", text: "The message shown", wait: 3)
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')
I have the following view partial in my Ruby on Rails 4 project:
<div class="badges">
<% #user.badges.each do |badge| %>
<img
height="47"
width="40"
src='<%= image_path("badges/#{badge}.svg") %>'
onerror='this.src=\'<%= image_path("badges/#{badge}.png") %>\'; this.onerror=null;'
/>
<% end %>
</div>
It's tested in a view test as follows:
it "shows the badges" do
User::ALLOWED_BADGES.each do |badge|
badge_image = image_path("badges/#{badge}.svg")
expect(rendered).to match "src='#{badge_image}'"
end
end
The rendered view shows the following:
<img
height="47"
width="40"
src='/assets/badges/cga50-92b4667cd8419a060a981a9ac5ff0152a3cd011141cae5f9964240d55aa5d88b.svg'
onerror='this.src=\'/assets/badges/cga50-3d0a4668a8b44c8518760546dc6f253d4287ba2ddd11e6ad577d7916f05d055f.png\'; this.onerror=null;'
/>
But the string value being matched against is src='/images/badges/cga50.svg', not src='/assets/badges/cga50-92b4667cd8419a060a981a9ac5ff0152a3cd011141cae5f9964240d55aa5d88b.svg'.
Why does image_path create the correct url in the redered view, but not in the test?
As Jim Edelstein pointed out in the comment above, this is a bug in Rails.
After some digging I found it's also a slightly contentious issue amongst the rails devs and the PR to 'fix' it is done but will be part of Rails 5, not Rails 4.
My work-around is to change the test as follows:
shared_examples 'shows the badges' do
it "shows the badges" do
User::ALLOWED_BADGES.each do |badge|
expect(rendered).to match /src='\/[a-z]+\/badges\/#{badge}-[a-f0-9]+\.svg/
end
end
end
It's a bit grubby but it works.
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.