Drop-down menu is empty when testing in Cucumber (Rails) - ruby-on-rails

I'm using Cucumber to do some testing for my Rails app, and all has gone well until I reached the point where I have to choose an option from a drop-down menu. (A selector made with the simple_form gem.) I have the selenium-webdriver gem installed.
This menu works in real-life testing (Chrome, Firefox, Safari), but when I run the Cucumber tests, the menu is empty. I have #javascript enabled for the scenario and I see it running through Firefox. It can find the menu, and when it "clicks" on it, the list is empty, so the test fails (because it needs to select an indicated option -- which isn't there). Any ideas?
EDIT: Here is the code in cucumber (step definition)
#...
find('#user_device_device_id').click
select('<some menu option>', :from => 'user_device_device_id' )
#...
And this is the HAML code for the dropdown in new.html:
= simple_form_for [:manage, #user_device], :html => {:class => 'form-vertical' } do |f|
= f.select(:device_id, Device.all.collect{ |d| [d.get_name, d.id, {'data-connectiontype' => d.connection_type}] }, :required => true)
Source code of the selector in HTML (from Firefox -- not during Cucumber test):
<select id="user_device_device_id" name="user_device[device_id]">
<option value="1" data-connectiontype="abc">Device 1</option>
<option value="2" data-connectiontype="defg">Device 2</option>
</select>

Nevermind, found the problem. The database used in testing is always empty at start, so none of my devices actually existed in testing. The fix was just to call Device.create! with the necessary parameters in one of the steps before I go to the form page. Worked perfectly.

Related

Can I mix in AngularJS in fields added by link_to_add_assocation via Cocoon gem in Rails 4?

So I am using the link_to_add_association helper to dynamically add form fields in a Rails 4 application. I initially thought I could mix in AngularJS as follows:
this is a haml code template (formtastic gem) of what will be added via cocoon
= f.inputs do
= f.input :funding_type, label: "Kite type", as: :select, collection: ["yes", "no", "maybe"], input_html: { "ng-model" => "data", "ng-change" => "update()", "data-ng-options" => "kiteType in data" }
Instead, I get escaped html similar to:
<li class="select input optional" id="person_kite_submissions_attributes_new_kite_submissions_funding_type_input"><label for="person_kite_submissions_attributes_new_kite_submissions_funding_type" class="label">Funding type</label><select name="person[kite_submissions_attributes][new_kite_submissions][funding_type]" id="person_kite_submissions_attributes_new_kite_submissions_funding_type" class="kite_submission_dropdown" ng-model="kiteSubmission.fundingType" ng-change="update()" data-ng-options="fundingType in kiteSubmission.fundingType"><option value=""></option>
<option value="Red">Red</option>
<option value="Blue">Blue</option>
<option value="Green">Green</option></select>
So then I think Angular will not be able to pick up on the DOM injections since the cocoon (or possibly formtastic gem) gem is escaping the html string.
Am I better off just ripping out the cocoon gem and using straight Angular? Or what would be a good approach. It's only when I view source in Chrome that I noticed this, inspecting the element yielded proper looking html code which is why it took me a couple of hours to notice this.

Capybara can't find elements on dropdown lists

I'm trying to troubleshoot a feature spec I'm writing to learn Rails & testing. It's an edit_spec for a Rails 4 app. I'm using RSpec and Capybara.
The Problem:
Capybara can not find elements from two drop down lists on a form. Here's the relevant parts of my form's source code:
<div class="field">
<label for="appointment_member_id">Member</label><br>
<select id="appointment_member_id" name="appointment[member_id]">
<option value="1">Callis Callis</option>
<option value="2">Rachale Rachale</option>
<option value="3">Hal Hal</option>
<option value="4">Kallis Kallis</option>
<option value="5">Earle Earle</option>
<option value="6">Rudy Rudy</option></select>
</div>
<div class="field">
<label for="appointment_trainer_id">Trainer</label><br>
<select id="appointment_trainer_id"
name="appointment[trainer_id]"> <option value="1">Jacob Jacob</option>
<option value="2">Michele Michele</option>
<option value="3">Alex Alex</option>
<option value="4">Yanni Yanni</option>
<option value="5">Upton Upton</option>
<option value="6">Willham Willham</option></select>
</div>
In my edit_spec, I tried using "option value" and "select id" to get Capybara to select names from the drop-down lists:
select('4', :from => 'appointment_member_id')
select('2', :from => 'appointment_trainer_id')
The result is a Capybara::ElementNotFound error. Here's the full text of the error message:
updates an appointment successfully with corrected information (FAILED - 1)
Failures:
1) Editing appointments updates an appointment successfully with corrected information
Failure/Error: select('4', :from => 'appointment_member_id')
Capybara::ElementNotFound:
Unable to find option "4"
I researched the issue and documentation for Capybara, and also a Stackoverflow post with similar problem. The poster solved the issue, but couldn't recall how he did it. I'm baffled as to why Capybara can't find the member and trainer from the drop down list? The option values and names appear to be visible on the form. What am I missing? I appreciate any help!
Capybara simulates user behaviour. So instead of ids and values try to use what the user see:
select 'Kallis Kallis', from: 'Member'
select 'Michele Michele', from: 'Trainer'
You could try the code below, or possibly assign the div and id and replace "div.field" with whatever id you choose
within "div.field" do
find("option[value='4']", text: 'Yanni Yanni').select_option
click_button 'your_submit_button'
end

