Search code examples
ruby-on-railstestingrspecfunctional-testingcapybara-webkit

Timing issue with ActionCable, Capybara Webkit, and Capybara Session#window


I have a Rails 5 app that uses ActionCable to register 'guest' appearances. When you visit the site, you're prompted for your name. Once entered, your appearance is broadcast to all subscribers. When you exit your window, or 'disappear', you are shown to have left. It works great; the problem I have is testing disappearances - it passes only about half the time.

Here's the relevant test:

RSpec.feature "Appearances", type: :feature, js: true do
  let(:picard) { 'Picard' }
  let(:riker) { 'Riker' }

scenario "A guest disappears" do
  create_guest picard
  new_window = within_new_incognito_window do
    visit '/'
    expect(page).to have_content picard
    create_guest riker
  end  
  new_window.close
  expect(page).to_not have_content riker
end

And the helpers:

module GuestHelpers
  def within_new_incognito_window
    new_window = open_new_window
    within_window new_window do
      Capybara.current_session.driver.clear_cookies
      yield
    end
    new_window
  end

  def create_guest(name)
    within('#new_guest') do
      fill_in('guest[name]', with: name)
    end
    click_on("Go")
    expect(page).to have_content name
  end
end

I've tried setting the default_max_wait_time to 100, I've tried inserting sleep(10) around closing the window, and I've tried ditching the helpers and just doing it procedurally. I think it's a timing issue around closing the window - am I missing something obvious?


Solution

  • To test features using RSpec which require ActiveJobs to run I find the following useful - install rspec-activejob, set config.active_job.queue_adapter = :test in your test.rb and then put the following file in your spec/support directory

    require 'rspec/active_job'
    
    RSpec.configure do |config|
      config.include(RSpec::ActiveJob)
    
      # clean out the queue after each spec
      config.after(:each) do
        ActiveJob::Base.queue_adapter.enqueued_jobs = []
        ActiveJob::Base.queue_adapter.performed_jobs = []
      end
    
      config.around :each, perform_enqueued: true do |example|
        @old_perform_enqueued_jobs = ActiveJob::Base.queue_adapter.perform_enqueued_jobs
        ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
        example.run
        ActiveJob::Base.queue_adapter.perform_enqueued_jobs = @old_perform_enqueued_jobs
      end
    
      config.around :each, peform_enququed_at: true do |example|
        @old_perform_enqueued_at_jobs = ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs
        ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = true
        example.run
        ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = @old_perform_enqueued_at_jobs
      end
    end
    

    which will configure different metadata you can use per test to specify whether or not you want the active jobs created to be executed during your test.

    scenario "does something that needs job to run", perform_enqueued: true do
       ...
    end