YusukeIwaki/capybara-playwright-driver

"Execution context was destroyed, most likely because of a navigation" when using `expect(page).to have_content` or `have_selector`

dtinth opened this issue · 8 comments

A common pattern in testing is to do this:

click_on 'Confirm'
expect(page).to have_content "Thank you"

However, page sometimes refers to the previous page. This resulted in an error like this:

     Playwright::Error:
       Error: Execution context was destroyed, most likely because of a navigation

This is my current ugly workaround. I monkeypatched Capybara::Session to retry assert_text 3 times:

  module Capybara::Session::PlaywrightSessionHasContentHack
    def assert_text(*args, **options)
      attempt = 0
      loop do |i|
        sleep 1 if attempt > 0
        attempt += 1
        begin
          return super
        rescue => e
          raise e if attempt > 3
          next if e.message.include?("Execution context was destroyed")
          next if e.message.include?("Cannot find context with specified id")
          raise e
        end
      end
    end
  end
  Capybara::Session.prepend(Capybara::Session::PlaywrightSessionHasContentHack)

Improved workaround that works with have_selectors too

  # Monkey-patch Capybara::Session#has_content? to automatically retry
  # when the page changes in the middle of the check.
  #
  # See: https://github.com/YusukeIwaki/capybara-playwright-driver/issues/51
  #
  module Capybara::Session::PlaywrightSessionHasContentHack
    def assert_text(*args, **options)
      retry_if_execution_context_destroyed do
        super
      end
    end

    def assert_no_text(*args, **options)
      retry_if_execution_context_destroyed do
        super
      end
    end

    def assert_selector(*args, **options)
      retry_if_execution_context_destroyed do
        super
      end
    end

    def assert_no_selector(*args, **options)
      retry_if_execution_context_destroyed do
        super
      end
    end

    def retry_if_execution_context_destroyed
      attempt = 0
      loop do |i|
        sleep 1 if attempt > 0
        attempt += 1
        begin
          return yield
        rescue => e
          raise e if attempt > 3
          next if e.message.include?("Execution context was destroyed")
          next if e.message.include?("Cannot find context with specified id")
          next if e.message.include?("Unable to adopt element handle from a different document")
          raise e
        end
      end
    end
  end
  Capybara::Session.prepend(Capybara::Session::PlaywrightSessionHasContentHack)

@dtinth Thank you for bug report. Do you have a code for reproducing this issue?

I don’t have a repro yet. I’ll try to create one once I recover from covid.

Stay safe.

I’m feeling a bit better (and bored) so here’s a repro.

https://github.com/dtinth/capybara-playwright-driver-issue-51-repro

I’ve added multiple layers of redirects because the error happens intermittently, and with different error messages at different times.

@YusukeIwaki Thank you for the fix! 🙏 Looking forward to the new release 😃

v0.3.1 is deployed.
https://rubygems.org/gems/capybara-playwright-driver/versions/0.3.1

Could you try it? Thank you, and sorry for late fix.

v0.3.2 is also deployed, which just includes the fix of shadow_root method.

@YusukeIwaki I have tried it (removed the monkey-patch workaround and bumped version), and it works perfectly. Thank you so much for building and maintaining this project 🙏.