Files
sure/test/controllers/onboardings_controller_test.rb
T
Brandon bf5ceff269 Fix flaky sign_out teardown in six test suites (#3208)
Six suites (passkey, MFA, SnapTrade, categorize, onboarding and the Active
Storage authorization integration tests) share a sign_out helper that deletes
the user's sessions through the controller, one HTTP request per session,
iterating in unspecified order. The moment the loop deletes the session the
test itself is signed in with, every later request in the loop is
unauthenticated and silently deletes nothing, so whichever sessions happen to
sort after it survive. The sessions fixture belongs to the same user these
suites use, so a surviving fixture row then fails every assertion that expects
the user to have no sessions.

Row order usually favors the fixture, which is why the suites usually pass.
Under parallel CI they fail a few times a week, always in this file family,
always with the fixture session as the leftover. Forcing newest-first order
reproduces it deterministically on current main: ten of the fifteen passkey
tests fail.

Teardown hygiene is not the behavior under test, so the helpers now destroy
the sessions directly, which no order can break. All six suites run green
three times in a row.
2026-08-27 07:30:35 +02:00

221 lines
6.8 KiB
Ruby

require "test_helper"
class OnboardingsControllerTest < ActionDispatch::IntegrationTest
setup do
@user = users(:family_admin)
@family = @user.family
# Reset onboarding state
@user.update!(set_onboarding_preferences_at: nil)
sign_in @user
end
test "should get show" do
get onboarding_url
assert_response :success
assert_select "h1", text: /set up your account/i
end
test "onboarding setup includes required moniker selection" do
get onboarding_url
assert_response :success
assert_select "input[name='user[family_attributes][moniker]'][value='Family'][required]"
assert_select "input[name='user[family_attributes][moniker]'][value='Group'][required]"
assert_select "p.text-sm.font-medium.text-primary", text: /Will be using.*with/i
end
test "should get preferences" do
get preferences_onboarding_url
assert_response :success
assert_select "h1", text: /preferences/i
end
test "preferences page renders Series chart data without errors" do
get preferences_onboarding_url
assert_response :success
# This test specifically targets the Series model bug
# The page should render without throwing the "unknown keyword: :trend" error
assert_select "[data-controller='time-series-chart']"
assert_select "#previewChart"
# Verify that the Series.from_raw_values call in the view works
# If the Series bug existed, this would raise an ActionView::Template::Error
assert_no_match /unknown keyword: :trend/, response.body
end
test "preferences page includes chart with valid JSON data" do
get preferences_onboarding_url
assert_response :success
# Extract the chart data from the response
chart_data_match = response.body.match(/data-time-series-chart-data-value="([^"]*)"/)
assert chart_data_match, "Chart data attribute should be present"
# Decode HTML entities and parse JSON
chart_data_json = CGI.unescapeHTML(chart_data_match[1])
# Should be valid JSON
assert_nothing_raised do
chart_data = JSON.parse(chart_data_json)
# Verify expected structure
assert chart_data.key?("start_date")
assert chart_data.key?("end_date")
assert chart_data.key?("interval")
assert chart_data.key?("trend")
assert chart_data.key?("values")
# Verify trend has expected structure
trend = chart_data["trend"]
assert trend.key?("value")
assert trend.key?("percent")
assert trend.key?("current")
assert trend.key?("previous")
# Verify values array has expected structure
values = chart_data["values"]
assert values.is_a?(Array)
assert values.length > 0
values.each do |value|
assert value.key?("date")
assert value.key?("value")
assert value.key?("trend")
end
end
end
test "should get goals" do
get goals_onboarding_url
assert_response :success
assert_select "h1", text: /What brings you here/i
end
test "should get trial" do
get trial_onboarding_url
assert_response :success
end
test "preferences page shows currency formatting example" do
get preferences_onboarding_url
assert_response :success
# Should show formatted currency example
assert_select "p", text: /\$2,325\.25/
assert_select "span", text: /\+\$78\.90/
end
test "preferences page derives currency from the onboarding country" do
@family.update!(country: "CA", currency: "USD")
get preferences_onboarding_url
assert_response :success
assert_select "select[name='user[family_attributes][currency]'] option[selected][value='CAD']"
end
test "preferences page keeps the saved currency after preferences have been set" do
@family.update!(country: "CA", currency: "USD")
@user.update!(set_onboarding_preferences_at: Time.current)
get preferences_onboarding_url
assert_response :success
assert_select "select[name='user[family_attributes][currency]'] option[selected][value='USD']"
end
test "preferences page preserves an explicit currency override" do
@family.update!(country: "CA", currency: "USD")
get preferences_onboarding_url, params: { currency: "USD" }
assert_response :success
assert_select "select[name='user[family_attributes][currency]'] option[selected][value='USD']"
end
test "preferences page ignores an unsupported currency override" do
@family.update!(country: "CA", currency: "USD")
get preferences_onboarding_url, params: { currency: "NOPE" }
assert_response :success
assert_select "[data-onboarding-currency-override-value='false']"
assert_select "select[name='user[family_attributes][currency]'] option[selected][value='CAD']"
end
test "preferences page shows date formatting example" do
get preferences_onboarding_url
assert_response :success
# Should show formatted date example (checking for the specific format shown)
assert_match /10-23-2024/, response.body
end
test "preferences page includes all required form fields" do
get preferences_onboarding_url
assert_response :success
# Verify all form fields are present
assert_select "select[name='user[family_attributes][locale]']"
assert_select "select[name='user[family_attributes][currency]']"
assert_select "select[name='user[family_attributes][date_format]']"
assert_select "select[name='user[theme]']"
assert_select "button[type='submit']"
end
test "preferences page includes JavaScript controllers" do
get preferences_onboarding_url
assert_response :success
# Should include onboarding controller for dynamic updates
assert_select "[data-controller*='onboarding']"
assert_select "[data-controller*='time-series-chart']"
end
test "all onboarding pages set correct layout" do
# Test that all onboarding pages use the wizard layout
get onboarding_url
assert_response :success
get preferences_onboarding_url
assert_response :success
get goals_onboarding_url
assert_response :success
get trial_onboarding_url
assert_response :success
end
test "onboarding pages require authentication" do
sign_out
get onboarding_url
assert_redirected_to new_session_url
get preferences_onboarding_url
assert_redirected_to new_session_url
get goals_onboarding_url
assert_redirected_to new_session_url
get trial_onboarding_url
assert_redirected_to new_session_url
end
private
def sign_out
# Deleting sessions through the controller de-authenticates the request the
# moment our own session dies, so every later delete in the loop is a
# silent no-op and whichever sessions sort after it survive. The order is
# unspecified, which made every suite that signs out this way flaky.
# Teardown hygiene is not the behavior under test, so destroy directly.
@user.sessions.destroy_all
end
end