Reputation: 3600
I'm using Capybara, and I'm selecting an item from a dropdown using this command:
select "Some Option", from: "client_id", visible: :all
This is the view code I'm using to generate the select:
<%= form_with(url: '#', method: 'GET') do |f| %>
<%= f.select :client_id,
options_for_select(
Client.accessible_by(current_ability)
.map{|c| [c.name, c.id]}, selected_client_id
),
{include_blank: defined?(include_blank) ? include_blank : false},
{
class: "form-control selectpicker border",
id: "client_id",
onchange: "this.form.submit()",
data: {'live-search': true, style: "btn-white"}
}
%>
<% end %>
Which renders fine and works as expected when I myself am interacting with it on the page.
I believe the problem stems from this: onchange: "this.form.submit()"
When Capybara chooses an option from the select, it's supposed to submit the form, and then the page is supposed to reload with a query string /?client_id=X
, but that is not what's happening and I don't know why.
When I look at the screenshot provided from the error, it appears as though Capybara hasn't even selected anything from the dropdown, it just says "Nothing selected."
Here's the Capybara test:
def check_grand_total(expected_value)
visit current_path
click_link "Balance"
# it seems as though Capybara is not waiting for this to finish... but I don't know, just my theory
# this is supposed to trigger: this.form.submit() on the dropdown
select "Some Client", from: "client_id", visible: :all
actual_value = page.first('.grand-total').text
assert_equal(expected_value, actual_value)
end
The error:
E
Error:
OrdersTest#test_deposit_orders:
Capybara::ExpectationNotMet: expected to find css ".grand-total" at least 1 time but there were no matches
test/system/helpers/balance_test_helper.rb:10:in `check_grand_total'
test/system/orders_test.rb:113:in `block in <class:OrdersTest>'
I'm thinking maybe Capybara isn't actually triggering the change
event for some reason.
Upvotes: 0
Views: 1132
Reputation: 49870
The class names you're specifying, and the fact that you're specifying (visible: :all) imply you may be using some type of JS widget to replace the standard html select element. Is that true? Overriding visibility checking in Capybara actions doesn't make any sense since you shouldn't be interacting with non-visible elements, and depending on exactly which action and driver will either error, or just not do anything. If you are using a replacement widget then you need to interact with the elements created by the widget rather than using select
If you're not using a JS widget then it could just be a case of a badly written test. There are a couple of things right away - first is that you are visiting current_path
, second is that you're asserting against text rather than using one of the Capybara provided assertions. The thing to remember is that browser actions occur asynchronously, so when you tell Capybara to choose an option from a select element there is no guarantee the actions triggered by doing that have completed when the select
call returns. Because of that you should be using the capybara provided assertions/expectations which include waiting/retrying behavior in order to sync the browser with the tests.
def check_grand_total(expected_value)
# This visit looks dangerous because it's not guaranteed to have changed
# the page when the `visit` returns, which means it may try and click
# the link on the previous page. You should really be checking for something
# that wouldn't be on the previous page -- It's also strange that there is a current path since tests should be independent
visit current_path
click_link "Balance" # see above comment
select "Some Client", from: "client_id"
# This will wait up to Capybara.default_max_wait_time seconds for
# the grand total element to contain the expected text
assert_selector '.grand-total', exact_text: expected_value
# If using RSpec it would be
# expect(page).to have_selector '.grand-total', exact_text: expected_value
end
Upvotes: 2
Reputation: 29308
So I think the issue is that the onchange
even does not actually fire This "issue" suggests causing focus loss via
fill_in("foo", with: "hello world").send_keys(:tab)
so it may be possible to adapt that to
select("Some Client", from: "client_id", visible: :all).send_keys(:tab)
That being said it also appears that a method exists to force an event on an Element
Capybara::Node::Element#trigger
so I would start with trying:
select("Some Client", from: "client_id", visible: :all).trigger(:onchange)
Disclaimer: I am not very familiar with capybara so this answer is based on source review and googling. According to Thomas Walpole, who clearly has extensive experience, " trigger
isn't supported by most drivers because it doesn't make a lot of sense to use during testing due to it not replicating what a user would actually do."
Upvotes: 1