John Small
John Small

Reputation: 973

Cucumber Devise login via remote Selenium chromium not working

Context: I set up a new system using rails-new into a devcontainer. So the dev environment is running inside docker. I'm using a dockerised Selenium Chromium to run my tests. I'm using Devise for authentication, and Cucumber for BDD.

Basic login is working when running manually, but not working when the Cucumber tests run. The user is set up correctly and first_user.valid_password?(password) is true inside the test steps. But Devise returns a 401 and the user is not logged in.

The logs show the email and password are coming through correctly. So I'm guessing that it must be something to do with CSRF tokens or something to do with the session.

Since this is a standard setup and thousands of people must have tested this and got over this problem, what's the solution?

Here's features/support/config.rb

require "capybara/cucumber"
require "selenium-webdriver"

Capybara.register_driver :selenium_remote_chrome do |app|
  options = Selenium::WebDriver::Chrome::Options.new
  options.add_argument("--window-size=1400,1400")
  options.add_argument("--no-sandbox")
  options.add_argument("--disable-dev-shm-usage")
  Capybara::Selenium::Driver.new(
    app,
    browser: :remote,
    url: "http://#{ENV.fetch("SELENIUM_HOST")}:4444/wd/hub",
    options: options
  )
end

Capybara.configure do |config|
  config.run_server = true
  config.server_port = ENV.fetch("CAPYBARA_SERVER_PORT", 3015).to_i
  config.server_host = "0.0.0.0"
  config.default_driver = :selenium_remote_chrome
  config.app_host = "http://rails-app:#{config.server_port}"
end

The feature file ;-

Background:
    Given a user exists with email "[email protected]" and password "password123"

  Rule: A user can login with the correct email and password

    Scenario: User logs in successfully
      When I go to the login page
      And I fill in "Email" with "[email protected]"
      And I fill in "Password" with "password123"
      And I press "Log in" 
      And I wait for the page to load
      Then I should see "Dashboard"
      And I should see "Log out"

the steps;-

Given("a user exists with email {string} and password {string}") do |email, password|
  User.create!(email: email, password: password, password_confirmation: password)
  expect(User.count).to eq(1) # Ensure that the user was created
  u = User.first
  expect(u.valid_password?(password)).to be true # Ensure that the password is correct
end

When("I go to the login page") do
  visit new_user_session_path
end

When("I fill in {string} with {string}") do |field, value|
  fill_in field, with: value
end

When("I press {string}") do |button|
  click_button button
end

Then("I should see {string}") do |text|
  expect(page).to have_content(text)
end

And("I wait for the page to load") do
  sleep 1
end

And("I wait for {int} seconds") do |seconds|
  sleep seconds
end

And log/test.log ;-

Completed 401 Unauthorized in 2ms (ActiveRecord: 0.2ms (1 query, 0 cached) | GC: 0.0ms)
Processing by Devise::SessionsController#new as TURBO_STREAM
  Parameters: {"user"=>{"email"=>"[email protected]", "password"=>"password123"}, "commit"=>"Log in"}
  Rendering layout layouts/application.html.haml
  Rendering devise/sessions/new.html.haml within layouts/application
  Rendered devise/shared/_links.html.haml (Duration: 0.3ms | GC: 0.0ms)
  Rendered devise/sessions/new.html.haml within layouts/application (Duration: 2.5ms | GC: 0.0ms)
  Rendered layout layouts/application.html.haml (Duration: 3.3ms | GC: 0.0ms)

Note that I removed email and password from log filtering so that I can see the values being passed in.

Login works when run manually

Any ideas what could be causing this problem ?

Upvotes: 0

Views: 20

Answers (1)

John Small
John Small

Reputation: 973

Solved it. I needed to use ```DatabaseCleaner.strategy = :truncation`` instead of :transaction

In the past I've been running Cucumber tests inside rack with the browser being controlled locally so that records created are visible to the browser.

But I can't do that when running inside a docker container. So I have to use a dockerised selenium which runs remotely. Therefore the default database cleaner strategy, :transaction won't work because records would be created inside a transaction, are are therefore invisible to remote selenium.

Once I switched to :truncation it all worked.

Upvotes: 0

Related Questions