Read more

Capybara: Execute asynchronous JavaScript

Tobias Kraze
November 05, 2019Software engineer at makandra GmbH

Capybara provides execute_script and evaluate_script to execute JavaScript code in a Selenium-controlled browser. This however is not a good solution for asynchronous JavaScript.

Illustration book lover

Growing Rails Applications in Practice

Check out our e-book. Learn to structure large Ruby on Rails codebases with the tools you already know and love.

  • Introduce design conventions for controllers and user-facing models
  • Create a system for growth
  • Build applications to last
Read more Show archive.org snapshot

Enter evaluate_async_script, which allows you to execute some asynchronous code and wait until it finishes. There is a timeout of a couple of seconds, so it will not wait forever.

Use it like this:

page.evaluate_async_script(<<~JS)
  let [done] = arguments
  doSomethingAsynchronous().then(() => {
    done() // call this to indicate we're done
  })
JS

You can return results to Ruby by passing them to the done callback:

result = page.evaluate_async_script(<<~JS)
  let [done] = arguments
  doSomethingAsynchronous().then(() => {
    done("some result")
  })
JS

Finally, you can pass additional object from Ruby to Javascript:

result = page.evaluate_async_script(<<~JS, arg1, arg2)
  let [arg1, arg2, done] = arguments
  doSomethingAsynchronous().then(() => {
    done("some result")
  })
JS

Returning a promise

Warning

The following is not documented anywhere, it may be incorrect.

In newer Capybara versions you may also wrap a promise inside an IIFE and use evaluate_script:

result = page.evaluate_script(<<~JS)
  (function() {
    return doSomethingAsynchronous()
  })()
JS

Of course, you may also add an async-modifier to this function declaration to enable the usage of await. Please check, if this works for you. If not, use evaluate_async_script and chain your calls.

Posted by Tobias Kraze to makandra dev (2019-11-05 12:09)