From 15e1ac5f9d5ec90c6e0e4bc394a919a8bb36c73f Mon Sep 17 00:00:00 2001 From: Erin Osher <4386583+erinosher@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:02:25 +0000 Subject: [PATCH 1/9] Upgrade dependencies, replace airbrake with sentry, and add dotenv --- .ruby-version | 2 +- Gemfile | 13 ++- Gemfile.lock | 242 ++++++++++++++++++++++++++------------------ config.ru | 9 ++ no_light_sinatra.rb | 11 +- 5 files changed, 165 insertions(+), 112 deletions(-) diff --git a/.ruby-version b/.ruby-version index 338a5b5..8a4b275 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.6.6 +3.1.6 \ No newline at end of file diff --git a/Gemfile b/Gemfile index b2890ba..b950793 100644 --- a/Gemfile +++ b/Gemfile @@ -1,13 +1,17 @@ -ruby '2.6.6' +ruby '3.1.6' source 'https://rubygems.org' -gem 'airbrake' -gem 'mongo_mapper', '0.15.0' +gem 'mongo_mapper', '0.16.0' gem 'sinatra' gem 'sinatra-contrib' gem 'zippy' gem 'omniauth' -gem 'omniauth-mlh', '0.4.1' +gem 'omniauth-mlh', '~> 4.1' +gem 'ostruct' +gem 'puma' +gem "stackprof" +gem "sentry-ruby" + group :development do gem 'sinatra-reloader' @@ -16,6 +20,7 @@ end group :development, :test do gem 'pry' gem 'rake' + gem 'dotenv' end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 5505ef5..98d1554 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,152 +1,200 @@ GEM remote: https://rubygems.org/ specs: - activemodel (6.1.2) - activesupport (= 6.1.2) - activemodel-serializers-xml (1.0.2) - activemodel (> 5.x) - activesupport (> 5.x) + activemodel (7.2.2) + activesupport (= 7.2.2) + activemodel-serializers-xml (1.0.3) + activemodel (>= 5.0.0.a) + activesupport (>= 5.0.0.a) builder (~> 3.1) - activesupport (6.1.2) - concurrent-ruby (~> 1.0, >= 1.0.2) + activerecord (7.2.2) + activemodel (= 7.2.2) + activesupport (= 7.2.2) + timeout (>= 0.4.0) + activesupport (7.2.2) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - tzinfo (~> 2.0) - zeitwerk (~> 2.3) - addressable (2.7.0) - public_suffix (>= 2.0.2, < 5.0) - airbrake (6.3.0) - airbrake-ruby (~> 2.4) - airbrake-ruby (2.12.0) - bson (4.12.0) - builder (3.2.4) - capybara (2.18.0) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + base64 (0.2.0) + benchmark (0.4.0) + bigdecimal (3.1.8) + bson (5.0.1) + builder (3.3.0) + capybara (3.40.0) addressable + matrix mini_mime (>= 0.1.3) - nokogiri (>= 1.3.3) - rack (>= 1.0.0) - rack-test (>= 0.5.4) - xpath (>= 2.0, < 4.0) - childprocess (0.9.0) - ffi (~> 1.0, >= 1.0.11) + nokogiri (~> 1.11) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (>= 1.5, < 3.0) + xpath (~> 3.2) coderay (1.1.3) - concurrent-ruby (1.1.8) - database_cleaner (1.99.0) - faker (1.9.6) - i18n (>= 0.7) - faraday (1.3.0) - faraday-net_http (~> 1.0) - multipart-post (>= 1.2, < 3) - ruby2_keywords - faraday-net_http (1.0.1) - ffi (1.14.2) - hashie (3.6.0) - i18n (1.8.8) + concurrent-ruby (1.3.4) + connection_pool (2.4.1) + database_cleaner (2.1.0) + database_cleaner-active_record (>= 2, < 3) + database_cleaner-active_record (2.2.0) + activerecord (>= 5.a) + database_cleaner-core (~> 2.0.0) + database_cleaner-core (2.0.1) + dotenv (3.1.4) + drb (2.2.1) + faker (3.5.1) + i18n (>= 1.8.11, < 2) + faraday (2.12.1) + faraday-net_http (>= 2.0, < 3.5) + json + logger + faraday-net_http (3.4.0) + net-http (>= 0.5.0) + hashie (5.0.0) + i18n (1.14.6) concurrent-ruby (~> 1.0) - jwt (2.2.2) - method_source (1.0.0) - mini_mime (1.0.2) - mini_portile2 (2.5.0) - minitest (5.14.3) - mongo (2.14.0) - bson (>= 4.8.2, < 5.0.0) - mongo_mapper (0.15.0) + json (2.8.2) + jwt (2.9.3) + base64 + logger (1.6.1) + matrix (0.4.2) + method_source (1.1.0) + mini_mime (1.1.5) + minitest (5.25.1) + mongo (2.21.0) + bson (>= 4.14.1, < 6.0.0) + mongo_mapper (0.16.0) activemodel (>= 5.0) activemodel-serializers-xml (~> 1.0) activesupport (>= 5.0) mongo (~> 2.0) plucky (~> 0.8.0) + rexml multi_json (1.15.0) - multi_xml (0.6.0) - multipart-post (2.1.1) - mustermann (1.1.1) + multi_xml (0.7.1) + bigdecimal (~> 3.1) + mustermann (3.0.3) ruby2_keywords (~> 0.0.1) - nokogiri (1.11.1) - mini_portile2 (~> 2.5.0) + net-http (0.5.0) + uri + nio4r (2.7.4) + nokogiri (1.16.7-arm64-darwin) racc (~> 1.4) - oauth2 (1.4.4) - faraday (>= 0.8, < 2.0) + oauth2 (2.0.9) + faraday (>= 0.17.3, < 3.0) jwt (>= 1.0, < 3.0) - multi_json (~> 1.3) multi_xml (~> 0.5) - rack (>= 1.2, < 3) - omniauth (1.9.1) + rack (>= 1.2, < 4) + snaky_hash (~> 2.0) + version_gem (~> 1.1) + omniauth (2.1.2) hashie (>= 3.4.6) - rack (>= 1.6.2, < 3) - omniauth-mlh (0.4.1) - activesupport - omniauth (~> 1.0) - omniauth-oauth2 (~> 1.3.1) - omniauth-oauth2 (1.3.1) - oauth2 (~> 1.0) - omniauth (~> 1.2) + rack (>= 2.2.3) + rack-protection + omniauth-mlh (4.1.0) + oauth2 (~> 2.0.9) + omniauth (~> 2.1.1) + omniauth-oauth2 (~> 1.8.0) + omniauth-oauth2 (1.8.0) + oauth2 (>= 1.4, < 3) + omniauth (~> 2.0) + ostruct (0.6.1) plucky (0.8.0) mongo (~> 2.0) - pry (0.14.0) + pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) - public_suffix (2.0.5) - racc (1.5.2) - rack (2.2.3) - rack-protection (2.1.0) - rack - rack-test (0.8.3) - rack (>= 1.0, < 3) - rake (12.3.3) - ruby2_keywords (0.0.4) - rubyzip (1.3.0) - selenium-webdriver (3.142.7) - childprocess (>= 0.5, < 4.0) - rubyzip (>= 1.2.2) - sinatra (2.1.0) - mustermann (~> 1.0) - rack (~> 2.2) - rack-protection (= 2.1.0) + public_suffix (6.0.1) + puma (6.4.3) + nio4r (~> 2.0) + racc (1.8.1) + rack (2.2.10) + rack-protection (3.2.0) + base64 (>= 0.1.0) + rack (~> 2.2, >= 2.2.4) + rack-test (2.1.0) + rack (>= 1.3) + rake (13.2.1) + regexp_parser (2.9.2) + rexml (3.3.9) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + securerandom (0.3.1) + selenium-webdriver (4.10.0) + rexml (~> 3.2, >= 3.2.5) + rubyzip (>= 1.2.2, < 3.0) + websocket (~> 1.0) + sentry-ruby (5.21.0) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.0.2) + sinatra (3.2.0) + mustermann (~> 3.0) + rack (~> 2.2, >= 2.2.4) + rack-protection (= 3.2.0) tilt (~> 2.0) - sinatra-contrib (2.1.0) - multi_json - mustermann (~> 1.0) - rack-protection (= 2.1.0) - sinatra (= 2.1.0) + sinatra-contrib (3.2.0) + multi_json (>= 0.0.2) + mustermann (~> 3.0) + rack-protection (= 3.2.0) + sinatra (= 3.2.0) tilt (~> 2.0) sinatra-reloader (1.0) sinatra-contrib - tilt (2.0.10) - tzinfo (2.0.4) + snaky_hash (2.0.1) + hashie + version_gem (~> 1.1, >= 1.1.1) + stackprof (0.2.26) + tilt (2.4.0) + timeout (0.4.2) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) - webdrivers (4.5.0) + uri (1.0.2) + version_gem (1.1.4) + webdrivers (5.3.1) nokogiri (~> 1.6) rubyzip (>= 1.3.0) - selenium-webdriver (>= 3.0, < 4.0) - xpath (2.1.0) - nokogiri (~> 1.3) - zeitwerk (2.4.2) + selenium-webdriver (~> 4.0, < 4.11) + websocket (1.2.11) + xpath (3.2.0) + nokogiri (~> 1.8) zippy (0.2.3) rubyzip (>= 1.0.0) PLATFORMS - ruby + arm64-darwin-24 DEPENDENCIES - airbrake capybara database_cleaner + dotenv faker minitest - mongo_mapper (= 0.15.0) + mongo_mapper (= 0.16.0) omniauth - omniauth-mlh (= 0.4.1) + omniauth-mlh (~> 4.1) + ostruct pry + puma rack-test rake + sentry-ruby sinatra sinatra-contrib sinatra-reloader + stackprof webdrivers zippy RUBY VERSION - ruby 2.6.6p146 + ruby 3.1.6p260 BUNDLED WITH - 1.17.3 + 2.3.27 diff --git a/config.ru b/config.ru index e660bd6..89a59d8 100644 --- a/config.ru +++ b/config.ru @@ -4,6 +4,15 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__)) require "bundler" Bundler.require +require 'sentry-ruby' + +Sentry.init do |config| + config.dsn = ENV["SENTRY_DSN"] + config.traces_sample_rate = 0.1 + config.profiles_sample_rate = 0.1 +end + +use Sentry::Rack::CaptureExceptions unless ENV["RACK_ENV"] == "test" require "find" %w{config/initializers lib models}.each do |load_path| Find.find(load_path) { |f| diff --git a/no_light_sinatra.rb b/no_light_sinatra.rb index 15abb08..09c1d54 100644 --- a/no_light_sinatra.rb +++ b/no_light_sinatra.rb @@ -1,16 +1,7 @@ +require 'dotenv/load' if ENV['RACK_ENV'] == 'development' require_relative 'models/submission' -Airbrake.configure do |c| - c.project_id = 141503 - c.project_key = '94823b6fc825a7bd16f6fc359d0ac501' - c.logger.level = Logger::DEBUG - c.environment = ENV['RACK_ENV'] - c.ignore_environments = %w(test) - c.blacklist_keys = [/password/i] -end - class NoLightSinatra < Sinatra::Base - use Airbrake::Rack::Middleware unless ENV['RACK_ENV'] == 'test' enable :sessions use OmniAuth::Builder do From 5b9f8381165b7a777a96249a0e2b10978967c2fb Mon Sep 17 00:00:00 2001 From: Erin Osher <4386583+erinosher@users.noreply.github.com> Date: Tue, 19 Nov 2024 16:02:35 +0000 Subject: [PATCH 2/9] Update omniauth provider config --- no_light_sinatra.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/no_light_sinatra.rb b/no_light_sinatra.rb index 09c1d54..b915d86 100644 --- a/no_light_sinatra.rb +++ b/no_light_sinatra.rb @@ -5,7 +5,7 @@ class NoLightSinatra < Sinatra::Base enable :sessions use OmniAuth::Builder do - provider :mlh, ENV['MY_MLH_KEY'], ENV['MY_MLH_SECRET'], scope: 'default' + provider :mlh, ENV['MY_MLH_KEY'], ENV['MY_MLH_SECRET'], scope: 'user:read:profile' end set public_folder: 'public', static: true From 4f69e8cdd38184b5da726ae89c6e17154eac0e55 Mon Sep 17 00:00:00 2001 From: Erin Osher <4386583+erinosher@users.noreply.github.com> Date: Tue, 19 Nov 2024 23:22:44 +0000 Subject: [PATCH 3/9] Migrate tests from minitest to rspec --- .rspec | 1 + Gemfile | 4 +- Gemfile.lock | 31 ++++--- Rakefile | 12 ++- spec/acceptance_helper.rb | 27 ------ spec/admin_can_download_submissions_spec.rb | 87 ------------------- .../admin_can_download_submissions_spec.rb | 85 ++++++++++++++++++ .../visitor_can_authorize_with_my_mlh_spec.rb | 64 ++++++++++++++ spec/features/visitor_can_submit_spec.rb | 73 ++++++++++++++++ .../visitor_can_visit_branded_editor_spec.rb | 37 ++++++++ spec/features/visitor_can_visit_home_spec.rb | 12 +++ spec/spec_helper.rb | 70 ++++++++------- .../visitor_can_authorize_with_my_mlh_spec.rb | 67 -------------- spec/visitor_can_submit_spec.rb | 77 ---------------- spec/visitor_can_visit_branded_editor_spec.rb | 40 --------- spec/visitor_can_visit_home_spec.rb | 15 ---- 16 files changed, 338 insertions(+), 364 deletions(-) create mode 100644 .rspec delete mode 100644 spec/acceptance_helper.rb delete mode 100644 spec/admin_can_download_submissions_spec.rb create mode 100644 spec/features/admin_can_download_submissions_spec.rb create mode 100644 spec/features/visitor_can_authorize_with_my_mlh_spec.rb create mode 100644 spec/features/visitor_can_submit_spec.rb create mode 100644 spec/features/visitor_can_visit_branded_editor_spec.rb create mode 100644 spec/features/visitor_can_visit_home_spec.rb delete mode 100644 spec/visitor_can_authorize_with_my_mlh_spec.rb delete mode 100644 spec/visitor_can_submit_spec.rb delete mode 100644 spec/visitor_can_visit_branded_editor_spec.rb delete mode 100644 spec/visitor_can_visit_home_spec.rb diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..c99d2e7 --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--require spec_helper diff --git a/Gemfile b/Gemfile index b950793..f8c3ee5 100644 --- a/Gemfile +++ b/Gemfile @@ -24,10 +24,10 @@ group :development, :test do end group :test do + gem 'rspec' gem 'capybara' gem 'webdrivers' - gem 'database_cleaner' + gem 'database_cleaner-mongo' gem 'faker' - gem 'minitest' gem 'rack-test' end diff --git a/Gemfile.lock b/Gemfile.lock index 98d1554..808032b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,10 +7,6 @@ GEM activemodel (>= 5.0.0.a) activesupport (>= 5.0.0.a) builder (~> 3.1) - activerecord (7.2.2) - activemodel (= 7.2.2) - activesupport (= 7.2.2) - timeout (>= 0.4.0) activesupport (7.2.2) base64 benchmark (>= 0.3) @@ -42,12 +38,11 @@ GEM coderay (1.1.3) concurrent-ruby (1.3.4) connection_pool (2.4.1) - database_cleaner (2.1.0) - database_cleaner-active_record (>= 2, < 3) - database_cleaner-active_record (2.2.0) - activerecord (>= 5.a) - database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) + database_cleaner-mongo (2.0.0) + database_cleaner-core (~> 2.0.0) + mongo (~> 2.0) + diff-lcs (1.5.1) dotenv (3.1.4) drb (2.2.1) faker (3.5.1) @@ -125,6 +120,19 @@ GEM rake (13.2.1) regexp_parser (2.9.2) rexml (3.3.9) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.2) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.1) ruby2_keywords (0.0.5) rubyzip (2.3.2) securerandom (0.3.1) @@ -153,7 +161,6 @@ GEM version_gem (~> 1.1, >= 1.1.1) stackprof (0.2.26) tilt (2.4.0) - timeout (0.4.2) tzinfo (2.0.6) concurrent-ruby (~> 1.0) uri (1.0.2) @@ -173,10 +180,9 @@ PLATFORMS DEPENDENCIES capybara - database_cleaner + database_cleaner-mongo dotenv faker - minitest mongo_mapper (= 0.16.0) omniauth omniauth-mlh (~> 4.1) @@ -185,6 +191,7 @@ DEPENDENCIES puma rack-test rake + rspec sentry-ruby sinatra sinatra-contrib diff --git a/Rakefile b/Rakefile index cf7eb65..690c359 100644 --- a/Rakefile +++ b/Rakefile @@ -1,13 +1,11 @@ require "bundler" Bundler.require -require './no_light_sinatra' - -%w{bundler find rake/testtask}.each { |lib| require lib } +require 'rspec/core/rake_task' task default: :spec -Rake::TestTask.new(:spec) do |t| - t.ruby_opts = ['-W1'] - t.test_files = FileList['spec/*_spec.rb'] -end \ No newline at end of file +RSpec::Core::RakeTask.new(:spec) do |t| + t.pattern = 'spec/**/*_spec.rb' + t.rspec_opts = ['--color', '--format documentation'] +end diff --git a/spec/acceptance_helper.rb b/spec/acceptance_helper.rb deleted file mode 100644 index 2ca5105..0000000 --- a/spec/acceptance_helper.rb +++ /dev/null @@ -1,27 +0,0 @@ -# encoding: utf-8 - -require_relative "spec_helper" -require_relative "../no_light_sinatra.rb" - -require 'capybara' -require 'capybara/dsl' -require 'capybara/webkit' -require 'capybara_minitest_spec' - -Capybara.app = NoLightSinatra -Capybara.default_driver = :webkit - - -class MiniTest::Spec - include Capybara::DSL -end - -class Capybara::Session - def params - Hash[*URI.parse(current_url).query.split(/\?|=|&/)] - end - - def find_classes(selector) - find(selector)["class"].to_s.split(' ').map(&:to_sym) - end -end \ No newline at end of file diff --git a/spec/admin_can_download_submissions_spec.rb b/spec/admin_can_download_submissions_spec.rb deleted file mode 100644 index 05dc755..0000000 --- a/spec/admin_can_download_submissions_spec.rb +++ /dev/null @@ -1,87 +0,0 @@ -require_relative 'spec_helper' -require_relative 'acceptance_helper' - -def app - NoLightSinatra -end - -describe NoLightSinatra do - - before do - mock_with_valid_mlh_credentials! - - 10.times do - create_submission(hackathon: 'hacktheplanet') - end - end - - it 'authenticates the user before they can download the zip' do - get '/logout' - get '/hacktheplanet.zip' - - last_response.status.must_equal(302) - last_response.location.must_include("/auth/mlh") - end - - it 'downloads the ZIP' do - visit '/hacktheplanet.zip' - - page.response_headers['Pragma'].must_equal 'public' - page.response_headers['Expires'].must_equal '0' - page.response_headers['Cache-Control'].must_equal 'public' - page.response_headers['Content-Type'].must_equal 'application/octet-stream' - page.response_headers['Content-Disposition'].must_equal 'attachment; filename="hacktheplanet.zip"' - page.response_headers['Content-Transfer-Encoding'].must_equal 'binary' - end - - it "creates a zip with the correct number of submissions" do - zipfile = visit_and_get_zip_for 'hacktheplanet' - - Zippy.list(zipfile.path).count.must_equal 10 - end - - it "names the files within the zip as expected" do - zipfile = visit_and_get_zip_for 'hacktheplanet' - actual_files = Zippy.list(zipfile.path) - submissions = Submission.by_hackathon('hacktheplanet') - - submissions.each do |submission| - actual_files.must_include submission.filename - end - end - - it "doesn't create zip files if empty" do - visit '/empty_submissions_hackathon.zip' - - page.text.must_have_content 'Error - No Submissions' - page.text.must_have_content 'We did not receive any submissions for your event ("empty_submissions_hackathon").' - end - - private - - def create_submission(submission_params = {}) - submission_params = { - hackathon: 'hacktheplanet', - seconds: Time.now.to_s, - html: "

