There are a couple pitfalls when testing more complex websites which use ajax and need to be tested with selenium or phantomjs in order to execute the Javascript in the site.
Random test failures and strange errors can happen if the test code doesn't take the asynchronous nature of the test setup into account.
11. scenario "publish event", js: true do
visit event_url(event)
click_on "Publish Event"
expect(page).to have_content("Registration: Open")
expect(event.reload).to be_published
end
12. scenario "publish event", js: true do
visit event_url(event)
click_on "Publish Event"
expect(page).to have_content("Registration: Open")
expect(event.reload).to be_published
end
13. scenario "publish event", js: true do
visit event_url(event)
click_on "Publish Event"
expect(page).to have_content("Registration: Open")
expect(event.reload).to be_published
end
Failure/Error: Unable to find matching line from backtrace
ActiveRecord::StatementInvalid:
PG::TRDeadlockDetected: ERROR: deadlock detected
DETAIL: Process 7186 waits for AccessShareLock on relation 16570 of
database 16396; blocked by process 7274.
Process 7274 waits for AccessExclusiveLock on relation 17012 of database
16396; blocked by process 7186.
16. module RSpecLoggerListener
extend self
def start(notification)
Rails.logger.info("nn====== Starting new test run")
end
def stop(notification)
Rails.logger.info("nn====== Test run finished")
end
def example_started(notification)
Rails.logger.info("nn====== START #{notification.example.full_description}")
end
def example_passed(notification)
Rails.logger.info("====== PASSED #{notification.example.full_description}")
end
def example_failed(notification)
Rails.logger.info("====== FAILED #{notification.example.full_description}")
end
def example_pending(notification)
Rails.logger.info("====== PENDING #{notification.example.full_description}")
end
end
RSpec.configuration.reporter.register_listener(
RSpecLoggerListener,
:start, :stop, :example_started, :example_passed,
:example_failed, :example_pending)
get it from: https://gist.github.com/mreinsch/2c8dbb01e51c32c3c5c8
17. module CapybaraHelperMethods
def wait_for_ajax
counter = 0
while !finished_all_ajax_requests?
counter += 1
sleep 0.2
raise "AJAX request took too long." if counter >= (Capybara.default_wait_time * 5)
end
end
def finished_all_ajax_requests?
active = page.evaluate_script <<-SCRIPT.strip.gsub(/s+/,' ')
(function () {
if (typeof jQuery != 'undefined') {
return jQuery.active;
}
else {
console.error("jQuery was undefined on " + document.URL + " - will retry...");
return -1;
}
})()
SCRIPT
active && active.zero?
end
end
RSpec.configuration.include CapybaraHelperMethods, :type => :feature
based on https://coderwall.com/p/aklybw/wait-for-ajax-with-capybara-2-0
18. scenario "publish event", js: true do
visit event_url(event)
click_on "Publish Event"
expect(page).to have_content("Registration: Open")
expect(event.reload).to be_published
wait_for_ajax
end
19. Beware of the async!
Michael Reinsch
michael@movingfast.io
github / twitter: mreinsch