mirror of
https://github.com/we-promise/sure.git
synced 2026-09-05 06:41:08 +00:00
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.
221 lines
6.8 KiB
Ruby
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
|