diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index b026f6180..9de0e18d9 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -6,51 +6,12 @@
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
-# Offense count: 4
-Capybara/RSpec/NegationMatcherAfterVisit:
- Exclude:
- - 'spec/features/member_portal_spec.rb'
- - 'spec/support/shared_examples/behaves_like_an_invitation_route.rb'
- - 'spec/support/shared_examples/behaves_link_member_viewing_workshop.rb'
-
-# Offense count: 1
-Capybara/RSpec/VisibilityMatcher:
- Exclude:
- - 'spec/components/chapters_sidebar_component_spec.rb'
-
-# Offense count: 2
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: AllowSafeAssignment.
-Lint/AssignmentInCondition:
- Exclude:
- - 'app/controllers/application_controller.rb'
- - 'app/models/feedback.rb'
-
-# Offense count: 1
-# Configuration parameters: AllowedMethods.
-# AllowedMethods: enums
-Lint/ConstantDefinitionInBlock:
- Exclude:
- - 'lib/tasks/setup.rake'
-
# Offense count: 1
# Configuration parameters: DebuggerMethods, DebuggerRequires.
Lint/Debugger:
Exclude:
- 'spec/spec_helper.rb'
-# Offense count: 1
-# Configuration parameters: AllowedParentClasses.
-Lint/MissingSuper:
- Exclude:
- - 'app/components/event_card_component.rb'
-
-# Offense count: 1
-# Configuration parameters: AllowKeywordBlockArguments.
-Lint/UnderscorePrefixedVariableName:
- Exclude:
- - 'spec/models/group_spec.rb'
-
# Offense count: 32
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
Metrics/AbcSize:
@@ -147,22 +108,6 @@ Metrics/PerceivedComplexity:
- 'app/services/invitation_manager.rb'
- 'lib/omniauth/strategies/codebar.rb'
-# Offense count: 6
-Naming/AccessorMethodName:
- Exclude:
- - 'app/controllers/admin/events_controller.rb'
- - 'app/controllers/admin/meetings_controller.rb'
- - 'app/controllers/admin/workshops_controller.rb'
- - 'app/controllers/feedback_controller.rb'
-
-# Offense count: 1
-# Configuration parameters: EnforcedStyle, AllowedPatterns, ForbiddenIdentifiers, ForbiddenPatterns.
-# SupportedStyles: snake_case, camelCase
-# ForbiddenIdentifiers: __id__, __send__
-Naming/MethodName:
- Exclude:
- - 'app/models/member.rb'
-
# Offense count: 10
# Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros, UseSorbetSigs.
# NamePrefix: is_, has_, have_, does_
@@ -172,7 +117,6 @@ Naming/MethodName:
Naming/PredicatePrefix:
Exclude:
- 'app/controllers/application_controller.rb'
- - 'app/models/member.rb'
- 'app/models/workshop.rb'
- 'app/policies/application_policy.rb'
- 'app/policies/chapter_policy.rb'
@@ -182,23 +126,8 @@ Naming/PredicatePrefix:
# Offense count: 2
RSpec/AnyInstance:
Exclude:
- - 'spec/controllers/member/details_controller_spec.rb'
- 'spec/support/helpers/login_helpers.rb'
-# Offense count: 1
-# Configuration parameters: Max, CountAsOne.
-RSpec/ExampleLength:
- Exclude:
- - 'spec/features/admin/event_spec.rb'
-
-# Offense count: 3
-# Configuration parameters: Max.
-RSpec/MultipleExpectations:
- Exclude:
- - 'spec/features/admin/event_spec.rb'
- - 'spec/features/admin/sponsor_spec.rb'
- - 'spec/lib/omniauth/strategies/codebar_spec.rb'
-
# Offense count: 5
Rails/HasAndBelongsToMany:
Exclude:
diff --git a/app/components/event_card_component.rb b/app/components/event_card_component.rb
index 8bcbbad1b..1bbed31a1 100644
--- a/app/components/event_card_component.rb
+++ b/app/components/event_card_component.rb
@@ -1,5 +1,6 @@
class EventCardComponent < ViewComponent::Base
def initialize(event_card:, user: nil)
+ super()
@event = event_card
@user = user
end
diff --git a/app/controllers/admin/events_controller.rb b/app/controllers/admin/events_controller.rb
index eab8cdb58..918558c6f 100644
--- a/app/controllers/admin/events_controller.rb
+++ b/app/controllers/admin/events_controller.rb
@@ -8,7 +8,7 @@ def new
def create
@event = Event.new(event_params)
- set_organisers(organiser_ids)
+ assign_organisers(organiser_ids)
if @event.save
redirect_to [:admin, @event], notice: 'Event successfully created.'
@@ -31,7 +31,7 @@ def show
end
def update
- set_organisers(organiser_ids)
+ assign_organisers(organiser_ids)
if @event.update(event_params)
redirect_to [:admin, @event], notice: 'You have just updated the event'
@@ -94,7 +94,7 @@ def grant_organiser_access(organiser_ids = [])
organiser_ids.each { |id| Member.find(id).add_role(:organiser, @event) }
end
- def set_organisers(organiser_ids)
+ def assign_organisers(organiser_ids)
organiser_ids.reject!(&:empty?)
grant_organiser_access(organiser_ids)
revoke_organiser_access(organiser_ids)
diff --git a/app/controllers/admin/meetings_controller.rb b/app/controllers/admin/meetings_controller.rb
index 17e8eff5c..5d66687ac 100644
--- a/app/controllers/admin/meetings_controller.rb
+++ b/app/controllers/admin/meetings_controller.rb
@@ -9,8 +9,8 @@ def create
@meeting = Meeting.new(meeting_params)
if @meeting.save
- set_organisers(organiser_ids)
- set_chapters(chapter_ids)
+ assign_organisers(organiser_ids)
+ assign_chapters(chapter_ids)
redirect_to [:admin, @meeting], notice: t('admin.messages.meeting.created')
else
flash[:notice] = @meeting.errors.full_messages.join(', ')
@@ -27,8 +27,8 @@ def show
def edit; end
def update
- set_organisers(organiser_ids)
- set_chapters(chapter_ids)
+ assign_organisers(organiser_ids)
+ assign_chapters(chapter_ids)
if @meeting.update(meeting_params)
redirect_to [:admin, @meeting], notice: t('admin.messages.meeting.updated')
@@ -86,13 +86,13 @@ def revoke_organiser_access(organiser_ids)
end
end
- def set_organisers(organiser_ids)
+ def assign_organisers(organiser_ids)
organiser_ids.reject!(&:empty?)
grant_organiser_access(organiser_ids)
revoke_organiser_access(organiser_ids)
end
- def set_chapters(chapter_ids)
+ def assign_chapters(chapter_ids)
chapter_ids.reject!(&:empty?)
@meeting.chapters = chapter_ids.map { |id| Chapter.find(id) }
end
diff --git a/app/controllers/admin/workshops_controller.rb b/app/controllers/admin/workshops_controller.rb
index c72a01633..d92f353b0 100644
--- a/app/controllers/admin/workshops_controller.rb
+++ b/app/controllers/admin/workshops_controller.rb
@@ -24,7 +24,7 @@ def create
if workshop_type_valid? && @workshop.save
grant_organiser_access(@workshop.chapter.organisers.pluck(:id))
- set_host(host_id)
+ assign_host(host_id)
redirect_to admin_workshop_path(@workshop), notice: I18n.t('admin.messages.workshop.created')
else
@@ -144,7 +144,7 @@ def workshop_id
params.permit(:workshop_id)[:workshop_id]
end
- def set_host(host_id)
+ def assign_host(host_id)
return unless host_id
host = @workshop.workshop_sponsors.find_or_initialize_by(sponsor_id: host_id)
@@ -154,7 +154,7 @@ def set_host(host_id)
host.update(host: true)
end
- def set_organisers(organiser_ids)
+ def assign_organisers(organiser_ids)
organiser_ids.reject!(&:empty?)
grant_organiser_access(organiser_ids)
revoke_organiser_access(organiser_ids)
@@ -199,7 +199,7 @@ def workshop_type_valid?
end
def update_workshop_details
- set_organisers(organiser_ids)
- set_host(host_id)
+ assign_organisers(organiser_ids)
+ assign_host(host_id)
end
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 59bc8881c..dbbb5b83b 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -169,7 +169,8 @@ def chapters
end
def redirect_back(fallback_location:, **args)
- if referer = request.headers['Referer']
+ referer = request.headers['Referer']
+ if referer
redirect_to referer, **args
else
redirect_to fallback_location, **args
diff --git a/app/controllers/feedback_controller.rb b/app/controllers/feedback_controller.rb
index 15988ad8f..e9992a82b 100644
--- a/app/controllers/feedback_controller.rb
+++ b/app/controllers/feedback_controller.rb
@@ -9,7 +9,7 @@ def show
return redirect_to root_path
end
- set_coaches(feedback_request.workshop)
+ assign_coaches(feedback_request.workshop)
@workshop = feedback_request.workshop
@feedback = Feedback.new
@@ -22,7 +22,7 @@ def submit
redirect_to root_path
else
feedback_request = FeedbackRequest.find_by!(token: params[:id], submited: false)
- set_coaches(feedback_request.workshop)
+ assign_coaches(feedback_request.workshop)
@workshop = feedback_request.workshop
@feedback = Feedback.new(feedback_params)
@@ -39,7 +39,7 @@ def feedback_params
params.expect(feedback: %i[coach_id tutorial_id request rating suggestions])
end
- def set_coaches(workshop)
+ def assign_coaches(workshop)
@coaches = workshop.invitations.to_coaches.accepted_or_attended
.order(Arel.sql('attended DESC NULLS LAST'))
.map(&:member)
diff --git a/app/controllers/workshop_invitation_controller.rb b/app/controllers/workshop_invitation_controller.rb
index a1f784de1..1e9312558 100644
--- a/app/controllers/workshop_invitation_controller.rb
+++ b/app/controllers/workshop_invitation_controller.rb
@@ -29,7 +29,7 @@ def accept
return back_with_message(t('messages.already_rsvped')) if @invitation.attending?
return back_with_message(t('messages.invitations.closed')) unless workshop.rsvp_available?
- if user.has_existing_RSVP_on(workshop.date_and_time)
+ if user.existing_rsvp_on?(workshop.date_and_time)
return back_with_message(t('messages.invitations.rsvped_to_other_workshop'))
end
diff --git a/app/models/feedback.rb b/app/models/feedback.rb
index d2cd52693..dd99f6bce 100644
--- a/app/models/feedback.rb
+++ b/app/models/feedback.rb
@@ -8,7 +8,8 @@ class Feedback < ApplicationRecord
validates :tutorial, presence: true
def self.submit_feedback(params, token)
- return false unless feedback_request = FeedbackRequest.find_by(token: token)
+ feedback_request = FeedbackRequest.find_by(token: token)
+ return false unless feedback_request
feedback = Feedback.new(params)
feedback.workshop = feedback_request.workshop
diff --git a/app/models/member.rb b/app/models/member.rb
index 72fa23e44..860a39bc7 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -126,7 +126,7 @@ def requires_additional_details?
can_log_in? && !valid?
end
- def has_existing_RSVP_on(date)
+ def existing_rsvp_on?(date)
invitations_on(date).any?
end
diff --git a/lib/tasks/setup.rake b/lib/tasks/setup.rake
index fd887a223..db71f5898 100644
--- a/lib/tasks/setup.rake
+++ b/lib/tasks/setup.rake
@@ -1,231 +1,231 @@
# frozen_string_literal: true
-namespace :setup do
- desc 'Inspect your local development environment and report what is missing or misconfigured'
- task check: :environment do
- checker = SetupChecker.new
- checker.run
- exit(checker.all_ok? ? 0 : 1)
+class SetupChecker
+ CHECKS = %i[
+ check_ruby_version
+ check_bundler
+ check_mise
+ check_postgresql
+ check_imagemagick
+ check_github_credentials
+ check_bundle
+ check_database_connection
+ check_database_exists
+ check_database_migrated
+ check_test_database
+ ].freeze
+
+ def initialize
+ @results = []
+ @expected_ruby = File.read('.ruby-version').strip
end
- class SetupChecker
- CHECKS = %i[
- check_ruby_version
- check_bundler
- check_mise
- check_postgresql
- check_imagemagick
- check_github_credentials
- check_bundle
- check_database_connection
- check_database_exists
- check_database_migrated
- check_test_database
- ].freeze
-
- def initialize
- @results = []
- @expected_ruby = File.read('.ruby-version').strip
- end
+ def run
+ puts
+ puts '== codebar planner setup check =='
+ puts
+ puts 'This command inspects your local development environment and reports'
+ puts 'anything that needs attention before you can run the application.'
+ puts
- def run
- puts
- puts '== codebar planner setup check =='
- puts
- puts 'This command inspects your local development environment and reports'
- puts 'anything that needs attention before you can run the application.'
- puts
+ CHECKS.each { |check| send(check) }
- CHECKS.each { |check| send(check) }
+ print_summary
+ end
- print_summary
- end
+ def all_ok?
+ @results.none? { |r| r[:status] == :error }
+ end
- def all_ok?
- @results.none? { |r| r[:status] == :error }
- end
+ private
- private
+ def ok(title, message)
+ @results << { status: :ok, title:, message: }
+ puts " ✅ #{title}: #{message}"
+ end
- def ok(title, message)
- @results << { status: :ok, title:, message: }
- puts " ✅ #{title}: #{message}"
- end
+ def warn(title, message, fix)
+ @results << { status: :warn, title:, message:, fix: }
+ puts " ⚠️ #{title}: #{message}"
+ puts " → #{fix}"
+ end
- def warn(title, message, fix)
- @results << { status: :warn, title:, message:, fix: }
- puts " ⚠️ #{title}: #{message}"
- puts " → #{fix}"
- end
+ def error(title, message, fix)
+ @results << { status: :error, title:, message:, fix: }
+ puts " ❌ #{title}: #{message}"
+ puts " → #{fix}"
+ end
- def error(title, message, fix)
- @results << { status: :error, title:, message:, fix: }
- puts " ❌ #{title}: #{message}"
- puts " → #{fix}"
+ def check_ruby_version
+ current = RUBY_VERSION
+ if current == @expected_ruby
+ ok('Ruby version', "#{current} (matches .ruby-version)")
+ else
+ error('Ruby version', "#{current} (expected #{@expected_ruby})",
+ "Install Ruby #{@expected_ruby}. See docs/development-setup.md for options.")
end
+ end
- def check_ruby_version
- current = RUBY_VERSION
- if current == @expected_ruby
- ok('Ruby version', "#{current} (matches .ruby-version)")
- else
- error('Ruby version', "#{current} (expected #{@expected_ruby})",
- "Install Ruby #{@expected_ruby}. See docs/development-setup.md for options.")
- end
+ def check_bundler
+ if system('which bundle > /dev/null 2>&1')
+ ok('Bundler', 'installed')
+ else
+ error('Bundler', 'not found',
+ 'Run: gem install bundler')
end
+ end
- def check_bundler
- if system('which bundle > /dev/null 2>&1')
- ok('Bundler', 'installed')
- else
- error('Bundler', 'not found',
- 'Run: gem install bundler')
- end
+ def check_mise
+ if system('which mise > /dev/null 2>&1')
+ ok('mise', 'installed')
+ else
+ warn('mise', 'not found',
+ 'Optional but recommended. Install: brew install mise (macOS) or see https://mise.jdx.dev')
end
+ end
- def check_mise
- if system('which mise > /dev/null 2>&1')
- ok('mise', 'installed')
- else
- warn('mise', 'not found',
- 'Optional but recommended. Install: brew install mise (macOS) or see https://mise.jdx.dev')
- end
+ def check_postgresql
+ unless system('which psql > /dev/null 2>&1')
+ error('PostgreSQL', 'psql not found',
+ 'Install: brew install postgresql && brew services start postgresql (macOS)' \
+ ' or see docs/development-setup.md')
+ return
+ end
+
+ # Check if postgres is accepting connections
+ db_config = ActiveRecord::Base.connection_db_config.configuration_hash
+ host = db_config[:host] || 'localhost'
+ port = db_config[:port] || 5432
+ user = db_config[:username] || ENV['USER']
+ password = db_config[:password]
+
+ env = password ? { 'PGPASSWORD' => password } : {}
+ cmd = "PGPASSWORD=#{password} psql -h #{host} -p #{port} -U #{user} -c 'SELECT 1' > /dev/null 2>&1"
+ if system(env, cmd)
+ ok('PostgreSQL', "running and accepting connections on #{host}:#{port}")
+ else
+ error('PostgreSQL', "not accepting connections on #{host}:#{port}",
+ 'Start PostgreSQL: brew services start postgresql (macOS).' \
+ ' Check credentials in config/database.yml or env vars.')
end
+ end
- def check_postgresql
- unless system('which psql > /dev/null 2>&1')
- error('PostgreSQL', 'psql not found',
- 'Install: brew install postgresql && brew services start postgresql (macOS)' \
- ' or see docs/development-setup.md')
- return
- end
-
- # Check if postgres is accepting connections
- db_config = ActiveRecord::Base.connection_db_config.configuration_hash
- host = db_config[:host] || 'localhost'
- port = db_config[:port] || 5432
- user = db_config[:username] || ENV['USER']
- password = db_config[:password]
-
- env = password ? { 'PGPASSWORD' => password } : {}
- cmd = "PGPASSWORD=#{password} psql -h #{host} -p #{port} -U #{user} -c 'SELECT 1' > /dev/null 2>&1"
- if system(env, cmd)
- ok('PostgreSQL', "running and accepting connections on #{host}:#{port}")
- else
- error('PostgreSQL', "not accepting connections on #{host}:#{port}",
- 'Start PostgreSQL: brew services start postgresql (macOS).' \
- ' Check credentials in config/database.yml or env vars.')
- end
+ def check_imagemagick
+ if system('which convert > /dev/null 2>&1')
+ ok('ImageMagick', 'installed')
+ else
+ error('ImageMagick', 'not found (convert command missing)',
+ 'Install: brew install imagemagick (macOS) or see docs/development-setup.md')
end
+ end
- def check_imagemagick
- if system('which convert > /dev/null 2>&1')
- ok('ImageMagick', 'installed')
- else
- error('ImageMagick', 'not found (convert command missing)',
- 'Install: brew install imagemagick (macOS) or see docs/development-setup.md')
- end
+ def check_github_credentials
+ unless File.exist?('mise.local.toml')
+ error('GitHub OAuth', 'mise.local.toml not found',
+ 'Copy: cp mise.local.toml.example mise.local.toml, then edit with your GitHub app credentials.')
+ return
end
- def check_github_credentials
- unless File.exist?('mise.local.toml')
- error('GitHub OAuth', 'mise.local.toml not found',
- 'Copy: cp mise.local.toml.example mise.local.toml, then edit with your GitHub app credentials.')
- return
- end
+ content = File.read('mise.local.toml')
+ if content.include?('your_github_oauth') || content.include?('your_') || content.include?('fill_in')
+ error('GitHub OAuth', 'mise.local.toml contains placeholder values',
+ 'Edit mise.local.toml with real GITHUB_KEY and GITHUB_SECRET from your GitHub OAuth app.')
+ else
+ ok('GitHub OAuth', 'mise.local.toml configured')
+ end
+ end
- content = File.read('mise.local.toml')
- if content.include?('your_github_oauth') || content.include?('your_') || content.include?('fill_in')
- error('GitHub OAuth', 'mise.local.toml contains placeholder values',
- 'Edit mise.local.toml with real GITHUB_KEY and GITHUB_SECRET from your GitHub OAuth app.')
- else
- ok('GitHub OAuth', 'mise.local.toml configured')
- end
+ def check_bundle
+ if system('bundle check > /dev/null 2>&1')
+ ok('Ruby dependencies', 'all gems installed')
+ else
+ error('Ruby dependencies', 'missing gems',
+ 'Run: bundle install')
end
+ end
+
+ def check_database_connection
+ ActiveRecord::Base.connection.execute('SELECT 1')
+ ok('Database connection', 'can connect to PostgreSQL')
+ rescue StandardError => e
+ error('Database connection', "cannot connect (#{e.class}: #{e.message})",
+ 'Check PostgreSQL is running and credentials in config/database.yml are correct.')
+ end
- def check_bundle
- if system('bundle check > /dev/null 2>&1')
- ok('Ruby dependencies', 'all gems installed')
+ def check_database_exists
+ ActiveRecord::Base.connection.execute('SELECT 1')
+ if ActiveRecord::Base.connection.execute("SELECT 1 FROM pg_database WHERE datname = 'planner_development'").any?
+ ok('Development database', 'planner_development exists')
else
- error('Ruby dependencies', 'missing gems',
- 'Run: bundle install')
+ error('Development database', 'planner_development does not exist',
+ 'Run: bundle exec rake db:create')
end
- end
-
- def check_database_connection
- ActiveRecord::Base.connection.execute('SELECT 1')
- ok('Database connection', 'can connect to PostgreSQL')
- rescue StandardError => e
- error('Database connection', "cannot connect (#{e.class}: #{e.message})",
- 'Check PostgreSQL is running and credentials in config/database.yml are correct.')
- end
+ rescue StandardError => e
+ error('Development database', "cannot check (#{e.class}: #{e.message})",
+ 'Run: bundle exec rake db:create')
+ end
- def check_database_exists
- ActiveRecord::Base.connection.execute('SELECT 1')
- if ActiveRecord::Base.connection.execute("SELECT 1 FROM pg_database WHERE datname = 'planner_development'").any?
- ok('Development database', 'planner_development exists')
+ def check_database_migrated
+ if ActiveRecord::Base.connection.execute('SELECT 1 FROM schema_migrations LIMIT 1')
+ latest = ActiveRecord::Base.connection.execute(
+ 'SELECT version FROM schema_migrations ORDER BY version DESC LIMIT 1'
+ ).first
+ if latest
+ ok('Database migrations', "up to date (latest: #{latest['version']})")
else
- error('Development database', 'planner_development does not exist',
- 'Run: bundle exec rake db:create')
+ error('Database migrations', 'no migrations found',
+ 'Run: bundle exec rake db:migrate')
end
- rescue StandardError => e
- error('Development database', "cannot check (#{e.class}: #{e.message})",
- 'Run: bundle exec rake db:create')
- end
+ end
+ rescue StandardError => e
+ error('Database migrations', "cannot check (#{e.class}: #{e.message})",
+ 'Run: bundle exec rake db:migrate')
+ end
- def check_database_migrated
- if ActiveRecord::Base.connection.execute('SELECT 1 FROM schema_migrations LIMIT 1')
- latest = ActiveRecord::Base.connection.execute(
- 'SELECT version FROM schema_migrations ORDER BY version DESC LIMIT 1'
- ).first
- if latest
- ok('Database migrations', "up to date (latest: #{latest['version']})")
- else
- error('Database migrations', 'no migrations found',
- 'Run: bundle exec rake db:migrate')
- end
- end
- rescue StandardError => e
- error('Database migrations', "cannot check (#{e.class}: #{e.message})",
- 'Run: bundle exec rake db:migrate')
+ def check_test_database
+ db_config = Rails.configuration.database_configuration['test']
+ host = db_config['host'] || 'localhost'
+ port = db_config['port'] || 5432
+ user = db_config['username'] || ENV['USER']
+ password = db_config['password']
+ database = db_config['database']
+
+ env = password ? { 'PGPASSWORD' => password } : {}
+ cmd = "PGPASSWORD=#{password} psql -h #{host} -p #{port} -U #{user} -c 'SELECT 1' #{database} > /dev/null 2>&1"
+ if system(env, cmd)
+ ok('Test database', "#{database} exists and is accessible")
+ else
+ error('Test database', "#{database} missing or not accessible",
+ 'Run: bundle exec rake db:test:prepare')
end
+ end
- def check_test_database
- db_config = Rails.configuration.database_configuration['test']
- host = db_config['host'] || 'localhost'
- port = db_config['port'] || 5432
- user = db_config['username'] || ENV['USER']
- password = db_config['password']
- database = db_config['database']
-
- env = password ? { 'PGPASSWORD' => password } : {}
- cmd = "PGPASSWORD=#{password} psql -h #{host} -p #{port} -U #{user} -c 'SELECT 1' #{database} > /dev/null 2>&1"
- if system(env, cmd)
- ok('Test database', "#{database} exists and is accessible")
- else
- error('Test database', "#{database} missing or not accessible",
- 'Run: bundle exec rake db:test:prepare')
- end
- end
+ def print_summary
+ puts
+ errors = @results.count { |r| r[:status] == :error }
+ warnings = @results.count { |r| r[:status] == :warn }
- def print_summary
+ if errors.zero? && warnings.zero?
+ puts '✅ All checks passed! Your environment is ready for development.'
puts
- errors = @results.count { |r| r[:status] == :error }
- warnings = @results.count { |r| r[:status] == :warn }
-
- if errors.zero? && warnings.zero?
- puts '✅ All checks passed! Your environment is ready for development.'
- puts
- puts 'Next steps:'
- puts ' bundle exec rails server # Start the app'
- puts ' bundle exec rspec # Run the test suite'
- puts ' bundle exec rake db:seed # (Optional) Add sample data'
- elsif errors.zero?
- puts "⚠️ #{warnings} warning(s). You can probably start developing, but review the warnings above."
- else
- puts "❌ #{errors} error(s) and #{warnings} warning(s) need attention before you can start developing."
- end
- puts
- end
+ puts 'Next steps:'
+ puts ' bundle exec rails server # Start the app'
+ puts ' bundle exec rspec # Run the test suite'
+ puts ' bundle exec rake db:seed # (Optional) Add sample data'
+ elsif errors.zero?
+ puts "⚠️ #{warnings} warning(s). You can probably start developing, but review the warnings above."
+ else
+ puts "❌ #{errors} error(s) and #{warnings} warning(s) need attention before you can start developing."
+ end
+ puts
+ end
+end
+
+namespace :setup do
+ desc 'Inspect your local development environment and report what is missing or misconfigured'
+ task check: :environment do
+ checker = SetupChecker.new
+ checker.run
+ exit(checker.all_ok? ? 0 : 1)
end
end
diff --git a/spec/components/chapters_sidebar_component_spec.rb b/spec/components/chapters_sidebar_component_spec.rb
index 2ac4a5e95..73bb65e5a 100644
--- a/spec/components/chapters_sidebar_component_spec.rb
+++ b/spec/components/chapters_sidebar_component_spec.rb
@@ -18,7 +18,7 @@
it 'renders nothing when no chapters' do
render_inline described_class.new(chapters: [])
- expect(page).to have_css('ul.list-unstyled.ms-0', visible: true)
+ expect(page).to have_css('ul.list-unstyled.ms-0', visible: :visible)
expect(page).to have_no_css('li')
end
end
diff --git a/spec/controllers/member/details_controller_spec.rb b/spec/controllers/member/details_controller_spec.rb
index 10b98a639..bb06347f6 100644
--- a/spec/controllers/member/details_controller_spec.rb
+++ b/spec/controllers/member/details_controller_spec.rb
@@ -1,10 +1,12 @@
RSpec.describe Member::DetailsController do
render_views
let(:member) { Fabricate(:member) }
+ let(:mailing_list) { instance_double(Services::MailingList) }
before do
allow(controller).to receive(:current_user).and_return(member)
- allow_any_instance_of(Services::MailingList).to receive(:subscribe).and_return(true)
+ allow(Services::MailingList).to receive(:new).and_return(mailing_list)
+ allow(mailing_list).to receive_messages(subscribe: true, unsubscribe: true)
end
describe 'PATCH #update' do
diff --git a/spec/features/admin/event_spec.rb b/spec/features/admin/event_spec.rb
index 2c0587a03..5e317d647 100644
--- a/spec/features/admin/event_spec.rb
+++ b/spec/features/admin/event_spec.rb
@@ -2,6 +2,21 @@
let(:member) { Fabricate(:member) }
let(:chapter) { Fabricate(:chapter) }
+ def fill_in_mandatory_event_fields(name:, slug:, description:, date:, sponsor:)
+ fill_in 'Event Name', with: name
+ fill_in 'Slug', with: slug
+ fill_in 'Date', with: date
+ fill_in 'Starts at', with: '16:00'
+ fill_in 'Ends at', with: '18:00'
+ fill_in 'Description', with: description
+ fill_in 'RSVP instructions', with: 'Some instructions'
+ fill_in 'Schedule', with: '9:00 Sign up & breakfast
9:30 kick off'
+ fill_in 'Coach spaces', with: '19'
+ fill_in 'Student spaces', with: '25'
+ select sponsor.name, from: 'Venue'
+ click_on 'Save'
+ end
+
describe 'an authorised member' do
before do
member.add_role(:organiser, chapter)
@@ -14,31 +29,22 @@
date = Time.zone.today + 2.days
visit new_admin_event_path
- fill_in 'Event Name', with: 'A test event'
- fill_in 'Slug', with: 'a-test-event'
- fill_in 'Date', with: date
- fill_in 'Starts at', with: '16:00'
- fill_in 'Ends at', with: '18:00'
- fill_in 'Description', with: 'A test event description'
- fill_in 'RSVP instructions', with: 'Some instructions'
- fill_in 'Schedule', with: '9:00 Sign up & breakfast
9:30 kick off'
- fill_in 'Coach spaces', with: '19'
- fill_in 'Student spaces', with: '25'
- select sponsor.name, from: 'Venue'
- click_on 'Save'
-
- expect(page).to have_text('Event successfully created')
+ fill_in_mandatory_event_fields(name: 'A test event', slug: 'a-test-event',
+ description: 'A test event description', date: date, sponsor: sponsor)
- expect(page).to have_text('A test event')
- expect(page).to have_text(humanize_date(date))
- expect(page).to have_text('A test event description')
- expect(page).to have_text('25 student spots, 19 coach spots')
- expect(page).to have_text('9:00 Sign up & breakfast 9:30 kick off')
+ aggregate_failures do
+ expect(page).to have_text('Event successfully created')
+ expect(page).to have_text('A test event')
+ expect(page).to have_text(humanize_date(date))
+ expect(page).to have_text('A test event description')
+ expect(page).to have_text('25 student spots, 19 coach spots')
+ expect(page).to have_text('9:00 Sign up & breakfast 9:30 kick off')
- within '#host' do
- expect(page).to have_text sponsor.name
- expect(page).to have_text sponsor.address.street
- expect(page).to have_text sponsor.address.city
+ within '#host' do
+ expect(page).to have_text sponsor.name
+ expect(page).to have_text sponsor.address.street
+ expect(page).to have_text sponsor.address.city
+ end
end
end
end
diff --git a/spec/features/admin/sponsor_spec.rb b/spec/features/admin/sponsor_spec.rb
index 7f5d3fd35..8ddcaa028 100644
--- a/spec/features/admin/sponsor_spec.rb
+++ b/spec/features/admin/sponsor_spec.rb
@@ -93,16 +93,18 @@
end
scenario 'displays all sponsor details' do
- expect(page).to have_text(sponsor.name)
- expect(page).to have_text(sponsor.description)
- expect(page).to have_link(sponsor.website, href: sponsor.website)
- expect(page).to have_text(sponsor.level)
- expect(page).to have_text(ContactPresenter.new(sponsor.contacts.first).full_name)
-
- expect(page).to have_text(sponsor.seats)
- expect(page).to have_text(sponsor.coach_spots)
- expect(page).to have_text(sponsor.accessibility_info)
- expect(page).to have_text(sponsor.address.street)
+ aggregate_failures do
+ expect(page).to have_text(sponsor.name)
+ expect(page).to have_text(sponsor.description)
+ expect(page).to have_link(sponsor.website, href: sponsor.website)
+ expect(page).to have_text(sponsor.level)
+ expect(page).to have_text(ContactPresenter.new(sponsor.contacts.first).full_name)
+
+ expect(page).to have_text(sponsor.seats)
+ expect(page).to have_text(sponsor.coach_spots)
+ expect(page).to have_text(sponsor.accessibility_info)
+ expect(page).to have_text(sponsor.address.street)
+ end
end
context 'with sponsorships' do
diff --git a/spec/features/member_portal_spec.rb b/spec/features/member_portal_spec.rb
index a775e74bd..b22a652f0 100644
--- a/spec/features/member_portal_spec.rb
+++ b/spec/features/member_portal_spec.rb
@@ -86,6 +86,7 @@
it 'can not access the member portal' do
visit dashboard_path
+ expect(page).to have_current_path(root_path)
expect(page).to have_no_css('#profile')
end
@@ -93,6 +94,7 @@
mock_github_auth
visit profile_path
+ expect(page).to have_current_path(terms_and_conditions_path)
expect(page).to have_no_css('#member_profile')
end
end
diff --git a/spec/lib/omniauth/strategies/codebar_spec.rb b/spec/lib/omniauth/strategies/codebar_spec.rb
index 21ddfaad9..44d78e563 100644
--- a/spec/lib/omniauth/strategies/codebar_spec.rb
+++ b/spec/lib/omniauth/strategies/codebar_spec.rb
@@ -47,18 +47,20 @@ def build_env(path, query: '', session: {})
env = build_env('/auth/codebar')
status, headers, _body = strategy.call(env)
- expect(status).to eq(302)
- expect(env['rack.session']['omniauth.codebar.state']).to be_present
- expect(env['rack.session']['omniauth.codebar.code_verifier']).to be_present
-
- location = headers['Location']
- expect(location).to include('/api/auth/oauth2/authorize')
- expect(location).to include('client_id=planner')
- expect(location).to include('response_type=code')
- expect(location).to include('code_challenge=')
- expect(location).to include('code_challenge_method=S256')
- expect(location).to include('scope=openid+profile+email')
- expect(env['rack.session']['omniauth.codebar.redirect_uri']).to eq('http://localhost:3000/auth/codebar/callback')
+ aggregate_failures do
+ expect(status).to eq(302)
+ expect(env['rack.session']['omniauth.codebar.state']).to be_present
+ expect(env['rack.session']['omniauth.codebar.code_verifier']).to be_present
+
+ location = headers['Location']
+ expect(location).to include('/api/auth/oauth2/authorize')
+ expect(location).to include('client_id=planner')
+ expect(location).to include('response_type=code')
+ expect(location).to include('code_challenge=')
+ expect(location).to include('code_challenge_method=S256')
+ expect(location).to include('scope=openid+profile+email')
+ expect(env['rack.session']['omniauth.codebar.redirect_uri']).to eq('http://localhost:3000/auth/codebar/callback')
+ end
end
it 'generates unique state per request' do
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 781922936..add457799 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -38,7 +38,7 @@
member_old = Fabricate(:member, groups: [group])
member_new = Fabricate(:member, groups: [group])
- _member_no_rsvp = Fabricate(:member, groups: [group])
+ member_no_rsvp = Fabricate(:member, groups: [group])
Fabricate(:workshop_invitation, workshop: old_workshop, member: member_old, attending: true)
Fabricate(:workshop_invitation, workshop: new_workshop, member: member_new, attending: true)
@@ -46,7 +46,7 @@
results = described_class.members_by_recent_rsvp(group).to_a
expect(results.first).to eq(member_new)
- expect(results.last).to eq(_member_no_rsvp)
+ expect(results.last).to eq(member_no_rsvp)
end
end
end
diff --git a/spec/support/shared_examples/behaves_like_an_invitation_route.rb b/spec/support/shared_examples/behaves_like_an_invitation_route.rb
index 6ddde0f07..5f77ad930 100644
--- a/spec/support/shared_examples/behaves_like_an_invitation_route.rb
+++ b/spec/support/shared_examples/behaves_like_an_invitation_route.rb
@@ -34,8 +34,8 @@
invitation.update(role: 'Student', attending: nil, tutorial: nil)
visit accept_invitation_route
- expect(page).to have_no_link 'I can no longer attend'
expect(page).to have_text('Tutorial must be selected')
+ expect(page).to have_no_link 'I can no longer attend'
end
scenario 'a Coach must can RSVP diredctly' do
diff --git a/spec/support/shared_examples/behaves_link_member_viewing_workshop.rb b/spec/support/shared_examples/behaves_link_member_viewing_workshop.rb
index 90c1cd3ab..1422dda6c 100644
--- a/spec/support/shared_examples/behaves_link_member_viewing_workshop.rb
+++ b/spec/support/shared_examples/behaves_link_member_viewing_workshop.rb
@@ -15,6 +15,7 @@
login(banned_member)
visit workshop_path(workshop)
+ expect(page).to have_css('h1')
expect(page).to have_no_button("Attend as a #{member_type.downcase}")
end
end