I'm experimenting with introducing Turbo Drive on a couple of pages in my Rails 6.1 app.
Ḯ've got it as expected after moving some legacy JQuery plugin initialization from $(document).ready() calls into document.addEventListener('turbo:load', ..) calls.
When I'm running my Capybara feature specs, however, I can see from the screenshots of the failing specs like the JQuery plugins have not been initialized like they should. One typical example of a test that is failing :
scenario 'element is visible', :js do
visit(my_page_path)
expect(page.find(#some-jquery-plugin-created-element).text).to \
eq 'some expected text from the plugin element'
end
Can anyone help me understand why this is not working in the feature specs? It looks to me like the turbo:load event is not getting triggered at all.
I enabling logging in the driver:
Selenium::WebDriver::Remote::Capabilities.chrome( "goog:loggingPrefs": { browser: 'ALL' } ).
.. but calling page.driver.browser.manage.logs.get(:browser) just before the failing expect call just returns an empty array.
Perhaps I'm doing it wrong?
Capybara.default_max_wait_time = 10
By writing your expectations like
expect(page.find(#some-jquery-plugin-created-element).text).to \
eq 'some expected text from the plugin element'
you are defeating Capybaras retry functionality which will lead to lots of failing tests in dynamic pages. You should "never" use the basic RSpec matchers (eq, etc) with Capybara related objects.
In your case the find call is waiting for a matching element to exist, then getting its text and checking it. This will fail because when the element is first added to the page it may not yet have its full text contents. Instead you should be using the Capybara provided matchers which will use Capybaras waiting behavior for things to match
expect(page.find(#some-jquery-plugin-created-element)).to have_text('some expected text from the plugin element')
From my experience: Capybara returns from visit() after the page is loaded, but not necessarily after all scripts are run. Your test is continued right away, before 'turbo:load' event is triggered/processed. You can verify if that's the case by putting sleep() in your test - which will in a crude way solve your problem:
scenario 'element is visible', :js do
visit(my_page_path)
sleep 10
expect(page.find(#some-jquery-plugin-created-element).text).to \
eq 'some expected text from the plugin element'
end
My understanding is that this is how browsers actually work: there is a gap after the content is shown, but before all scripts are executed on load. This is not a problem for user experience, as the gap is short and it's very unlikely that user is able to interact with the page during that short period. It's different in case of tests, as they are much faster than human.
To make tests work without unnecessary delay, you need to make sure to continue them only after required scripts are executed. One way to do that is to:
As an example, I do hide the whole HTML element:
<html style="visibility: hidden;">
Then show it only after Turbo is loaded:
function showPage(event) {
document.documentElement.style.visibility="visible";
}
document.addEventListener('turbo:load', showPage);
and after every page load, use methods with waiting capability, to make sure test will continue only after page is completely loaded.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With