Check if all (hidden) elements contain a string with Minitest/Capybara - ruby-on-rails

I have a list of elements on a page (cards with comic books) and I want to test if all elements have the title containing a string (let's say "spiderman").
<%= #comics.each do |comic| %>
<div class="comic-card-text-box">
<p class="comic-card-title"><%= comic.title %></p>
</div>
<% end %>
Using Minitest/Capybara/Rails 7, I'm trying to do something like this:
test "displays search results" do
login_as users(:user1)
visit root_path
fill_in "Search...", with: "spiderman"
assert_selector ".comic-card-title" do |element|
assert_match element, /spiderman/
end
end
And that's the failure I get:
expected to find visible css ".comic-card-title" but there were no matches. Also found "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", which matched the selector but not all filters.
I have 2 issues there that I'm confused about.
I am trying to check if an element contains a substring. What if this element is hidden on the page (only by hovering it will appear)?
How to iterate between these elements to check if all of them have the substring?

Your elements aren't matching either visibility or the filter proc you're passing to assert_selector (don't use assert_match with Capybara elements). If that's because the elements need to be hovered over then you should really just hover over them. Also assert_selector just validates that at least one of the elements will match, not that all of them will. From your questions I'm assuming the comic-card-text-box element is visible on the page (takes up some space) and that the comic-card-title element only becomes visible when you hover over the comic-card-text-box element. If that's the case then you'd want to do something like
cards = all('.comic-card-text-box', minimum: 1) # specifying count: <expected count> rather than minimum would be better
cards.each do |card|
card.hover
assert_css(card, '.comic-card-title', text: 'spiderman')
end
If my assumptions about what exactly you're trying to do, and your sites behavior are wrong please clarify in your question.

Related

RoR. Count the numbers of "No", "Yes" and "Does not apply" in an object in rails

I have a form with a checklist for a vehicle (tires ok?, lights ok?, etc)
As an Answer they only have 3 options "Yes","No" and "Does not apply", I need to count the number of "Yes", the number of "No" and the number of "Does not apply" in the object and display in the show
In the show if I put <%= #equipo %> i got <Equipo:0x00007f96922c1160>
in the controller if i put
#test = Equipo.where(created_at: #equipo.created_at).map { |e| e }
and in the show <%= #test %> give me something like this
[#<Equipo id: 1, nombre: "OL09", c1: "No", c2: "No", c3: "Yes", c4: "Yes", c5: "Does not apply", comments: "adsljadl", created_at: "2020-07-24 18:41:58", updated_at: "2020-07-24 18:41:58">]
If i put map directly in #equipo give me no method error
You can select only those columns that can take the three mentioned values and return an array with their values. You can flatten that and count the number of occurrences in the array:
Equipo
.where(created_at: #equipo.created_at)
.pluck(:c1, :c2, :c3, :c4, :c5)
.flatten
.group_by(&:itself)
.transform_values(&:count)
# {"No"=>2, "Yes"=>2, "Does not apply"=>1}
There are different ways to count the elements in an array, if your Ruby version allows you, you can use Enumerable#tally.

Rails undefined method when accessing hash in view

I've got a hash that I define in my plot controller, under the edit action
#custom_params = { :custom_plot_type => "Line", :x_axis_title => "", :x_low => "", :x_high => "", :y_axis_title => "", :y_low => "", :y_high => "" }
When I visit the edit page, I have a form_with #plot as the model containing the following select box:
= fields_for :custom_params do |c|
= c.label(':custom_plot_type', "Plot Type")
= c.select(:custom_plot_type, options_for_select(['Line', 'Scatter', 'Heat Map', 'Column'], #custom_params[:custom_plot_type]))
Rails gives me the following error
undefined method `custom_plot_type' for #< Hash:0x00007ffff8c4b030>
I have followed the answer on this question and I'm pretty sure my definition of the hash is correct. But no matter which way I format it (for example, using "custom_plot_type" => "Line" and #custom_params["custom_plot_type"] instead) I get the same error in the view.
I can also output the element I want in the console within the controller, using
puts #custom_params
puts #custom_params[:custom_plot_type]
This outputs the correct value, but as soon as I try to access it in the view it crashes
Edit: Adding more information about the form to clarify in response to a comment. My form is structured as follows:
= form_with(model: #plot, local: true, method: "patch") do |f|
= label_tag('plot[name]', "New plot name:")
= text_field_tag('plot[name]', #plot.name)
%br
= label_tag('plot[description]', "Description:")
= text_field_tag('plot[description]', #plot.description)
%br
%br
= fields_for :custom_params do |c|
= c.label(':custom_plot_type', "Plot Type")
= c.select(:custom_plot_type, options_for_select(['Line', 'Scatter', 'Heat Map', 'Column'], #custom_params[:custom_plot_type]))
The thinking behind this was to have the a form that has fields that are associated with the Plot model, and also to have a subform for "custom params" that are not associated with the model itself but will be saved to a file. So the form does have a model instance associated with it, but the select tag in question does not.
I edited the form to use a select_tag instead of a c.select element, as follows:
= fields_for :custom_params do |c|
= label_tag('custom_params[:custom_plot_type]', "Plot Type")
= select_tag('custom_params[:custom_plot_type]', options_for_select(['Line', 'Scatter', 'Heat Map', 'Column'], #custom_params[:custom_plot_type]))
I still use the value of c for other form elements later on, but using _tag elements instead of elements related to the form builder allow me to use the arbitrary hash within the select tag - thanks #tgmerritt for the suggestion

Testing datepicker gem with Rspec

I am using the bootstrap-datepicker-rails gem for my app and have created a form where users can pick a start date and an end date.
When the user edits the form I want them to be able to see the dates that they had previously chosen. (e.g. Start: October 31, 2015, End: November 15, 2015).
I am trying to write a test to make sure my app does this, but I'm stuck on the syntax.
user_updates_project_spec.rb
require "rails_helper"
feature "User updates project" do
before (:each) do
#user = create(:user)
login_as(#user, scope: :user)
#project = create(:project, creator: #user,
name: "Building a robot",
start_at: "2015-10-31",
end_at: "2015-11-15", )
end
scenario "and sees correct dates on form" do
visit edit_user_project_path(#user, #project)
expect(".datepicker").to have_content("October 31, 2015")
end
end
The error I get is:
Failure/Error: expect(".datepicker").to have_content("October 31, 2015")
expected to find text "October 31, 2015" in ".datepicker"
This is what my form looks like:
<div class="col-sm-6 pl0">
<%= f.input :start_at, label: "Start Date", as: :string,
input_html: {value:#project.set_start_date_for_form.to_s(:long),
class: "datepicker start-date"} %>
</div>
<div class="col-sm-6 pr0">
<%= f.input :end_at, label: "End Date", as: :string,
input_html: {value: #project.set_end_date_for_form.to_s(:long),
class: "datepicker end-date"} %>
</div>
Any ideas on why it's not working?
The way you've written your expects, what you're really saying is expect the string ".datepicker" to have the content "October 31, 2015" which is never going to be true. expect needs to take an element that the have_content matcher can be run against rather than a string. You could rewrite it like this
expect(page.find(:css, '.datepicker.start-date')).to have_content("October 31, 2015")
although a better method would probably be to use
expect(page).to have_css('.datepicker', text: 'October 31, 2015)
since that will match if the content is changing or being loaded via ajax.
One other thing to note is that since you're using a datepicker widget you might actually need to look at the html that is produced by the JS in a real browser, instead of the html in your view tempalte, to determine exactly which element you need to be checking for the content inside.
I guess there may be a confusing for .start-date and .end-date also you can use .text attribute to check them:
expect(page.find(:css, '.datepicker.start-date').text).to eq('October 31, 2015')
expect(page.find(:css, '.datepicker.end-date').text).to eq('November 15, 2015')
If it fails, you can print the value to debug it:
puts page.find(:css, '.datepicker.start-date').text
It turns out I wasn't checking inside the correct element when testing.
What solved it was:
expect(page).to have_css("#project_start_at[value='October 31, 2015']")
expect(page).to have_css("#project_end_at[value='November 15, 2015']")
The element id's that datepicker set were project_start_at and project_end_at.

How does fill_in work in Rspec/Capybara?

I am following Michael Hartl's Ruby on Rails Tutorial. When I use rspec/capybara, the fill_in method makes me confused. I have the following view code:
<%= f.label :name %>
<%= f.text_field :name %>
This is my testing code:
fill_in "Name", with: "Example User"
It seems that label and text_field are both required for fill_in to locate the input field. If I either take off f.label or change <%= f.text_field :name %> to be <%= f.text_field :another_name %>, the test will give me ElementNotFound error. Can anyone explain how the fill_in works here? Are input field and label both required for fill_in method?
It is stated that fill_in looks for field name, id or label text. According to ActionView::Helpers::FormHelper section of rails guides, the view code which you ask about should be translated to the following html code:
# I assume that you made a form for a #user object
<label for="user_name">
Name
</label>
<input id="user_name" name="user[name]" type="text" />
As you see, label produced the "Name" text, which you ask for inside of your fill_in expression. But id and name properties of input field are slightly different, so you should have been using id based selector to achieve the same result:
fill_in "user_name", with: 'Example User'
So, to sum up, label field is not required, but you should watch for your html code carefully and select the appropriate parameters for fill_in expression.
Just to add to what twonegatives posted. Here's what the capybara docs say for the fill_in() method:
The field can be found via its name [attribute], id [attribute,] or label text
http://rubydoc.info/github/jnicklas/capybara/Capybara/Node/Actions:fill_in
When I delete the 'Name' label, I can use any of the following and the tests still pass:
fill_in 'user_name', with: "Example User" #locate text field by id attribute
fill_in :user_name, with: "Example User" #locate text field by id attribute
fill_in 'user[name]', with: "Example User" #locate text field by name attribute
fill_in :'user[name]' with: "Example User" #locate text field by name attribute
In ruby, some characters cannot be used in a symbol name unless the whole symbol is quoted.
Capybara must be retrieving all the text fields (or text areas) from the page, then getting the values of the id and name attributes(easily done with something like Nokogiri) then checking if either value is equal to the first argument to fill_in() (after converting the first argument to a String via to_s()).
I provided my 2 cents here: Capybara not finding form elements.
If you call the Rails text_field helper with underscore (:first_name), the DOM gets rendered as "First name" and its what Capybara will need. No need for an id attribute.
View:
<%= form.text_field :first_name %>
Test:
fill_in "First name", with: 'Elaine'

haml select_tag with constants

I'm new to Ruby and Haml, so I"m going around in circles on this. Googling isn't giving me any sample code I can use.
I can use the select_tag and populate the list from a table. But I can't figure out how to use a simple static list of items. Can someone change this to be proper Haml? Note: the source table is 'email' and the field is 'status'.
= select_tag(:email, :status, {"canceled", "pending", "success"})
I'm looking to get a dropdown list that just has the items "canceled, pending, success" in it.
The error I get is odd number list for Hash._hamlout.format_script...
Update: I found some sample code that seemed to be what I need, and it doesn't give any errors, but the dropdown box is empty:
= select_tag(:email, :status,{ "canceled" => "1", "pending" => "2", "success"=>"3"})
Here is the HTML it produces:
<select female="2" male="1" id="email" name="email">status </select >
You are using the tag helper rather than the object-oriented helper. Use select
I'd also recommend using options_for_select. Like so:
= select(:email, :status, options_for_select([["canceled", "1"], ["pending", "2"], ["success", "3"]]))
See:
http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/select
http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/options_for_select
Got it working! I need to use "Select" instead of "Select_tag". :-)
= select(:email, :status,{ "canceled" => "canceled", "pending" => "pending", "success"=>"success"})

Resources