Form submitted twice, due to :remote=>true

My form submitted twice, after double checked, it was cause by ':remote=>true'. I removed it, my project works well. Who can show me why? And how to use ':remote=>true'?
My ruby code:
<%= form_tag(admin_product_group_product_scopes_path(#product_group), :remote => true, :id => 'new_product_group_form') do %>
<%
options =
grouped_options_for_select(
Scopes::Product::SCOPES.map do |group_name, scopes|
[
t(:name, :scope => [:product_scopes, :groups, group_name]),
scopes.keys.map do |scope_name|
[ t(:name, :scope => [:product_scopes, :scopes, scope_name]), scope_name]
end
]
end
)
%>
<p>
<label for="product_scope_name"><%= t('add_scope') %></label>
<%= select_tag("product_scope[name]", options) %>
<input type="submit" value="<%= t('add') %>" />
</p>
<% end %>
The final html code in browser.
<form accept-charset="UTF-8" action="/admin/product_groups/17/product_scopes" data-remote="true" id="new_product_group_form" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓"><input name="authenticity_token" type="hidden" value="GocX/l4ZNgF/feKtzC8FuohebM2k5MuIHtdeGp2Oi0A="></div>
<p>
<label for="product_scope_name">Add a scope</label>
<select id="product_scope_name" name="product_scope[name]"><optgroup label="Taxon"><option value="taxons_name_eq">In Taxon(without descendants)</option>
<option value="in_taxons">In taxons and all their descendants</option></optgroup><optgroup label="Text search"><option value="in_name">Product name have following</option>
<option value="in_name_or_keywords">Product name or meta keywords have following</option>
<option value="in_name_or_description">Product name or description have following</option>
<option value="with_ids">Products with IDs</option></optgroup><optgroup label="Values"><option value="with">With value</option>
<option value="with_property">With property</option>
<option value="with_property_value">With property value</option>
<option value="with_option">With option</option>
<option value="with_option_value">With option and value</option></optgroup><optgroup label="Price"><option value="price_between">Price between</option>
<option value="master_price_lte">Master price lesser or equal to</option>
<option value="master_price_gte">Master price greater or equal to</option></optgroup></select>
<input type="submit" value="Add">
</p>
</form>
In case people are stumbling on this question like I did:
I had the same problem, and sannankhalid's answer didn't fix it, but deleting a locally precompiled application.js in the public/assets directory did -- the ujs is included twice, so it fires twice. Via https://stackoverflow.com/a/9627690/604093
On Rails 5, rails-ujs replaces jquery_ujs. Events will trigger twice if both are required.
// app/assets/javascripts/application.js
//= require jquery_ujs <-- delete this
//= require rails-ujs
I am assuming that you are using jquery. This is usually happened when there is an incomplete call or there is some sort of error and you haven't refresh the page. Try something like this:
<script type="text/javascript">
$('#new_product_group_form').submit(function() {
$(this).unbind('submit').submit();
});
</script>
It seems that Ryan Muller's answer is correct. But removing application.js is not proper way to do as per my view. What I have done is I have opened the developer's tool in chrome and click on network part. Now when I click on submit button then it would show me that who is making request. So I removed that JS and tried it again and it works. So as per Ryan Muller its correct that its problem of JS by including twice. But make sure you maintain the dependency of JS as well.
Try ctrl-c on the server to stop it. Then rm -r public/assets/ to get rid of the assets directory (and the duplicate application.js). Restart the server from the same terminal window and it might work as expected.
Wanted to add another possible cause for this. For me it was using Mixpanel's api. Specifically https://mixpanel.com/docs/integration-libraries/javascript-full-api#track_forms
It seems, that using :remote=> true in conjunction with mixpanel.track_forms will cause the form to submit via normal html after the desired json.
It's a probably rare, but took me a while to track down.
Here is the HAML equivalent to sannankhalid's.
:javascript
= f.submit(function() {
$(this).unbind('submit').submit();
});
I had the double-POST issue with Rails 4 and Bootstrap 3 (w/jQuery), submitting form updates from modals.
in your application template (or wherever you keep the <%= javascript_include_tag "application" %> tag or the haml equivalent, add the "data-turbolinks-track" => true flag, so the tag will now look like this: <%= javascript_include_tag "application", "data-turbolinks-track" => true %>.
It happened to me once the solution was to remove the google tag manager, because I was tracking the form submission with it.
<!-- Google Tag Manager -->
<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-PQZJ2T"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-PQZJ2T');</script>
In most cases, this issue is caused by jquery_ujs or rails_ujs being included multiple times.
See discussion on: https://github.com/rails/jquery-ujs/issues/208

Filtering displayed records by form input

I'm super new to programming and am stuck... again,
I'm using rails 3 and currently have a venue model where each venue belongs to an area and a type (which are each thier own models) all of the venues in the database are displayed at the index page in partitions displaying the venue name, and its area and type.
How can I go about having a form with 2 dropdowns (areas and types) on the index page which filter the venue records shown depending on what is selected in the dropdowns. e.g. select pubs as type and Manchester as area and only the pubs in Manchester are shown or select pubs as type and all as area and all the pubs from all areas are shown.
I have tried installing sphinx and thinking_sphinx but can't seem to get them working on my windows 7. I got as far as a 1067 error on service startup and a "Failed to start searchd daemon." from thinking_sphinx on rake ts:start, which im presuming is from the sevice not running, so I'm hoping the answer to this won't involve sphinx.
I've had a look at scopes and am thinking this could possibly be the way to go? Although I haven't got the first idea as to how to include a dropdown to select the scope needed or indeed how to write a scope which will satisfy the kind of filter I want.
Thanks very much for any help, its much appreciated!
In your controller you should be able to do something as simple as:
#filtered_venues = Venue.where(:area => params[:venue][:area], :type => params[:venue][:type]).all
That should give you the filtered results that you want.
And then in your view you should be able to use form helpers to create the select elements:
select("venue", "area", ['New York', 'London', 'Amsterdam'], {}, { :prompt => 'Select Area' })
select("venue", "type", ['Pub', 'Outdoor', 'Hall'], {}, { :prompt => 'Select Type' })
Should output something like:
<select name="venue[area]">
<option value="">Select Area</option>
<option value="New York">New York</option>
<option value="London">London</option>
<option value="Amsterdam">Amsterdam</option>
</select>
<select name="venue[:type]">
<option value="">Select Type</option>
<option value="Pub">Pub</option>
<option value="Outdoor">Outdoor</option>
<option value="Hall">Hall</option>
</select>
There are a lot of other ways to get the options in there dynamically if you have collections ready for area or type. Check out the following for more information: http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html
You can do this in several ways, depending on how exactly you want to filter. The best is to build the filter into a .find(:conditions => ...) call and let the database do the hard work. If you don't know beforehand on what parameters you are going to filter, it's easier but less efficient to do it in code. Assuming you have search parameters venue_area and venue_type, you could do something like this in the controller:
def index
#venues = Venue.all # Or whatever criteria you might have here
#venues = #venues.select { |v| v.area_id == params[:venue_area] } if !params[:venue_area].blank?
#venues = #venues.select { |v| v.type_id == params[:venue_type] } if !params[:venue_type].blank?
...
end
You then create dropdowns containing all type and area ID:s in your search form. There are several helpers you can use for this, such as select_tag. The #options would be better populated in the controller, of course, but this shows the relationship clearer:
<form method="get">
<% #options = Area.all.map { |a| [ a.name, a.id ] } %>
<%= select_tag("venue_area", options_for_select(#options)) %>
<input type="submit" value="Filter" />
</form>
This type of filtering could be done in either JavaScript, Ruby, or both.
If the HTML itself contains all the metadata you need to determine which items match which filters, then it could be done entirely in JavaScript by hiding and showing elements in response to a change in the form.
If the user is required to click a submit button for their chosen filters to take effect, then the form could simply submit and you could repopulate the form on the back end with Rails.
If the HTML doesn't have all the metadata and you don't want them to have to submit a form, you can use AJAX to send the chosen filters to the back end, have Rails construct the new form and send it back to the client, and then use JavaScript to update the front end.
You may get a few ideas from the Railscasts episode Search, Sort, Paginate with AJAX. It's not exactly your situation, but it might point you in the right direction for how these types of operations work in general.

How do I allow a user to select zero options from a multiple selection box in rails?

I have a settings model with a column options, and set it to serialize with serialize :options. In my view, I have a multiple selection box, using select("settings", "options", ['option1','option2','option3'], {}, :multiple => true) which works fine so long as the user selects at least one option. However, if they don't select any options, no options are submitted and so the options aren't updated.
How do I allow the user to select zero options from a multiple selection box in rails?
That has nothing to do with rails: html form won't send such parameter to server if nothing is chosen in 'select' element. But you should be able to fix it in controller. Something like this
if params[:settings] == nil
params[:settings] = [];
end
Not sure if there's more elegant solution.
Add a hidden field after the select box, that posts an empty value to "settings[options]"
It's same trick that rails uses to make sure unchecked checkboxes get posted as false.
I do not like assuming a value is empty if the attribute is not posted. It breaks the way Rails expects to update attributes, and it can present problems if you are using your controller actions also for APIs as well as HTML. My preferred way of handling this is by adding a hidden input field before multiselects.
<input type="hidden" value="" name="parent_model[my_attribute_ids][]">
If you use JQuery you can automate the addition of these hidden input fields:
$('select[multiple="multiple"]').each(function(i){
$(this).before('<input type="hidden" name="'+this.name+'" value="" />')
});
I realize this answer is not very timely, but I hope this will help someone with a similar question.
You could provide a "None" option.
Example from: http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html
select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {:include_blank => 'None'}, {:multiple => true})
Would become
<select name="post[person_id]" multiple="multiple">
<option value="">None</option>
<option value="1">David</option>
<option value="2" selected="selected">Sam</option>
<option value="3">Tobias</option>
</select>

Resources