Read more

Capybara: Pretending to interact with the document

Henning Koch
March 10, 2021Software engineer at makandra GmbH

Browsers blocks abusable JavaScript API calls until the user has interacted with the document. Examples would be opening new tab or start playing video or audio.

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

E.g. if you attempt to call video.play() in a test, the call will reject with a message like this:

NotAllowedError: play() failed because the user didn't interact with the document first. https://goo.gl/xX8pDD

Workaround

To pretend document interaction in a test you can create an element, click on it, and remove the element again. This unblocks the entire JavaScript API for the current page.

You can include the following module in your test to get a method interact_with_page that does just that:

module InteractWithPage

  def interact_with_page
    create_empty_interaction_stub
    click_empty_interaction_stub
  ensure
    remove_empty_interaction_stub
  end

  private

  def create_empty_interaction_stub
    page.execute_script(<<~JS)
      let stub = document.createElement('div')
      stub.classList.add('empty-interaction')

      // Stop the click from bubbling up and closing any overlays
      stub.addEventListener('click', function(event) {
        event.stopImmediatePropagation()
        event.preventDefault()
      })

      // Make sure the stub has a clickable area
      stub.style.width = '1px'
      stub.style.height = '1px'
      stub.style.backgroundColor = 'blue'

      // Make sure the stub is positioned over any overlays to be clickable
      stub.style.position = 'fixed'
      stub.style.bottom = '0'
      stub.style.right = '0'
      stub.style.zIndex = '9999999999999999'

      document.body.appendChild(stub)
    JS
  end

  def click_empty_interaction_stub
    page.find('.empty-interaction').click
  end

  def remove_empty_interaction_stub
    page.execute_script(<<~JS)
      let stub = document.querySelector('.empty-interaction')
      if (stub) {
        stub.remove()
      }
    JS
  end

end

To use it in Cucumber:

World(InteractWithPage)

When 'I have interacted with the page' do
  interact_with_page
end
Henning Koch
March 10, 2021Software engineer at makandra GmbH
Posted by Henning Koch to makandra dev (2021-03-10 11:04)