Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't access example's driver in before hook for system tests #2550

Closed
arielj opened this issue Dec 31, 2021 · 8 comments
Closed

Can't access example's driver in before hook for system tests #2550

arielj opened this issue Dec 31, 2021 · 8 comments

Comments

@arielj
Copy link

arielj commented Dec 31, 2021

What Ruby, Rails and RSpec versions are you using?

Ruby version: 2.6.5
Rails version: Rails 6.1.4.1
RSpec version: rspec-core (3.10.1) and rspec-rails (5.0.2)

Observed behaviour

I'm trying to interact with the selenium driver to use the intercept feature https://github.com/SeleniumHQ/selenium/blob/selenium-4.0.0/rb/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb#L63. I want to intercept all the requests done by the browser (my idea is to warn the users if the js code is doing any external request) so I'm trying to setup the intercept in a before hook.

The problem is that page.driver is a Capybara::RackTest::Driver in the before hook but it's a Capybara::Selenium::Driver during the actual example code, so I can't really interact with the example's driver before the test.

Expected behaviour

I would expect the before(:example) hook to have access to the same page.driver of the example.

Can you provide an example app?

I don't have an example app, but it's pretty easy to reproduce. With an example that requires javascript (so the driver is not RackTest), run page.driver in a before(:each) block for that example and then run page.driver when already in the example block. The first will show a:

EDIT: added a sample app to reproduce this, the README includes the instructions for the 3 methods I tried with RSpec and the config for Minitest too to compare https://github.com/arielj/HooksPageDriver/

Some debugging

I've been doing some debugging and I got to here https://github.com/rspec/rspec-core/blob/3-10-maintenance/lib/rspec/core/example.rb#L262. If I do @example_group_instance.page.driver I get the RackTest driver, but when I step inside the next instruction (with step in byebug) I'm taken directly inside the example bock, where I can run page.driver to get the correct one.

I also tried with MiniTest and it works fine using an after_setup hook, in that case page.driver is the same driver the test uses.

@pirj
Copy link
Member

pirj commented Dec 31, 2021

I guess this is due to the hook execution order, similar to #2526.
What you're interested in is run_before_example one line above.

I suggest giving the config.before trick a shot.

@arielj
Copy link
Author

arielj commented Dec 31, 2021

I found a workaround by overriding the initialize method of the SystemExampleGroup module like in here https://github.com/rspec/rspec-rails/blob/main/lib/rspec/rails/example/system_example_group.rb#L92 to add a before block that ends up having the correct driver:

module RSpec::Rails::SystemExampleGroup
  def initialize(*args, &blk)
    super(*args, &blk)

    self.class.before do
      # this gets executed twice, the first time the driver is incorrect but the second time the driver of the example block
      puts page.driver
    end
  end

I know this is not ideal, but maybe it helps understaing what's going on, I'm not familiar on how rspec works.

@arielj
Copy link
Author

arielj commented Dec 31, 2021

I guess this is due to the hook execution order, similar to #2526. What you're interested in is run_before_example one line above.

I suggest giving the config.before trick a shot.

I tried with the include_context trick from the other issue and it didn't work. Also tried with a config.before(:each, type: :system) block. I can try to make a sample app tomorrow with the different things I tried and the workaround.

@arielj
Copy link
Author

arielj commented Dec 31, 2021

I created a sample app with the 3 methods I tried (before hook, shared context before hook, monkeypatched initialzie method) https://github.com/arielj/HooksPageDriver/

the details are on the README file and inside spec/rails_helper.rb as a comment

@pirj
Copy link
Member

pirj commented Dec 31, 2021

A quick non-systematic solution would be to explicitly set the driver in a sibling hook:

RSpec.configure do |config|
  config.before(:each, type: :system) do
    driven_by :selenium_headless # I'm on Firefix, just :selenium should work for you
  end

  config.before(:each, type: :system) do
    puts "before hook: #{page.driver}"
  end

Normally, the driver is set here, but it's possible to set it before this runs.

@arielj
Copy link
Author

arielj commented Dec 31, 2021

Great, that workaround is much cleaner than the hacky monkeypatch that worked for me.

I changed it to:

config.before(:each, type: :system) do
  driven_by Capybara.javascript_driver
end

I think it can make it easier to maintain.

Thanks!

@JonRowe
Copy link
Member

JonRowe commented Jan 7, 2022

I'm going to close this as its a hook ordering issue but has a workaround, there are other open issues tracking this (like #2526)

@JonRowe JonRowe closed this as completed Jan 7, 2022
@JonRowe
Copy link
Member

JonRowe commented Jan 8, 2022

(I'd also be happy to accept a PR adding this caveat as documentation, e.g. something in the system test feature about how if you need to ensure a certain driver has been set to do this in the hook)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants