feat: Implement comprehensive onboarding system for new users
Add complete user onboarding flow that redirects new users to complete their profile before accessing the application: - Add onboarding_completed boolean field to users with migration - Create OnboardingController with form validation and completion logic - Design professional onboarding UI with progressive disclosure for company info - Implement Stimulus controller for toggling company information section - Add application-wide redirect middleware for incomplete users - Create comprehensive test suite for all onboarding functionality - Update test fixtures and helpers to support onboarding in existing tests The onboarding collects required first/last name and optional company information. Users are redirected to onboarding after login until profile is completed. Features smooth animations, full-width form button, and clean UX design. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
57
test/controllers/application_controller_onboarding_test.rb
Normal file
57
test/controllers/application_controller_onboarding_test.rb
Normal file
@@ -0,0 +1,57 @@
|
||||
require "test_helper"
|
||||
|
||||
class ApplicationControllerOnboardingTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@user_without_onboarding = users(:one)
|
||||
@user_without_onboarding.update!(onboarding_completed: false)
|
||||
|
||||
@user_with_onboarding = users(:two)
|
||||
@user_with_onboarding.update!(onboarding_completed: true, first_name: "John", last_name: "Doe")
|
||||
end
|
||||
|
||||
test "should redirect incomplete users to onboarding from dashboard" do
|
||||
sign_in @user_without_onboarding
|
||||
get dashboard_path
|
||||
assert_redirected_to onboarding_path
|
||||
end
|
||||
|
||||
test "should allow complete users to access dashboard" do
|
||||
sign_in @user_with_onboarding
|
||||
get dashboard_path
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should redirect incomplete users to onboarding from events" do
|
||||
sign_in @user_without_onboarding
|
||||
get events_path
|
||||
assert_redirected_to onboarding_path
|
||||
end
|
||||
|
||||
test "should allow complete users to access events" do
|
||||
sign_in @user_with_onboarding
|
||||
get events_path
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should not redirect from home page when not signed in" do
|
||||
get root_path
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should redirect signed in incomplete users from home to onboarding" do
|
||||
sign_in @user_without_onboarding
|
||||
get root_path
|
||||
assert_redirected_to dashboard_path # Home redirects to dashboard for signed in users
|
||||
end
|
||||
|
||||
test "should not interfere with devise controllers" do
|
||||
get new_user_session_path
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should not redirect when already on onboarding page" do
|
||||
sign_in @user_without_onboarding
|
||||
get onboarding_path
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
104
test/controllers/onboarding_controller_test.rb
Normal file
104
test/controllers/onboarding_controller_test.rb
Normal file
@@ -0,0 +1,104 @@
|
||||
require "test_helper"
|
||||
|
||||
class OnboardingControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@user_without_onboarding = users(:one)
|
||||
@user_without_onboarding.update!(onboarding_completed: false)
|
||||
|
||||
@user_with_onboarding = users(:two)
|
||||
@user_with_onboarding.update!(onboarding_completed: true, first_name: "John", last_name: "Doe")
|
||||
end
|
||||
|
||||
test "should redirect to onboarding when user not signed in" do
|
||||
get onboarding_path
|
||||
assert_redirected_to new_user_session_path
|
||||
end
|
||||
|
||||
test "should show onboarding page for incomplete user" do
|
||||
sign_in @user_without_onboarding
|
||||
get onboarding_path
|
||||
assert_response :success
|
||||
assert_select "h1", "Bienvenue sur AperoNight !"
|
||||
assert_select "form"
|
||||
end
|
||||
|
||||
test "should redirect completed user to dashboard" do
|
||||
sign_in @user_with_onboarding
|
||||
get onboarding_path
|
||||
assert_redirected_to dashboard_path
|
||||
end
|
||||
|
||||
test "should complete onboarding with valid data" do
|
||||
sign_in @user_without_onboarding
|
||||
|
||||
assert_not @user_without_onboarding.onboarding_completed?
|
||||
|
||||
post complete_onboarding_path, params: {
|
||||
user: {
|
||||
first_name: "Jane",
|
||||
last_name: "Smith",
|
||||
company_name: "Test Company"
|
||||
}
|
||||
}
|
||||
|
||||
assert_redirected_to dashboard_path
|
||||
follow_redirect!
|
||||
assert_select ".notification", /Bienvenue sur AperoNight/
|
||||
|
||||
@user_without_onboarding.reload
|
||||
assert @user_without_onboarding.onboarding_completed?
|
||||
assert_equal "Jane", @user_without_onboarding.first_name
|
||||
assert_equal "Smith", @user_without_onboarding.last_name
|
||||
assert_equal "Test Company", @user_without_onboarding.company_name
|
||||
end
|
||||
|
||||
test "should complete onboarding without optional company name" do
|
||||
sign_in @user_without_onboarding
|
||||
|
||||
post complete_onboarding_path, params: {
|
||||
user: {
|
||||
first_name: "Jane",
|
||||
last_name: "Smith",
|
||||
company_name: ""
|
||||
}
|
||||
}
|
||||
|
||||
assert_redirected_to dashboard_path
|
||||
@user_without_onboarding.reload
|
||||
assert @user_without_onboarding.onboarding_completed?
|
||||
end
|
||||
|
||||
test "should not complete onboarding without required fields" do
|
||||
sign_in @user_without_onboarding
|
||||
|
||||
post complete_onboarding_path, params: {
|
||||
user: {
|
||||
first_name: "",
|
||||
last_name: "Smith"
|
||||
}
|
||||
}
|
||||
|
||||
assert_response :success
|
||||
assert_select ".notification", /Veuillez remplir tous les champs requis/
|
||||
|
||||
@user_without_onboarding.reload
|
||||
assert_not @user_without_onboarding.onboarding_completed?
|
||||
end
|
||||
|
||||
test "should not complete onboarding without last name" do
|
||||
sign_in @user_without_onboarding
|
||||
|
||||
post complete_onboarding_path, params: {
|
||||
user: {
|
||||
first_name: "Jane",
|
||||
last_name: ""
|
||||
}
|
||||
}
|
||||
|
||||
assert_response :success
|
||||
assert_select ".notification", /Veuillez remplir tous les champs requis/
|
||||
|
||||
@user_without_onboarding.reload
|
||||
assert_not @user_without_onboarding.onboarding_completed?
|
||||
end
|
||||
end
|
||||
@@ -5,7 +5,10 @@ class OrdersControllerTest < ActionDispatch::IntegrationTest
|
||||
@user = User.create!(
|
||||
email: "test@example.com",
|
||||
password: "password123",
|
||||
password_confirmation: "password123"
|
||||
password_confirmation: "password123",
|
||||
onboarding_completed: true,
|
||||
first_name: "Test",
|
||||
last_name: "User"
|
||||
)
|
||||
|
||||
@event = Event.create!(
|
||||
|
||||
2
test/fixtures/users.yml
vendored
2
test/fixtures/users.yml
vendored
@@ -5,9 +5,11 @@ one:
|
||||
encrypted_password: <%= Devise::Encryptor.digest(User, 'password123') %>
|
||||
last_name: Trump
|
||||
first_name: Donald
|
||||
onboarding_completed: true
|
||||
|
||||
two:
|
||||
email: user2@example.com
|
||||
encrypted_password: <%= Devise::Encryptor.digest(User, 'password123') %>
|
||||
last_name: Obama
|
||||
first_name: Barack
|
||||
onboarding_completed: true
|
||||
|
||||
@@ -63,4 +63,33 @@ class UserTest < ActiveSupport::TestCase
|
||||
refute user.valid?, "User with last_name longer than 12 chars should be invalid"
|
||||
assert_not_nil user.errors[:last_name], "No validation error for too long last_name"
|
||||
end
|
||||
|
||||
# Test onboarding functionality
|
||||
test "new users should need onboarding by default" do
|
||||
user = User.new(email: "test@example.com", password: "password123")
|
||||
assert user.needs_onboarding?, "New user should need onboarding"
|
||||
assert_not user.onboarding_completed?, "New user should not have completed onboarding"
|
||||
end
|
||||
|
||||
test "should complete onboarding" do
|
||||
user = users(:one)
|
||||
user.update!(onboarding_completed: false)
|
||||
|
||||
assert user.needs_onboarding?, "User should need onboarding initially"
|
||||
|
||||
user.complete_onboarding!
|
||||
|
||||
assert_not user.needs_onboarding?, "User should not need onboarding after completion"
|
||||
assert user.onboarding_completed?, "User should have completed onboarding"
|
||||
end
|
||||
|
||||
test "needs_onboarding? should return correct value" do
|
||||
user = users(:one)
|
||||
|
||||
user.update!(onboarding_completed: false)
|
||||
assert user.needs_onboarding?, "User with false onboarding_completed should need onboarding"
|
||||
|
||||
user.update!(onboarding_completed: true)
|
||||
assert_not user.needs_onboarding?, "User with true onboarding_completed should not need onboarding"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,6 +17,18 @@ module ActiveSupport
|
||||
fixtures :all
|
||||
|
||||
# Add more helper methods to be used by all tests here...
|
||||
|
||||
# Helper to create users with completed onboarding by default for tests
|
||||
def create_test_user(attributes = {})
|
||||
User.create!({
|
||||
email: "test#{rand(10000)}@example.com",
|
||||
password: "password123",
|
||||
password_confirmation: "password123",
|
||||
first_name: "Test",
|
||||
last_name: "User",
|
||||
onboarding_completed: true
|
||||
}.merge(attributes))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user