capybara-playwright-driver icon indicating copy to clipboard operation
capybara-playwright-driver copied to clipboard

Browser Always Running in Headless Mode Despite headless: false Configuration

Open pioz opened this issue 10 months ago • 3 comments

I have registered the Capybara driver for Playwright using the following code:

Capybara.register_driver :playwright do |app|
  Capybara::Playwright::Driver.new(app, browser_type: :chromium, headless: false)
end

My test configuration in test/application_system_test_case.rb is as follows:

require 'test_helper'

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :playwright, screen_size: [1400, 1400]

  Capybara.configure do |config|
    config.server = :puma, { Silent: true }
    config.server_port = 31_337 # same as test.yml
    config.default_max_wait_time = 10 # seconds
    config.test_id = 'data-test-id'
  end
end

However, when I run the tests, the browser window does not appear—it always runs in headless mode. How can I configure the tests to run with a visible browser window?

I'm using Capybara 3.40.0 and capybara-playwright-driver 0.5.4.

Any help or suggestions would be appreciated. Thank you!

pioz avatar Feb 07 '25 15:02 pioz

I also tried it but Chrome actually appeared.

spec_helper.rb

  Capybara.register_driver(:playwright) do |app|
    Capybara::Playwright::Driver.new(app, browser_type: :chromium, channel: :chrome, headless: false)
  end
  Capybara.app = App
  # Capybara.default_driver = :selenium_chrome_debugport
  Capybara.default_driver = :playwright
require 'capybara'
require_relative '../app'
require 'playwright/test'

RSpec.describe 'app' do
  include Playwright::Test::Matchers

  it 'should work' do
    visit '/login'
    fill_in 'username', with: 'admin'
    fill_in 'password', with: 'password!'
    click_button 'ログイン'

    page.driver.with_playwright_page do |page|
      expect(page.get_by_text('ADMIN')).to be_in_viewport
      expect(page.get_by_text('お知らせ')).to be_in_viewport
    end
  end
end

https://github.com/user-attachments/assets/1e157717-cb1c-4b58-ab6b-47aa4432369b

YusukeIwaki avatar Feb 24 '25 10:02 YusukeIwaki

I've also run into this in the context of a Rails test suite. Is there perhaps an interaction with Rails happening here?

Running

capybara (3.40.0)
capybara-playwright-driver (0.5.4)
# rails_helper.rb

Capybara.register_driver :playwright do |app|
  headless = (false unless ENV["CI"] || ENV["HEADLESS"]) || true
  Capybara::Playwright::Driver.new(app,
    browser_type: ENV["PLAYWRIGHT_BROWSER"]&.to_sym || :chromium,
    channel: :chrome,
    headless:)
end

Capybara.default_driver = :playwright

# some stuff

RSpec.configure do |config|
  config.before(:each, type: :system) do
    if ENV["SELENIUM_DRIVER"].present?
      driver = (ENV["HEADLESS"] == "false") ? :chrome : :headless_chrome
      driven_by(:selenium, using: driver, screen_size: [1920, 4096])
    else
      driven_by(:playwright)
    end
  end
end

crespire avatar Mar 17 '25 16:03 crespire

I've also run into this in the context of a Rails test suite. Is there perhaps an interaction with Rails happening here?

Running

capybara (3.40.0)
capybara-playwright-driver (0.5.4)
# rails_helper.rb

Capybara.register_driver :playwright do |app|
  headless = (false unless ENV["CI"] || ENV["HEADLESS"]) || true
  Capybara::Playwright::Driver.new(app,
    browser_type: ENV["PLAYWRIGHT_BROWSER"]&.to_sym || :chromium,
    channel: :chrome,
    headless:)
end

Capybara.default_driver = :playwright

# some stuff

RSpec.configure do |config|
  config.before(:each, type: :system) do
    if ENV["SELENIUM_DRIVER"].present?
      driver = (ENV["HEADLESS"] == "false") ? :chrome : :headless_chrome
      driven_by(:selenium, using: driver, screen_size: [1920, 4096])
    else
      driven_by(:playwright)
    end
  end
end

I will leave my previous comment up for posterity, but the problem in our case which is silly in hindsight is that we had already registred a :playwright driver in another context, and the configuration was conflicting. I've changed our suite runner driver's name to :playwright_tests and we get the behaviour we're expecting.

crespire avatar Mar 17 '25 18:03 crespire

I can confirm that we encountered this problem and we've made the same change as @crespire (register it with a different name, rather than likely the default/suggested name) and it all works (so thanks @crespire !).

However, we didn't have the driver registered anywhere else in our codebase, only in one place in rails_helper.rb (so maybe a gem internally does that).

andyjeffries avatar Apr 18 '25 08:04 andyjeffries

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Apr 26 '25 05:04 stale[bot]

The renaming solution works for me, too.

My settings:

Capybara.register_driver :playwright_test do |app|
  driver_options = {
    # :chromium, :firefox, :webkit
    browser_type: ENV["PLAYWRIGHT_BROWSER"]&.to_sym || :chromium,
    headless: ENV["CI"].present? || ENV["PLAYWRIGHT_HEADLESS"].present?
  }

  Capybara::Playwright::Driver.new(app, **driver_options)
end

Capybara.default_driver = :playwright_test
Capybara.javascript_driver = :playwright_test

# Add this at the end to ensure it doesn't get overridden
RSpec.configure do |config|
  config.before(:each, type: :system) do
    driven_by :playwright_test
  end