Hello dear world.

#{rand(1000)}" - }.merge(submission_params) - - visit '/%s' % submission_params[:hackathon] - - page.first('#submission_hackathon', visible: false).set(submission_params[:hackathon]) - page.first('#submission_seconds', visible: false).set(submission_params[:seconds]) - page.first('#submission_html').set(submission_params[:html]) - - page.click_button('!light') - end - - def visit_and_get_zip_for(hackathon) - visit "/#{hackathon}.zip" - - tempfile = Tempfile.new(hackathon) - tempfile.write(page.source) - tempfile.close - - tempfile - end -end diff --git a/spec/features/admin_can_download_submissions_spec.rb b/spec/features/admin_can_download_submissions_spec.rb new file mode 100644 index 0000000..0b5e13a --- /dev/null +++ b/spec/features/admin_can_download_submissions_spec.rb @@ -0,0 +1,85 @@ +require 'spec_helper' + +def app + NoLightSinatra +end + +RSpec.describe 'Admin can download submissions', type: :feature do + before do + mock_with_valid_mlh_credentials! + + 10.times do + create_submission(hackathon: 'hacktheplanet') + end + end + + it 'authenticates the user before they can download the zip' do + get '/logout' + get '/hacktheplanet.zip' + + expect(last_response.status).to eq(302) + expect(last_response.location).to include("/auth/mlh") + end + + it 'downloads the ZIP' do + visit '/hacktheplanet.zip' + + expect(page.response_headers['Pragma']).to eq('public') + expect(page.response_headers['Expires']).to eq('0') + expect(page.response_headers['Cache-Control']).to eq('public') + expect(page.response_headers['Content-Type']).to eq('application/octet-stream') + expect(page.response_headers['Content-Disposition']).to eq('attachment; filename="hacktheplanet.zip"') + expect(page.response_headers['Content-Transfer-Encoding']).to eq('binary') + end + + it "creates a zip with the correct number of submissions" do + zipfile = visit_and_get_zip_for('hacktheplanet') + + expect(Zippy.list(zipfile.path).count).to eq(10) + end + + it "names the files within the zip as expected" do + zipfile = visit_and_get_zip_for('hacktheplanet') + actual_files = Zippy.list(zipfile.path) + submissions = Submission.by_hackathon('hacktheplanet') + + submissions.each do |submission| + expect(actual_files).to include(submission.filename) + end + end + + it "doesn't create zip files if empty" do + visit '/empty_submissions_hackathon.zip' + + expect(page).to have_content('Error - No Submissions') + expect(page).to have_content('We did not receive any submissions for your event ("empty_submissions_hackathon").') + end + + private + + def create_submission(submission_params = {}) + submission_params = { + hackathon: 'hacktheplanet', + seconds: Time.now.to_s, + html: "

