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 online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
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)