diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 671c5e3f43..ff47fad6ba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -278,8 +278,11 @@ jobs: - name: Run migration tests run: bundle exec rspec --pattern "spec/{migrations}/**/*_spec.rb" + - name: Run system tests + run: bundle exec rspec --profile -- spec/system + - name: Run all other tests - run: bundle exec rake ofn:specs:run:excluding_folders["models,controllers,serializers,features,lib,migrations"] + run: bundle exec rake ofn:specs:run:excluding_folders["models,controllers,serializers,features,lib,migrations,system"] test-the-rest: runs-on: ubuntu-18.04 diff --git a/Gemfile b/Gemfile index 3df788509a..a85c2e0ffe 100644 --- a/Gemfile +++ b/Gemfile @@ -150,6 +150,7 @@ group :test, :development do gem 'timecop' gem 'unicorn-rails' gem 'webdrivers' + gem 'cuprite' end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 1609fdd768..2621bb1616 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -185,6 +185,7 @@ GEM chronic (0.10.2) chunky_png (1.4.0) climate_control (0.2.0) + cliver (0.3.2) cocaine (0.5.8) climate_control (>= 0.0.3, < 1.0) codecov (0.5.2) @@ -222,6 +223,9 @@ GEM crass (1.0.6) css_parser (1.9.0) addressable + cuprite (0.13) + capybara (>= 2.1, < 4) + ferrum (~> 0.11.0) dalli (2.7.11) database_cleaner (2.0.1) database_cleaner-active_record (~> 2.0.0) @@ -269,6 +273,11 @@ GEM faraday-excon (1.1.0) faraday-net_http (1.0.1) faraday-net_http_persistent (1.1.0) + ferrum (0.11) + addressable (~> 2.5) + cliver (~> 0.3) + concurrent-ruby (~> 1.1) + websocket-driver (>= 0.6, < 0.8) ffaker (2.18.0) ffi (1.15.3) flipper (0.20.4) @@ -706,6 +715,7 @@ DEPENDENCIES coffee-rails (~> 5.0.0) combine_pdf compass-rails + cuprite custom_error_message! dalli database_cleaner diff --git a/spec/system/first_system_spec.rb b/spec/system/first_system_spec.rb new file mode 100644 index 0000000000..dd148a253b --- /dev/null +++ b/spec/system/first_system_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require "system_helper" + +describe "Visit Admin", js: true do + include UIComponentHelper + include AuthenticationHelper + include WebHelper + let(:user) { create(:user, password: "password", password_confirmation: "password") } + let!(:enterprise) { create(:enterprise, owner: user) } # Required for access to admin + + scenario "logging into admin redirects home, then back to admin" do + visit spree.admin_dashboard_path + + fill_in "Email", with: user.email + fill_in "Password", with: user.password + + click_login_button + expect(page).to have_content "DASHBOARD" + expect(page).to have_current_path spree.admin_dashboard_path + expect(page).to have_no_content "CONFIGURATION" + end +end diff --git a/spec/system/support/capybara_setup.rb b/spec/system/support/capybara_setup.rb new file mode 100644 index 0000000000..dea6cfa57c --- /dev/null +++ b/spec/system/support/capybara_setup.rb @@ -0,0 +1,24 @@ +# Usually, especially when using Selenium, developers tend to increase the max wait time. +# With Cuprite, there is no need for that. +# We use a Capybara default value here explicitly. +Capybara.default_max_wait_time = 10 + +# Normalize whitespaces when using `has_text?` and similar matchers, +# i.e., ignore newlines, trailing spaces, etc. +# That makes tests less dependent on slightly UI changes. +Capybara.default_normalize_ws = true + +# Where to store system tests artifacts (e.g. screenshots, downloaded files, etc.). +# It could be useful to be able to configure this path from the outside (e.g., on CI). +Capybara.save_path = ENV.fetch("CAPYBARA_ARTIFACTS", "./tmp/capybara") + +Capybara.singleton_class.prepend(Module.new do + attr_accessor :last_used_session + + def using_session(name, &block) + self.last_used_session = name + super + ensure + self.last_used_session = nil + end +end) diff --git a/spec/system/support/cuprite_helpers.rb b/spec/system/support/cuprite_helpers.rb new file mode 100644 index 0000000000..0ec2d66335 --- /dev/null +++ b/spec/system/support/cuprite_helpers.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module CupriteHelpers + # Drop #pause anywhere in a test to stop the execution. + # Useful when you want to checkout the contents of a web page in the middle of a test + # running in a headful mode. + def pause + page.driver.pause + end + + # Drop #debug anywhere in a test to open a Chrome inspector and pause the execution + def debug(*args) + page.driver.debug(*args) + end + + # Use our `Capybara.save_path` to store screenshots with other capybara artifacts + # (Rails screenshots path is not configurable https://github.com/rails/rails/blob/49baf092439fc74fc3377b12e3334c3dd9d0752f/actionpack/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb#L79) + def absolute_image_path + Rails.root.join("#{Capybara.save_path}/screenshots/#{image_name}.png") + end + + # Make failure screenshots compatible with multi-session setup. + # That's where we use Capybara.last_used_session introduced before. + def take_screenshot + return super unless Capybara.last_used_session + + Capybara.using_session(Capybara.last_used_session) { super } + end +end diff --git a/spec/system/support/cuprite_setup.rb b/spec/system/support/cuprite_setup.rb new file mode 100644 index 0000000000..8bc258f0b2 --- /dev/null +++ b/spec/system/support/cuprite_setup.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require "capybara/cuprite" + +Capybara.register_driver(:cuprite) do |app| + Capybara::Cuprite::Driver.new( + app, + **{ + window_size: [1200, 800], + browser_options: {}, + process_timeout: 20, + timeout: 20, + # Don't load scripts from external sources, like google maps or stripe + url_whitelist: ["http://localhost", "http://0.0.0.0", "http://127.0.0.1"], + inspector: true, + headless: true + } + ) +end + +# Configure Capybara to use :cuprite driver by default +Capybara.default_driver = Capybara.javascript_driver = :cuprite + +RSpec.configure do |config| + config.include CupriteHelpers, type: :system + + config.prepend_before(:each, type: :system) { driven_by :cuprite } + + # Make sure url helpers in mailers use the Capybara server host. + config.around(:each, type: :system) do |example| + original_host = Rails.application.default_url_options[:host] + Rails.application.default_url_options[:host] = Capybara.server_host + example.run + Rails.application.default_url_options[:host] = original_host + end +end diff --git a/spec/system/support/precompile_assets.rb b/spec/system/support/precompile_assets.rb new file mode 100644 index 0000000000..519ecb0fca --- /dev/null +++ b/spec/system/support/precompile_assets.rb @@ -0,0 +1,23 @@ +RSpec.configure do |config| + # Skip assets precompilcation if we exclude system specs. + # For example, you can run all non-system tests via the following command: + # + # rspec --tag ~type:system + # + # In this case, we don't need to precompile assets. + next if config.filter.opposite.rules[:type] == "system" || config.exclude_pattern.match?(%r{spec/system}) + + config.before(:suite) do + # We can use webpack-dev-server for tests, too! + # Useful if you working on a frontend code fixes and want to verify them via system tests. + if Webpacker.dev_server.running? + next + else + $stdout.puts "\n Precompiling assets.\n" + + Webpacker.compile + end + end +end + + diff --git a/spec/system_helper.rb b/spec/system_helper.rb new file mode 100644 index 0000000000..354b413b49 --- /dev/null +++ b/spec/system_helper.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +require "rails_helper" + +# system/support/ files contain system tests configurations and helpers +Dir[File.join(__dir__, "system/support/**/*.rb")].sort.each { |file| require file }