thoughtbot / clearance

Rails authentication with email & password.
https://thoughtbot.com
MIT License
3.71k stars 460 forks source link

How do I write system tests with clearance? #1022

Open avk opened 6 months ago

avk commented 6 months ago

I'm struggling with adding clearance to my system tests.

class PiecesTest < ApplicationSystemTestCase
  setup do
    @writer = writers(:one)
    @piece = pieces(:one)
    @fields = {
      draft: t("activerecord.attributes.piece.draft"),
      prose: t("activerecord.attributes.piece.prose"),
      title: t("activerecord.attributes.piece.title"),
      word_count: t("activerecord.attributes.piece.word_count_suffix"),
    }
  end

  test "should add a new prose piece" do
    visit pieces_url(as: @writer)
    click_on t("pieces.actions.new"), match: :first

    check @fields[:prose]
    fill_in @fields[:word_count], with: 5000
    fill_in @fields[:title], with: "Prose Piece"
    fill_in @fields[:draft], with: 1
    click_on t("pieces.actions.submit")

    assert_text t("pieces.actions.created")
  end
end

The backdoor middleware, will only work for the visit call (the first line of the test). It doesn't work with redirects, like the one generated by the click in the second line of the test.

The controller test helpers are also not available if I require "clearance/test_unit".

What else can I try to get this to work?

avk commented 5 months ago

This feels hacky, but here's the first approach I got working across multiple system tests and navigation even within a test:

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :headless_firefox, screen_size: [1400, 1400]

  def sign_in_as(writer)
    # why? ensure cookies can be set on first navigation
    visit root_url if page.driver.browser.current_url == "about:blank"

    # Selenium driver interface to auth cookie borrowed from 
    # https://github.com/nruth/show_me_the_cookies/blob/master/lib/show_me_the_cookies/adapters/selenium.rb
    page.driver.browser.manage.add_cookie(
      # How Clearance keeps track of authenticated users
      name: Clearance.configuration.cookie_name,
      value: writer.remember_token
    )
  end

  def sign_out
    # Selenium driver interface to auth cookie borrowed from 
    # https://github.com/nruth/show_me_the_cookies/blob/master/lib/show_me_the_cookies/adapters/selenium.rb
    page.driver.browser.manage.delete_cookie(
      # How Clearance keeps track of authenticated users
      Clearance.configuration.cookie_name
    )
  end
end

And if I call sign_in_as in setup or in a test, the authentication will persist for the whole test.

class PiecesTest < ApplicationSystemTestCase
  setup do
    @writer = writers(:one)
    sign_in_as @writer
    @fields = {
      draft: t("activerecord.attributes.piece.draft"),
      prose: t("activerecord.attributes.piece.prose"),
      title: t("activerecord.attributes.piece.title"),
      word_count: t("activerecord.attributes.piece.word_count_suffix"),
    }
  end

  test "should add a new prose piece" do
    visit pieces_url # redirects if not signed in
    click_on t("pieces.actions.new"), match: :first # navigates to new page

    check @fields[:prose]
    fill_in @fields[:word_count], with: 1000
    fill_in @fields[:title], with: "Prose Piece"
    fill_in @fields[:draft], with: 1
    click_on t("pieces.actions.submit") # navigates to new page

    assert_text t("pieces.actions.created")
  end
end

The above works with secure cookies, but doesn't work with signed cookies.

Clearance.configure do |config|
  # ...
  config.signed_cookie = false # breaks system tests
  # ...
end

This approach was considerably faster than navigating to sign_in_path and filling out the session form, and as stated originally, more flexible than the backdoor when navigating across pages without explicit visit calls.

I'm very open to other approaches.

avk commented 3 months ago

I could still use input on this. I tried to extend the above example to work with the rack-test driver and had no luck. I could set the cookie, but Clearance would seemingly not be logged in during those system tests.