Hello dear world.

#{rand(1000)}" + }.merge(submission_params) + + visit "/#{submission_params[:hackathon]}" + + page.first('#submission_hackathon', visible: false).set(submission_params[:hackathon]) + page.first('#submission_seconds', visible: false).set(submission_params[:seconds]) + page.first('#submission_html').set(submission_params[:html]) + + page.click_button('!light') + end + + def visit_and_get_zip_for(hackathon) + visit "/#{hackathon}.zip" + + tempfile = Tempfile.new(hackathon) + tempfile.write(page.source) + tempfile.close + + tempfile + end +end diff --git a/spec/features/visitor_can_authorize_with_my_mlh_spec.rb b/spec/features/visitor_can_authorize_with_my_mlh_spec.rb new file mode 100644 index 0000000..9951df6 --- /dev/null +++ b/spec/features/visitor_can_authorize_with_my_mlh_spec.rb @@ -0,0 +1,64 @@ +require "spec_helper" + +def app + NoLightSinatra +end + +RSpec.describe 'Visitor can authorize with MyMLH', type: :feature do + before do + mock_with_valid_mlh_credentials! + end + + it 'redirects the un-branded editor visitors to MyMLH' do + get '/hacktheplanet' + + expect(last_response.status).to eq(302) + expect(last_response.location).to include("/auth/mlh") + end + + it 'redirects the branded editor visitors to MyMLH' do + get '/hacktheplanet/dell' + + expect(last_response.status).to eq(302) + expect(last_response.location).to include("/auth/mlh") + end + + it 'displays the editor to signed in users' do + sign_in! + + get '/hacktheplanet' + + expect(last_response.status).to eq(200) + expect(last_request.path).to eq("/hacktheplanet") + end + + it 'displays the user\'s name' do + sign_in! + + visit '/hacktheplanet' + + expect(page.find('.current-user span').text).to eq("You are playing !Light as Grace Hopper.") + end + + it 'allows a user to logout' do + sign_in! + visit '/hacktheplanet' + + click_link "Logout" + + expect(page).not_to have_css('.current-user') + end + + it 'redirects authorized users to the previous page' do + visit '/hacktheplanet-test' + + expect(current_path).to eq('/hacktheplanet-test') + end + + private + + def sign_in! + get '/auth/mlh' + follow_redirect! + end +end diff --git a/spec/features/visitor_can_submit_spec.rb b/spec/features/visitor_can_submit_spec.rb new file mode 100644 index 0000000..430af2b --- /dev/null +++ b/spec/features/visitor_can_submit_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' + +RSpec.describe 'Visitor can submit', type: :feature do + let(:app) { NoLightSinatra } + + before do + mock_with_valid_mlh_credentials! + end + + it 'submits an entry' do + visit '/random-hackathon' + fill_in_submission(name: 'Mike Swift') + + expect(page).to have_content 'Submission received' + expect(page).to have_content 'It took you %s minutes' % [last_submission.to_min_secs] + expect(page).to have_content 'You wrote %s lines of code / %s bytes.' % [last_submission.lines, last_submission.bytes] + end + + it 'authenticates the user before they can submit' do + post '/submit' + + expect(last_response.status).to eq(302) + expect(last_response.location).to include("/auth/mlh") + end + + it 'submits a duplicate entry' do + submit_an_entry = lambda { + visit '/random-hackathon' + fill_in_submission + } + + submit_an_entry.call + + expect(page).to have_content 'Submission received' + expect(page).to have_content 'It took you %s minutes' % [last_submission.to_min_secs] + expect(page).to have_content 'You wrote %s lines of code / %s bytes.' % [last_submission.lines, last_submission.bytes] + + # Attempt to re-submit the exact same entry. + submit_an_entry.call + + expect(page).to have_content 'Error - Already Submitted' + expect(page).to have_content 'You have already submitted this code under your name ("Grace Hopper").' + end + + it 'submits a different entry' do + visit '/random-hackathon' + fill_in_submission(name: 'Mike Swift', hackathon: 'treehacks') + + expect(page).to have_content 'Submission received' + expect(page).to have_content 'It took you %s minutes' % [last_submission.to_min_secs] + expect(page).to have_content 'You wrote %s lines of code / %s bytes.' % [last_submission.lines, last_submission.bytes] + end + + private + + def fill_in_submission(submission_params = {}) + submission_params = { + hackathon: 'random-hackathon', + seconds: Time.now.to_s, + html: File.read(File.join('spec', 'testdata', 'entry.html')) + }.merge(submission_params) + + page.first('#submission_hackathon', visible: false).set(submission_params[:hackathon]) + page.first('#submission_seconds', visible: false).set(submission_params[:seconds]) + page.first('#submission_html', visible: true).set(submission_params[:html]) + + page.click_button('!light') + end + + def last_submission + Submission.last + end +end diff --git a/spec/features/visitor_can_visit_branded_editor_spec.rb b/spec/features/visitor_can_visit_branded_editor_spec.rb new file mode 100644 index 0000000..bfb3f79 --- /dev/null +++ b/spec/features/visitor_can_visit_branded_editor_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +def app + NoLightSinatra +end + +RSpec.describe 'Visitor can visit branded editor', type: :feature do + it 'loads the default editor' do + visit_branded_editor(nil) + + expect(page.find_classes('body.editor')).not_to include(:dell) + expect(page.find('#submission_html')['placeholder']).to eq('Enter your code here ...') + expect(page.find('#submit').text).to eq('!light') + end + + it 'loads the dell branded editor' do + visit_branded_editor(:dell) + + expect(page.find_classes('body.editor')).to include(:dell) + expect(page.find('#submission_html')['placeholder']).to eq('Enter your code here ...') + expect(page.find('#submit').text).to eq('!light') + end + + it 'loads the bloomberg editor' do + visit_branded_editor(:bloomberg) + + expect(page.find_classes('body.editor')).to include(:bloomberg) + expect(page.find('#submission_html')['placeholder']).to eq('Enter your code here ...') + expect(page.find('#submit').text).to eq('!light') + end + + private + + def visit_branded_editor(custom_branding) + visit "/hacktheplanet/#{custom_branding}" + end +end diff --git a/spec/features/visitor_can_visit_home_spec.rb b/spec/features/visitor_can_visit_home_spec.rb new file mode 100644 index 0000000..d128a56 --- /dev/null +++ b/spec/features/visitor_can_visit_home_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +RSpec.describe 'Visitor can visit home', type: :feature do + it 'can find NoLightSinatra class' do + expect(NoLightSinatra).to be_a(Class) + end + + it 'loads the homepage' do + visit '/' + expect(page).to have_content('To use, visit http://no-light.mlh.io/[enter hackathon name]') + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d7cfa2b..ea90e5d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,33 +9,42 @@ ENV['RACK_ENV'] = 'test' set :environment, :test -require_relative "../no_light_sinatra.rb" +require_relative '../no_light_sinatra' -require 'webdrivers' -require 'minitest/pride' -require 'minitest/autorun' -require 'minitest/spec' +require 'capybara/rspec' require 'rack/test' -require 'faker' +require 'database_cleaner/core' +require 'database_cleaner/mongo/deletion' -require 'database_cleaner' -DatabaseCleaner[:mongo_mapper].strategy = :truncation +client = Mongo::Client.new('mongodb://127.0.0.1/no_light_test') +cleaner = DatabaseCleaner::Cleaner.new(:mongo, db: client.database) +Capybara.app = NoLightSinatra -require 'find' -%w{./config/initializers ./lib}.each do |load_path| - Find.find(load_path) { |f| require f if f.match(/\.rb$/) } -end -class MiniTest::Spec - include Rack::Test::Methods +RSpec.configure do |config| + config.include Rack::Test::Methods + + config.before(:suite) do + cleaner.strategy = :deletion + cleaner.clean_with(:deletion) + end - before(:each) do - DatabaseCleaner[:mongo_mapper].start + config.before(:each) do + cleaner.start end - after(:each) do - visit '/logout' - DatabaseCleaner[:mongo_mapper].clean + config.after(:each) do + cleaner.clean + end +end + +class Capybara::Session + def params + Hash[*URI.parse(current_url).query.split(/\?|=|&/)] + end + + def find_classes(selector) + find(selector)["class"].to_s.split(' ').map(&:to_sym) end end @@ -43,15 +52,16 @@ class MiniTest::Spec def mock_with_valid_mlh_credentials! OmniAuth.config.mock_auth[:mlh] = OmniAuth::AuthHash.new({ - provider: :mlh, - uid: "1", - info: OmniAuth::AuthHash::InfoHash.new({ - email: 'grace.hopper@mlh.io', - created_at: Time.now, - updated_at: Time.now, - first_name: 'Grace', - last_name: 'Hopper', - scopes: ['default'] - }) - }) + provider: :mlh, + uid: "1", + info: OmniAuth::AuthHash::InfoHash.new({ + email: 'grace.hopper@mlh.io', + created_at: Time.now, + updated_at: Time.now, + first_name: 'Grace', + last_name: 'Hopper', + scopes: ['default'] + }) + }) end + diff --git a/spec/visitor_can_authorize_with_my_mlh_spec.rb b/spec/visitor_can_authorize_with_my_mlh_spec.rb deleted file mode 100644 index 3774d26..0000000 --- a/spec/visitor_can_authorize_with_my_mlh_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -require_relative "spec_helper" -require_relative "acceptance_helper" -require 'pry' - -def app - NoLightSinatra -end - -describe NoLightSinatra do - - before do - mock_with_valid_mlh_credentials! - end - - it 'the un-branded editor redirects visitors to MyMLH' do - get '/hacktheplanet' - - last_response.status.must_equal(302) - last_response.location.must_include("/auth/mlh") - end - - it 'the branded editor redirects visitors to MyMLH' do - get '/hacktheplanet/dell' - - last_response.status.must_equal(302) - last_response.location.must_include("/auth/mlh") - end - - it 'displays the editor to signed in users' do - sign_in! - - get '/hacktheplanet' - - last_response.status.must_equal(200) - last_request.path.must_equal("/hacktheplanet") - end - - it 'displays the users name' do - sign_in! - - visit '/hacktheplanet' - - page.find('.current-user span').text.must_equal "You are playing !Light as Grace Hopper." - end - - it 'allows a user to logout' do - sign_in! - visit '/hacktheplanet' - - click_link "Logout" - - page.has_css?('.current-user').must_equal false - end - - it 'redirects authorized users to the previous page' do - visit '/hacktheplanet-test' - - current_path.must_equal('/hacktheplanet-test') - end - - private - - def sign_in! - get '/auth/mlh' - follow_redirect! - end -end diff --git a/spec/visitor_can_submit_spec.rb b/spec/visitor_can_submit_spec.rb deleted file mode 100644 index 521f04e..0000000 --- a/spec/visitor_can_submit_spec.rb +++ /dev/null @@ -1,77 +0,0 @@ -require_relative 'spec_helper' -require_relative 'acceptance_helper' - -def app - NoLightSinatra -end - -describe NoLightSinatra do - - before do - mock_with_valid_mlh_credentials! - end - - it 'submit an entry' do - visit '/random-hackathon' - fill_in_submission(name: 'Mike Swift') - - page.text.must_have_content 'Submission received' - page.text.must_have_content 'IT TOOK YOU %s MINUTES' % [last_submission.to_min_secs] - page.text.must_have_content 'YOU WROTE %s LINES OF CODE / %s BYTES.' % [last_submission.lines, last_submission.bytes] - end - - it 'authenticates the user before they can submit' do - post '/submit' - - last_response.status.must_equal(302) - last_response.location.must_include("/auth/mlh") - end - - it 'submit a duplicate entry' do - submit_an_entry = lambda { - visit '/random-hackathon' - fill_in_submission - } - - submit_an_entry.call - - page.text.must_have_content 'Submission received' - page.text.must_have_content 'IT TOOK YOU %s MINUTES' % [last_submission.to_min_secs] - page.text.must_have_content 'YOU WROTE %s LINES OF CODE / %s BYTES.' % [last_submission.lines, last_submission.bytes] - - # Attempt to re-submit the exact same entry. - submit_an_entry.call - - page.text.must_have_content 'Error - Already Submitted' - page.text.must_have_content 'You have already submitted this code under your name ("Grace Hopper").' - end - - it 'submit a different entry' do - visit '/random-hackathon' - fill_in_submission(name: 'Mike Swift', hackathon: 'treehacks') - - page.text.must_have_content 'Submission received' - page.text.must_have_content 'IT TOOK YOU %s MINUTES' % [last_submission.to_min_secs] - page.text.must_have_content 'YOU WROTE %s LINES OF CODE / %s BYTES.' % [last_submission.lines, last_submission.bytes] - end - - private - - def fill_in_submission(submission_params = {}) - submission_params = { - hackathon: 'random-hackathon', - seconds: Time.now.to_s, - html: File.read(File.join('spec', 'testdata', 'entry.html')) - }.merge(submission_params) - - page.first('#submission_hackathon', visible: false).set(submission_params[:hackathon]) - page.first('#submission_seconds', visible: false).set(submission_params[:seconds]) - page.first('#submission_html', visible: true).set(submission_params[:html]) - - page.click_button('!light') - end - - def last_submission - Submission.last - end -end \ No newline at end of file diff --git a/spec/visitor_can_visit_branded_editor_spec.rb b/spec/visitor_can_visit_branded_editor_spec.rb deleted file mode 100644 index cbd0c58..0000000 --- a/spec/visitor_can_visit_branded_editor_spec.rb +++ /dev/null @@ -1,40 +0,0 @@ -require_relative "spec_helper" -require_relative "acceptance_helper" - -def app - NoLightSinatra -end - -describe NoLightSinatra do - - it 'loads the default editor' do - visit_branded_editor(nil) - - page.find_classes('body.editor').wont_include :dell - page.find('#submission_html')["placeholder"].must_equal 'Enter your code here ...' - page.find('#submit').text.must_equal '!light' - end - - it 'loads the dell branded editor' do - visit_branded_editor(:dell) - - page.find_classes('body.editor').must_include :dell - page.find('#submission_html')["placeholder"].must_equal 'Enter your code here ...' - page.find('#submit').text.must_equal '!light' - end - - it 'loads the bloomberg editor' do - visit_branded_editor(:bloomberg) - - page.find_classes('body.editor').must_include :bloomberg - page.find('#submission_html')["placeholder"].must_equal 'Enter your code here ...' - page.find('#submit').text.must_equal '!light' - end - - private - - def visit_branded_editor(custom_branding) - visit "/hacktheplanet/%s" % (custom_branding) - end - -end \ No newline at end of file diff --git a/spec/visitor_can_visit_home_spec.rb b/spec/visitor_can_visit_home_spec.rb deleted file mode 100644 index 94f902d..0000000 --- a/spec/visitor_can_visit_home_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require_relative "spec_helper" -require_relative "acceptance_helper" - -describe NoLightSinatra do - - it 'can find NoLightSinatra class' do - NoLightSinatra.must_be_kind_of Class - end - - it 'loads the homepage' do - visit '/' - page.must_have_content 'To use, visit http://no-light.mlh.io/[enter hackathon name]' - end - -end \ No newline at end of file From c89563901cedd2e4eb656a99efd1c3c58b62aae6 Mon Sep 17 00:00:00 2001 From: Erin Osher <4386583+erinosher@users.noreply.github.com> Date: Tue, 19 Nov 2024 23:29:19 +0000 Subject: [PATCH 4/9] Add GitHub Action Workflow --- .github/workflows/ci.yml | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c8852f1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,49 @@ +name: Ruby CI + +on: + push: + branches: + - main + - master + pull_request: + branches: + - main + - master + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + ruby-version: + - 2.7 + - 3.0 + - 3.1 + - 3.2 + mongodb-version: + - 6.0 + - 7.0 + - 8.0 + + env: + SECRET_KEY_BASE: "test_secret" + + steps: + - uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true + + - name: Install dependencies + run: bundle install + + - name: Start MongoDB + uses: supercharge/mongodb-github-action@v1 + with: + mongodb-version: ${{ matrix.mongodb-version }} + + - name: Run tests + run: bundle exec rake spec From 812f464bd69886889c914a28bf5d2c8aa9d10a7c Mon Sep 17 00:00:00 2001 From: Erin Osher <4386583+erinosher@users.noreply.github.com> Date: Tue, 19 Nov 2024 23:32:31 +0000 Subject: [PATCH 5/9] Remove explicit ruby version from gemfile --- Gemfile | 1 - Gemfile.lock | 3 --- 2 files changed, 4 deletions(-) diff --git a/Gemfile b/Gemfile index f8c3ee5..59222e7 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,3 @@ -ruby '3.1.6' source 'https://rubygems.org' gem 'mongo_mapper', '0.16.0' diff --git a/Gemfile.lock b/Gemfile.lock index 808032b..8bb9aa4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -200,8 +200,5 @@ DEPENDENCIES webdrivers zippy -RUBY VERSION - ruby 3.1.6p260 - BUNDLED WITH 2.3.27 From 6adc5a89399b88ab0aabcbc52e2f35d256c42275 Mon Sep 17 00:00:00 2001 From: Erin Osher <4386583+erinosher@users.noreply.github.com> Date: Tue, 19 Nov 2024 23:33:44 +0000 Subject: [PATCH 6/9] add x86 linux platform --- Gemfile.lock | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 8bb9aa4..047a6da 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -83,6 +83,8 @@ GEM nio4r (2.7.4) nokogiri (1.16.7-arm64-darwin) racc (~> 1.4) + nokogiri (1.16.7-x86_64-linux) + racc (~> 1.4) oauth2 (2.0.9) faraday (>= 0.17.3, < 3.0) jwt (>= 1.0, < 3.0) @@ -177,6 +179,7 @@ GEM PLATFORMS arm64-darwin-24 + x86_64-linux DEPENDENCIES capybara From 1b3c39dd00ea9b22814f17e4cec09dbe9041e336 Mon Sep 17 00:00:00 2001 From: Erin Osher <4386583+erinosher@users.noreply.github.com> Date: Tue, 19 Nov 2024 23:33:49 +0000 Subject: [PATCH 7/9] remove circle config --- circle.yml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 circle.yml diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 89e57d7..0000000 --- a/circle.yml +++ /dev/null @@ -1,13 +0,0 @@ -machine: - timezone: "UTC" - ruby: - version: 2.4.0 - services: - - mongodb - environment: - SECRET_KEY_BASE: "test_secret" - -test: - override: - - bundle install - - rake spec \ No newline at end of file From 779e09108ef315f6ff210b6941bff3ceec3dae01 Mon Sep 17 00:00:00 2001 From: Erin Osher <4386583+erinosher@users.noreply.github.com> Date: Tue, 19 Nov 2024 23:35:17 +0000 Subject: [PATCH 8/9] Update matrix --- .github/workflows/ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8852f1..c53f49b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,16 +14,16 @@ jobs: test: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: ruby-version: - - 2.7 - - 3.0 - - 3.1 - - 3.2 + - '3.0' + - '3.1' + - '3.2' mongodb-version: - - 6.0 - - 7.0 - - 8.0 + - '6.0' + - '7.0' + - '8.0' env: SECRET_KEY_BASE: "test_secret" From 80af753b266db55dd18777f78191d1000453d06b Mon Sep 17 00:00:00 2001 From: Erin Osher <4386583+erinosher@users.noreply.github.com> Date: Wed, 20 Nov 2024 00:19:51 +0000 Subject: [PATCH 9/9] Update matrix --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c53f49b..a6b770f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,10 +17,9 @@ jobs: fail-fast: false matrix: ruby-version: - - '3.0' - '3.1' - - '3.2' mongodb-version: + - '5.0' - '6.0' - '7.0' - '8.0'