From 92077efa46451a2d8e0182d397fbcf99bc5888b3 Mon Sep 17 00:00:00 2001 From: Stefanni Brasil Date: Mon, 16 Mar 2026 10:06:27 -0600 Subject: [PATCH 1/3] Check what user sees first before checking DB assertions When github's free CI servers are under heavy load, a race condition between the page loading and checking the database causes tests to flake. This is caused by a system test inputting data into a form then immediately checking the database without waiting for the form to finish submitting. For every database check in the system files, make sure it's preceded by a capybara matcher with automatic waiting or replace the database check with a check for something to appear on the page. --- spec/system/devise/passwords/new_spec.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/spec/system/devise/passwords/new_spec.rb b/spec/system/devise/passwords/new_spec.rb index 12f88073cb..30c76e529b 100644 --- a/spec/system/devise/passwords/new_spec.rb +++ b/spec/system/devise/passwords/new_spec.rb @@ -45,14 +45,19 @@ it "sends user email" do fill_in "Email", with: user.email - expect { click_on "Send me reset password instructions" }.to change { ActionMailer::Base.deliveries.count }.by(1) - expect(ActionMailer::Base.deliveries.last.to_addresses.map(&:address)).to include user.email + click_on "Send me reset password instructions" + + expect(page).to have_content "If the account exists you will receive an email or SMS with instructions on how to reset your password in a few minutes." + + expect(ActionMailer::Base.deliveries.count).to eq(1) + expect(ActionMailer::Base.deliveries.last.to).to eq([user.email]) end it "has reset password url with token" do fill_in "Email", with: user.email click_on "Send me reset password instructions" + expect(page).to have_content "If the account exists you will receive an email or SMS with instructions on how to reset your password in a few minutes." expect(reset_password_link(user.email)).to match(/http:\/\/localhost:3000\/users\/password\/edit\?reset_password_token=.*/) end @@ -60,6 +65,8 @@ fill_in "Email", with: user.email click_on "Send me reset password instructions" + expect(page).to have_content "If the account exists you will receive an email or SMS with instructions on how to reset your password in a few minutes." + token = reset_password_link(user.email).gsub("http://localhost:3000/users/password/edit?reset_password_token=", "") encrypted_token = Devise.token_generator.digest(User, :reset_password_token, token) expect(User.find_by(reset_password_token: encrypted_token)).not_to be_nil @@ -80,6 +87,7 @@ click_on "Log In" expect(page).to have_text(user.display_name) + expect(page).to have_text("My Cases") expect(page).not_to have_text("Sign in") end end From f4f0658342aa28adfd29eaea8139bf99b781ede8 Mon Sep 17 00:00:00 2001 From: Stefanni Brasil Date: Mon, 16 Mar 2026 10:08:08 -0600 Subject: [PATCH 2/3] Build factories instead of creating as much as possible This reduces test setup time. I also find it easier to understand when the test checks that an attribute is present instead of not nil. --- spec/system/devise/passwords/new_spec.rb | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/spec/system/devise/passwords/new_spec.rb b/spec/system/devise/passwords/new_spec.rb index 30c76e529b..95b0cda16e 100644 --- a/spec/system/devise/passwords/new_spec.rb +++ b/spec/system/devise/passwords/new_spec.rb @@ -7,10 +7,10 @@ end describe "reset password page" do - let!(:user) { create(:user, email: "glados@aperture.labs", phone_number: "+16578900012") } - it "displays error messages for non-existent user" do - fill_in "Email", with: "tangerine@forward.com" + user = build(:user, email: "glados@example.com", phone_number: "+16578900012") + + fill_in "Email", with: "tangerine@example.com" fill_in "Phone number", with: user.phone_number click_on "Send me reset password instructions" @@ -18,6 +18,8 @@ end it "displays phone number error messages for incorrect formatting" do + user = create(:user, email: "glados@example.com", phone_number: "+16578900012") + fill_in "Email", with: user.email fill_in "Phone number", with: "2134567eee" @@ -26,12 +28,16 @@ expect(page).to have_text("Phone number must be 10 digits or 12 digits including country code (+1)") end - it "displays error if user tries to submit empty form" do + it "displays error if user tries to submit an empty form" do + user = build(:user, email: "glados@example.com", phone_number: "+16578900012") + click_on "Send me reset password instructions" expect(page).to have_text("Please enter at least one field.") end it "redirects to sign up page for email" do + user = build(:user, email: "glados@example.com", phone_number: "+16578900012") + fill_in "Email", with: user.email click_on "Send me reset password instructions" @@ -69,7 +75,7 @@ token = reset_password_link(user.email).gsub("http://localhost:3000/users/password/edit?reset_password_token=", "") encrypted_token = Devise.token_generator.digest(User, :reset_password_token, token) - expect(User.find_by(reset_password_token: encrypted_token)).not_to be_nil + expect(User.find_by(reset_password_token: encrypted_token)).to be_present end it "user can update password" do From 4bcee40b558088efc88778818076700b989e32c8 Mon Sep 17 00:00:00 2001 From: Stefanni Brasil Date: Mon, 16 Mar 2026 10:16:30 -0600 Subject: [PATCH 3/3] remove unused factory --- spec/system/devise/passwords/new_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/system/devise/passwords/new_spec.rb b/spec/system/devise/passwords/new_spec.rb index 95b0cda16e..7ed3e2fcde 100644 --- a/spec/system/devise/passwords/new_spec.rb +++ b/spec/system/devise/passwords/new_spec.rb @@ -29,8 +29,6 @@ end it "displays error if user tries to submit an empty form" do - user = build(:user, email: "glados@example.com", phone_number: "+16578900012") - click_on "Send me reset password instructions" expect(page).to have_text("Please enter at least one field.") end