My app has quite a few different places where I update the database by calling ajax functions when the user clicks a button/link/div. I need to include these features in my tests, but I don't yet know enough about Capybara.
Below is an example of one div that I want to test, the associated ajax call, and the test that I would like to pass. I should add that I'm building through Cloud9 IDE, in case that makes a difference.
View
<% @seminar.students.order(:last_name).in_groups_of(6).each do |group| %>
<div class="row">
<% group.each do |student| %>
<% if student %>
<% this_ss = @seminar.seminar_students.find_by(:user => student) %>
<% if this_ss.present %>
<% thisClass = "col-md-2 seat clickySeat" %>
<% thisText = "Present" %>
<% else %>
<% thisClass = "col-md-2 seat clickySeat absent" %>
<% thisText = "Absent" %>
<% end %>
<div class = "<%= thisClass %>" id="<%= this_ss.id %>" name="ss_<%= this_ss.id %>">
<%= student.first_plus_init %><br/>
<p class="presentTag"><%= thisText %></p>
</div>
<% end %>
<% end %>
</div>
<% end %>
Coffeescript
if $('.clickySeat').length > 0
$('.clickySeat').on "click", ->
this_tag = $(this).find(".presentTag")
seminar_student_id = $(this).attr('id')
url = '/seminar_students/'+seminar_student_id
if this_tag.text() == "Absent"
attendance = true
$(this).removeClass("absent")
this_tab.text("Present")
else
attendance = false
$(this).addClass("absent")
this_tab.text("Absent")
$.ajax
type: "PUT",
url: url,
dataType: "json"
data:
seminar_student:
present: attendance
Test
test "attendance with click" do
@ss = @student_3.seminar_students.find_by(:seminar => @seminar)
assert @ss.present
capybara_login(@teacher_1)
click_on("desk_consult_#{@seminar.id}")
click_on("#{new_consultancy_button_text}")
find("##{@ss.id}").click
assert_not @ss.present
click_on("Create Desk Consultants Groups")
end
In development and production, the divs and ajax are working to toggle students being marked absent/present in the database. But in testing, the line assert_not @ss.present fails. This is the pattern that I'm trying to fix everywhere: none of my capybara tests are invoking any javascript.
I've read through a few similar questions and the related articles. I've tried all suggestions that I've read so far, such as adding the selenium-webkit gem so that I can use the line Capybara.javascript-driver = :selenium. No solutions that I've encountered so far have worked for me.
Thank you in advance for any insight.
None of your tests running JS is generally one of two problems, and then there are two further issues with your test that would probably cause failure beyond that.
For the JS issue, the first cause could be that you have an error in one of your JS assets which causes them not to be processed when concatenated together. Since you state it works in production mode (where concatenation also occurs) we should be able to rules this out. The second cause would be that you're not running with a JS capable driver. Make sure you have followed the instructions from the README - https://github.com/teamcapybara/capybara#using-capybara-with-minitest - are deriving your test classes from ActionDispatch::IntegrationTest and that the last part of that section (where Capybara.current_driver = Capybara.javascript_driver) is being run before any tests that require JS. (If you want all your tests to run with JS support you can just set Capybara.default_driver = Capybara.javascript_driver after setting Capybara.javascript_driver). If using Rails < 5.1 you'll probably also need to install and configure database_cleaner - see https://github.com/teamcapybara/capybara#transactions-and-database-setup
That should get your tests running with a JS capable driver - beyond that -
First: When you call find(...).click
that is not guaranteed to wait for any actions caused by the click to complete. This means you need to set an assertion (one of the Capybara provided assertions since they have waiting/retrying behavior) for a visible change triggered by the click to ensure it has completed before attempting to access an element from the database.
Second: since you've already loaded @ss
it's not going to reflect a change in DB state until you reload the object.
Together that means your test will need to change along the lines of
test "attendance with click" do
@ss = @student_3.seminar_students.find_by(:seminar => @seminar)
assert @ss.present
capybara_login(@teacher_1)
click_on("desk_consult_#{@seminar.id}")
click_on("#{new_consultancy_button_text}")
find("##{@ss.id}").click
assert_text("You are marked as absent") # An assertion for whatever visible change occurs due to the previous click which will delay the next statement until the app has completed processing
assert_not @ss.reload.present # Need to reload the object to see any DB changes
click_on("Create Desk Consultants Groups")
end
The need to reload objects all the time is part of the reason that doing assertions on DB objects is generally frowned upon, and you should mainly just be testing the UI behavior/changes (in feature tests)