Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capybara feature specs fails with Turbo Drive

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.


What I've tried

Checking the browser logs

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?

Making sure Capybara doesn't time out before the page finishes loading

Capybara.default_max_wait_time = 10

like image 251
rogerkk Avatar asked Oct 23 '25 15:10

rogerkk


2 Answers

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')
like image 69
Thomas Walpole Avatar answered Oct 25 '25 05:10

Thomas Walpole


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:

  1. Include some hidden element on your page,
  2. Make it visible e.g. in your turbo:load handler only after everything else is done,
  3. In your tests wait for the element to show up using Capybara methods that have retry capability.

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.

like image 25
cryptogopher Avatar answered Oct 25 '25 03:10

cryptogopher



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!