end

dpaluy avatar May 11 '25 02:05 dpaluy

Seems like a bug that this doesn't work when the registered driver is called :playwright, but does work otherwise. I'm not seeing another driver registered with the name :playwright before registering the driver in my app, but, sure enough, the driver customizations don't seem to kick in unless the driver is named something other than :playwright.

tylerhunt avatar Jun 23 '25 15:06 tylerhunt

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jul 19 '25 03:07 stale[bot]

I deeply investegated about this issue with adding log into Capybara::RegistrationContainer.

.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/capybara-3.40.0/lib/capybara/registration_container.rb

    def initialize
      @registered = {}
    end

    def register(name, block)
      puts "REGISTER ------- name: #{name} --- #{block}"
      puts caller
      @registered[name] = block
    end

Then I observed the logs below.

REGISTER ------- name: playwright --- #<Proc:0x0000000122567278 /Users/yusuke-iwaki/Desktop/railsplaywright/spec/rails_helper.rb:50>
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/capybara-3.40.0/lib/capybara.rb:132:in 'Capybara.register_driver'
/Users/yusuke-iwaki/Desktop/railsplaywright/spec/rails_helper.rb:50:in 'block (2 levels) in <top (required)>'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/example.rb:457:in 'BasicObject#instance_exec'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/example.rb:457:in 'RSpec::Core::Example#instance_exec'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/hooks.rb:365:in 'RSpec::Core::Hooks::BeforeHook#run'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/configuration.rb:2191:in 'block in RSpec::Core::Configuration#run_suite_hooks'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/configuration.rb:2189:in 'Array#each'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/configuration.rb:2189:in 'RSpec::Core::Configuration#run_suite_hooks'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/configuration.rb:2096:in 'RSpec::Core::Configuration#with_suite_hooks'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/runner.rb:116:in 'block in RSpec::Core::Runner#run_specs'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/reporter.rb:74:in 'RSpec::Core::Reporter#report'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/runner.rb:115:in 'RSpec::Core::Runner#run_specs'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/runner.rb:89:in 'RSpec::Core::Runner#run'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/runner.rb:71:in 'RSpec::Core::Runner.run'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/runner.rb:45:in 'RSpec::Core::Runner.invoke'


REGISTER ------- name: playwright --- #<Proc:0x0000000122522da8 /Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/actionpack-8.1.1/lib/action_dispatch/system_testing/driver.rb:41>
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/capybara-3.40.0/lib/capybara.rb:132:in 'Capybara.register_driver'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/actionpack-8.1.1/lib/action_dispatch/system_testing/driver.rb:41:in 'ActionDispatch::SystemTesting::Driver#register'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/actionpack-8.1.1/lib/action_dispatch/system_testing/driver.rb:28:in 'ActionDispatch::SystemTesting::Driver#use'
<internal:kernel>:91:in 'Kernel#tap'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-rails-7.1.1/lib/rspec/rails/example/system_example_group.rb:153:in 'RSpec::ExampleGroups::Authentication#driven_by'
/Users/yusuke-iwaki/Desktop/railsplaywright/spec/rails_helper.rb:58:in 'block (2 levels) in <top (required)>'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/example.rb:457:in 'BasicObject#instance_exec'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/example.rb:457:in 'RSpec::Core::Example#instance_exec'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/hooks.rb:365:in 'RSpec::Core::Hooks::BeforeHook#run'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/hooks.rb:529:in 'block in RSpec::Core::Hooks::HookCollections#run_owned_hooks_for'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/hooks.rb:528:in 'Array#each'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/hooks.rb:528:in 'RSpec::Core::Hooks::HookCollections#run_owned_hooks_for'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/hooks.rb:615:in 'block in RSpec::Core::Hooks::HookCollections#run_example_hooks_for'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/hooks.rb:614:in 'Array#reverse_each'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/hooks.rb:614:in 'RSpec::Core::Hooks::HookCollections#run_example_hooks_for'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/hooks.rb:484:in 'RSpec::Core::Hooks::HookCollections#run'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/example.rb:505:in 'RSpec::Core::Example#run_before_example'
/Users/yusuke-iwaki/.rbenv/versions/3.4.1/lib/ruby/gems/3.4.0/gems/rspec-core-3.13.6/lib/rspec/core/example.rb:261:in 'block in RSpec::Core::Example#run'

spec/rails_helper.rb:

  # Configure Capybara to use Playwright driver
  config.before(:suite, type: :system) do
    Capybara.register_driver :playwright do |app|
      Capybara::Playwright::Driver.new(app, browser_type: :chromium, headless: false)
    end
    Capybara.javascript_driver = :playwright
    Capybara.default_driver = :playwright
  end

  config.before(:each, type: :system) do
    driven_by :playwright, using: :chromium, screen_size: [1400, 1400], options: {
      headless: true
    }
  end

With this code, most of us would expect that this code register and overrides Rails's default playwright driver configuration. However in fact, this works as:

  1. Register customized playwright driver
  2. Find playwright driver invoked by driven_by
  • driven_by internally register the default Rails's playwright first --> this overrides customized playwright driver
    • https://github.com/rails/rails/blob/v8.1.1/actionpack/lib/action_dispatch/system_testing/driver.rb#28
  • find a driver registered as playwright --> the Rails's default driver is found

Similar issue: https://github.com/rails/rails/issues/39987

YusukeIwaki avatar Oct 30 '25 00:10 YusukeIwaki