From 5bf0aa6745db27c45c0778f9f6e9046f9ee9fb94 Mon Sep 17 00:00:00 2001 From: eileencodes Date: Sun, 6 Nov 2016 18:55:15 -0500 Subject: Turn system testing into it's own gem and rename Renames `Rails::SystemTestCase` to `ActionSystemTest` and moves it to a gem under the Rails name. We need to name the class `ActionSystemTestCase` because the gem expects a module but tests themselves expect a class. Adds MIT-LICENSE, CHANGELOG, and README for the future. --- actionsystemtest/lib/action_system_test.rb | 59 +++++++++++++ .../lib/action_system_test/driver_adapter.rb | 31 +++++++ .../lib/action_system_test/driver_adapters.rb | 46 ++++++++++ .../driver_adapters/capybara_driver.rb | 83 ++++++++++++++++++ .../driver_adapters/rails_selenium_driver.rb | 98 ++++++++++++++++++++++ .../driver_adapters/web_server.rb | 45 ++++++++++ .../lib/action_system_test/gem_version.rb | 15 ++++ actionsystemtest/lib/action_system_test/railtie.rb | 17 ++++ .../lib/action_system_test/test_helper.rb | 23 +++++ .../lib/action_system_test/test_helpers.rb | 9 ++ .../action_system_test/test_helpers/assertions.rb | 40 +++++++++ .../action_system_test/test_helpers/form_helper.rb | 58 +++++++++++++ .../test_helpers/screenshot_helper.rb | 47 +++++++++++ actionsystemtest/lib/action_system_test/version.rb | 8 ++ 14 files changed, 579 insertions(+) create mode 100644 actionsystemtest/lib/action_system_test.rb create mode 100644 actionsystemtest/lib/action_system_test/driver_adapter.rb create mode 100644 actionsystemtest/lib/action_system_test/driver_adapters.rb create mode 100644 actionsystemtest/lib/action_system_test/driver_adapters/capybara_driver.rb create mode 100644 actionsystemtest/lib/action_system_test/driver_adapters/rails_selenium_driver.rb create mode 100644 actionsystemtest/lib/action_system_test/driver_adapters/web_server.rb create mode 100644 actionsystemtest/lib/action_system_test/gem_version.rb create mode 100644 actionsystemtest/lib/action_system_test/railtie.rb create mode 100644 actionsystemtest/lib/action_system_test/test_helper.rb create mode 100644 actionsystemtest/lib/action_system_test/test_helpers.rb create mode 100644 actionsystemtest/lib/action_system_test/test_helpers/assertions.rb create mode 100644 actionsystemtest/lib/action_system_test/test_helpers/form_helper.rb create mode 100644 actionsystemtest/lib/action_system_test/test_helpers/screenshot_helper.rb create mode 100644 actionsystemtest/lib/action_system_test/version.rb (limited to 'actionsystemtest/lib') diff --git a/actionsystemtest/lib/action_system_test.rb b/actionsystemtest/lib/action_system_test.rb new file mode 100644 index 0000000000..4a80e34695 --- /dev/null +++ b/actionsystemtest/lib/action_system_test.rb @@ -0,0 +1,59 @@ +require "action_system_test/test_helper" +require "action_system_test/driver_adapter" + +# System tests are similar to Integration tests in that they incorporate multiple +# controllers and actions, but can be used to simulate a real user experience. +# System tests are also known as Acceptance tests. +# +# To create a System Test in your application extend your test class from +# ActionSystemTestCase. System tests use Capybara as a base and +# allows you to configure the driver. The default driver is +# RailsSeleniumDriver which provides Capybara with no-setup +# configuration of the Selenium Driver. If you prefer you can use the bare +# Selenium driver and set your own configuration. +# +# A system test looks like the following: +# +# require 'test_helper' +# +# class Users::CreateTest < ActionSystemTestCase +# def adding_a_new_user +# visit users_path +# click_on 'New User' +# +# fill_in 'Name', with: 'Arya' +# click_on 'Create User' +# +# assert_text 'Arya' +# end +# end +# +# System test driver can be configured in your Rails configuration file for the +# test environment. +# +# config.system_testing.driver = :rails_selenium_driver +# +# You can also specify a driver by initializing a new driver object. This allows +# you to change the default settings for the driver you're setting. +# +# config.system_testing.driver = ActionSystemTest::DriverAdapters::RailsSeleniumDriver.new( +# browser: :firefox +# ) +# +# A list of supported adapters can be found in DriverAdapters. +# +# If you want to use one of the default drivers provided by Capybara you can +# set the driver in your config to one of those defaults: +:rack_test+, +# +:selenium+, +:webkit+, or +:poltergeist+. These 4 drivers use Capyara's +# driver defaults whereas the RailsSeleniumDriver has pre-set +# configuration for browser, server, port, etc. +module ActionSystemTest + include ActionSystemTest::TestHelper + include ActionSystemTest::DriverAdapter + + ActiveSupport.run_load_hooks(:system_testing, self) +end + +class ActionSystemTestCase < ActionDispatch::IntegrationTest + include ActionSystemTest +end diff --git a/actionsystemtest/lib/action_system_test/driver_adapter.rb b/actionsystemtest/lib/action_system_test/driver_adapter.rb new file mode 100644 index 0000000000..fe12ecaf80 --- /dev/null +++ b/actionsystemtest/lib/action_system_test/driver_adapter.rb @@ -0,0 +1,31 @@ +require "action_system_test/driver_adapters" + +module ActionSystemTest + # The ActionSystemTest::DriverAdapter module is used to load the driver + # set in your Rails' test configuration file. + # + # The default driver adapter is the +:rails_selenium_driver+. + module DriverAdapter + extend ActiveSupport::Concern + + module ClassMethods + def default_driver # :nodoc + :rails_selenium_driver + end + + # Returns the current driver that is set. If no driver is set in the + # Rails' configuration file then +:rails_selenium_driver+ will be + # initialized. + def driver + @driver ||= DriverAdapters.lookup(default_driver) + end + + # Specify the adapter and settings for the system test driver set in the + # Rails' configuration file. + def driver=(driver) + @driver = DriverAdapters.lookup(driver) + @driver.call + end + end + end +end diff --git a/actionsystemtest/lib/action_system_test/driver_adapters.rb b/actionsystemtest/lib/action_system_test/driver_adapters.rb new file mode 100644 index 0000000000..aead9d6f60 --- /dev/null +++ b/actionsystemtest/lib/action_system_test/driver_adapters.rb @@ -0,0 +1,46 @@ +module ActionSystemTest + # == System Testing Driver Adapters + # + # By default Rails supports Capybara with the Selenium Driver. Rails provides + # configuration setup for using the selenium driver with Capybara. + # Additionally Rails can be used as a layer between Capybara and its other + # supported drivers: +:rack_test+, +:selenium+, +:webkit+, or +:poltergeist+. + # + # *{RackTest}[https://github.com/jnicklas/capybara#racktest] + # *{Selenium}[http://seleniumhq.org/docs/01_introducing_selenium.html#selenium-2-aka-selenium-webdriver] + # *{Webkit}[https://github.com/thoughtbot/capybara-webkit] + # *{Poltergeist}[https://github.com/teampoltergeist/poltergeist] + # + # === Driver Features + # + # | | Default Browser | Supports Screenshots? | + # |-----------------|-----------------------|-----------------------| + # | Rails' Selenium | Chrome | Yes | + # | Rack Test | No JS Support | No | + # | Selenium | Firefox | Yes | + # | WebKit | Headless w/ Qt | Yes | + # | Poltergeist | Headless w/ PhantomJS | Yes | + module DriverAdapters + extend ActiveSupport::Autoload + + autoload :CapybaraDriver + autoload :RailsSeleniumDriver + + class << self + # Returns driver for specified name. + # + # ActionSystemTest::DriverAdapters.lookup(:rails_selenium_driver) + # # => ActionSystemTest::DriverAdapters::RailsSeleniumDriver + def lookup(driver) + if CapybaraDriver::CAPYBARA_DEFAULTS.include?(driver) + CapybaraDriver.new(name: driver) + elsif driver.is_a?(Symbol) + klass = const_get(driver.to_s.camelize) + klass.new + else + driver + end + end + end + end +end diff --git a/actionsystemtest/lib/action_system_test/driver_adapters/capybara_driver.rb b/actionsystemtest/lib/action_system_test/driver_adapters/capybara_driver.rb new file mode 100644 index 0000000000..5362f6db25 --- /dev/null +++ b/actionsystemtest/lib/action_system_test/driver_adapters/capybara_driver.rb @@ -0,0 +1,83 @@ +require "action_system_test/driver_adapters/web_server" + +module ActionSystemTest + module DriverAdapters + # == CapybaraDriver for System Testing + # + # The CapybaraDriver is a shim that sits between Rails and + # Capybara. + # + # The drivers Capybara supports are: +:rack_test+, +:selenium+, +:webkit+, + # and +:poltergeist+. + # + # Rails provides its own defaults for Capybara with the Selenium driver + # through RailsSeleniumDriver, but allows users to use Selenium + # directly. + # + # To set your system tests to use one of Capybara's default drivers, add + # the following to yur Rails' configuration test environment: + # + # config.system_testing.driver = :rack_test + # + # The +:rack_test+ driver is a basic test driver that doesn't support + # JavaScript testing and doesn't require a server. + # + # The +:poltergeist+ and +:webkit+ drivers are headless, but require some + # extra environment setup. Because the default server for Rails is Puma, each + # of the Capybara drivers will default to using Puma. Changing the configuration + # to use Webrick is possible by initalizing a new driver object. + # + # The default settings for the CapybaraDriver are: + # + # # + # + # The settings for the CapybaraDriver can be changed from + # Rails' configuration file. + # + # config.system_testing.driver = ActionSystemTest::DriverAdapters::CapybaraDriver.new( + # name: :webkit, + # server: :webrick + # ) + class CapybaraDriver + include WebServer + + CAPYBARA_DEFAULTS = [ :rack_test, :selenium, :webkit, :poltergeist ] + + attr_reader :name, :server, :port + + def initialize(name: :rack_test, server: :puma, port: 28100) + @name = name + @server = server + @port = port + end + + def call + registration + setup + end + + def supports_screenshots? + @name != :rack_test + end + + private + def registration + register_server + end + + def setup + set_server + set_port + set_driver + end + + def set_driver + Capybara.default_driver = @name + end + end + end +end diff --git a/actionsystemtest/lib/action_system_test/driver_adapters/rails_selenium_driver.rb b/actionsystemtest/lib/action_system_test/driver_adapters/rails_selenium_driver.rb new file mode 100644 index 0000000000..16e485398a --- /dev/null +++ b/actionsystemtest/lib/action_system_test/driver_adapters/rails_selenium_driver.rb @@ -0,0 +1,98 @@ +require "action_system_test/driver_adapters/web_server" + +module ActionSystemTest + module DriverAdapters + # == RailsSeleniumDriver for System Testing + # + # The RailsSeleniumDriver uses the Selenium 2.0 webdriver. The + # selenium-webdriver gem is required by this driver. + # + # The RailsSeleniumDriver is useful for real browser testing and + # supports Chrome and Firefox. + # + # By default Rails system testing will use Rails' configuration with Capybara + # and the Selenium driver. To explictly set the RailsSeleniumDriver + # add the following to your Rails' configuration test environment: + # + # config.system_testing.driver = :rails_selenium_driver + # + # Because this driver supports real browser testing it is required that a + # server is configured. + # + # If no server is specified when the driver is initialized, Puma will be used + # by default. The default settings for the RailsSeleniumDriver + # are as follows: + # + # # + # + # The settings for the RailsSeleniumDriver can be changed in the + # Rails configuration file. + # + # config.system_testing.driver = ActionSystemTest::DriverAdapters::RailsSeleniumDriver.new( + # server: :webrick, + # port: 28100, + # screen_size: [ 800, 800 ] + # ) + # + # The default browser is set to Chrome because the current version of + # Firefox does not work with selenium-webdriver. If you want to use Firefox, + # you will need to use Firefox 45.0esr or 47.0 and ensure + # that selenium-webdriver is version 2.53.4. To change the browser from + # +:chrome+ to +:firefox+, initialize the Selenium driver in your Rails' + # test environment: + # + # config.system_testing.driver = ActionSystemTest::DriverAdapters::RailsSeleniumDriver.new( + # browser: :firefox + # ) + class RailsSeleniumDriver + include WebServer + + attr_reader :browser, :server, :port, :screen_size + + def initialize(browser: :chrome, server: :puma, port: 28100, screen_size: [ 1400, 1400 ]) # :nodoc: + @browser = browser + @server = server + @port = port + @screen_size = screen_size + end + + def call # :nodoc: + registration + setup + end + + def supports_screenshots? + true + end + + private + def registration + register_browser_driver + register_server + end + + def setup + set_server + set_port + set_driver + end + + def register_browser_driver + Capybara.register_driver @browser do |app| + Capybara::Selenium::Driver.new(app, browser: @browser).tap do |driver| + driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size) + end + end + end + + def set_driver + Capybara.default_driver = @browser.to_sym + end + end + end +end diff --git a/actionsystemtest/lib/action_system_test/driver_adapters/web_server.rb b/actionsystemtest/lib/action_system_test/driver_adapters/web_server.rb new file mode 100644 index 0000000000..e115fe583c --- /dev/null +++ b/actionsystemtest/lib/action_system_test/driver_adapters/web_server.rb @@ -0,0 +1,45 @@ +begin + require "rack/handler/puma" +rescue LoadError + false +end + +module ActionSystemTest + module DriverAdapters + module WebServer # :nodoc: + def register_server + Capybara.register_server @server do |app, port, host| + case @server + when :puma + register_puma(app, port) + when :webrick + register_webrick(app, port) + else + register_default(app, port) + end + end + end + + private + def register_default(app, port) + Capybara.run_default_server(app, port) + end + + def register_puma(app, port) + Rack::Handler::Puma.run(app, Port: port, Threads: "0:4") + end + + def register_webrick(app, port) + Rack::Handler::WEBrick.run(app, Port: port) + end + + def set_server + Capybara.server = @server + end + + def set_port + Capybara.server_port = @port + end + end + end +end diff --git a/actionsystemtest/lib/action_system_test/gem_version.rb b/actionsystemtest/lib/action_system_test/gem_version.rb new file mode 100644 index 0000000000..c10fce2da0 --- /dev/null +++ b/actionsystemtest/lib/action_system_test/gem_version.rb @@ -0,0 +1,15 @@ +module ActionSystemTest + # Returns the version of the currently loaded Action System Test as a Gem::Version. + def self.gem_version + Gem::Version.new VERSION::STRING + end + + module VERSION + MAJOR = 5 + MINOR = 1 + TINY = 0 + PRE = "alpha" + + STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") + end +end diff --git a/actionsystemtest/lib/action_system_test/railtie.rb b/actionsystemtest/lib/action_system_test/railtie.rb new file mode 100644 index 0000000000..6f4fe1b993 --- /dev/null +++ b/actionsystemtest/lib/action_system_test/railtie.rb @@ -0,0 +1,17 @@ +require "action_system_test" + +module ActionSystemTest + # = System Testing Railtie + class Railtie < Rails::Railtie # :nodoc: + config.system_testing = ActiveSupport::OrderedOptions.new + + initializer "system_testing.set_configs" do |app| + options = app.config.system_testing + options.driver ||= ActionSystemTest.default_driver + + ActiveSupport.on_load(:system_testing) do + options.each { |k,v| send("#{k}=", v) } + end + end + end +end diff --git a/actionsystemtest/lib/action_system_test/test_helper.rb b/actionsystemtest/lib/action_system_test/test_helper.rb new file mode 100644 index 0000000000..428f0faeca --- /dev/null +++ b/actionsystemtest/lib/action_system_test/test_helper.rb @@ -0,0 +1,23 @@ +require "capybara/dsl" +require "action_system_test/test_helpers" + +module ActionSystemTest + module TestHelper # :nodoc: + include TestHelpers::Assertions + include TestHelpers::FormHelper + include TestHelpers::ScreenshotHelper + include Capybara::DSL + + Capybara.app = Rack::Builder.new do + map "/" do + run Rails.application + end + end + + def after_teardown + take_screenshot if supported? + Capybara.reset_sessions! + super + end + end +end diff --git a/actionsystemtest/lib/action_system_test/test_helpers.rb b/actionsystemtest/lib/action_system_test/test_helpers.rb new file mode 100644 index 0000000000..10ddeff92f --- /dev/null +++ b/actionsystemtest/lib/action_system_test/test_helpers.rb @@ -0,0 +1,9 @@ +module ActionSystemTest + module TestHelpers + extend ActiveSupport::Autoload + + autoload :Assertions + autoload :FormHelper + autoload :ScreenshotHelper + end +end diff --git a/actionsystemtest/lib/action_system_test/test_helpers/assertions.rb b/actionsystemtest/lib/action_system_test/test_helpers/assertions.rb new file mode 100644 index 0000000000..edc8dbaa7f --- /dev/null +++ b/actionsystemtest/lib/action_system_test/test_helpers/assertions.rb @@ -0,0 +1,40 @@ +module ActionSystemTest + module TestHelpers + # Assertions for system testing that aren't included by default in Capybara. + # These are assertions that are useful specifically for Rails applications. + module Assertions + # Asserts that all of the provided selectors are present on the given page. + # + # assert_all_of_selectors('p', 'td') + def assert_all_of_selectors(*items) + options = items.extract_options! + type = type_for_selector(items) + + items.each do |item| + assert_selector type, item, options + end + end + + # Asserts that none of the provided selectors are present on the page. + # + # assert_none_of_selectors('ul', 'ol') + def assert_none_of_selectors(*items) + options = items.extract_options! + type = type_for_selector(items) + + items.each do |item| + assert_no_selector type, item, options + end + end + + private + def type_for_selector(*items) + if items.first.is_a?(Symbol) + items.shift + else + Capybara.default_selector + end + end + end + end +end diff --git a/actionsystemtest/lib/action_system_test/test_helpers/form_helper.rb b/actionsystemtest/lib/action_system_test/test_helpers/form_helper.rb new file mode 100644 index 0000000000..3c65c23f6d --- /dev/null +++ b/actionsystemtest/lib/action_system_test/test_helpers/form_helper.rb @@ -0,0 +1,58 @@ +module ActionSystemTest + module TestHelpers + # Form helpers for system testing that aren't included by default in + # Capybara. + module FormHelper + # Finds all provided fields or text areas and fills in with supplied values. + # + # fill_in_all_fields('Name' => 'Eileen', 'Job Title' => 'Programmer') + def fill_in_all_fields(fields) + fields.each do |name, value| + fill_in name, with: value + end + end + + # Locates a checkbox that is present inside a label and checks it. When + # using styled boxes Selenium may not be able to see the checkbox. This + # form helper looks inside the checkbox and clicks the label instead of + # setting the value of the checkbox. + # + # click_checkbox_label 'Admin' + # + # By default +click_checkbox_label+ looks for checkboxes that are not + # checked by default. To locate an already checked box and uncheck it + # set checked to true: + # + # click_checkbox_label 'Admin', checked: true + def click_checkbox_label(name, checked: false) + field = find_checkbox(name, checked) + label = find_label_wrapper(field) + label.click + end + + # In lieu of locating a button and calling +click_on+, +press_enter+ will + # submit the form via enter. This method will only work for drivers that + # load a browser like Selenium. + # + # test 'Adding a User' do + # fill_in 'Name', with: 'Arya' + # + # press_enter + # + # assert_text 'Arya' + # end + def press_enter + page.driver.browser.action.send_keys(:enter).perform + end + + private + def find_checkbox(name, checked) + find(:field, name, visible: :all, checked: checked) + end + + def find_label_wrapper(field, location: "./ancestor::label") + field.find :xpath, location + end + end + end +end diff --git a/actionsystemtest/lib/action_system_test/test_helpers/screenshot_helper.rb b/actionsystemtest/lib/action_system_test/test_helpers/screenshot_helper.rb new file mode 100644 index 0000000000..37336e976f --- /dev/null +++ b/actionsystemtest/lib/action_system_test/test_helpers/screenshot_helper.rb @@ -0,0 +1,47 @@ +module ActionSystemTest + module TestHelpers + # Screenshot helper for system testing + module ScreenshotHelper + # Takes a screenshot of the current page in the browser if the system + # test driver supports screenshots and the test failed. + # + # Additionally +take_screenshot+ can be used within your tests at points + # you want to take a screenshot if the driver supports screenshots. The + # Rack Test driver does not support screenshots. + # + # You can check of the driver supports screenshots by running + # + # ActionSystemTest.driver.supports_screenshots? + # => true + def take_screenshot + puts "[Screenshot]: #{image_path}" + puts find_image + end + + private + def supported? + ActionSystemTest.driver.supports_screenshots? && !passed? + end + + def image_path + path = "tmp/screenshots/failures_#{method_name}.png" + page.save_screenshot(Rails.root.join(path)) + path + end + + def find_image + if ENV["CAPYBARA_INLINE_SCREENSHOT"] == "artifact" + "\e]1338;url=artifact://#{image_path}\a" + else + name = inline_base64(File.basename(image_path)) + image = inline_base64(File.read(image_path)) + "\e]1337;File=name=#{name};height=400px;inline=1:#{image}\a" + end + end + + def inline_base64(path) + Base64.encode64(path).gsub("\n", "") + end + end + end +end diff --git a/actionsystemtest/lib/action_system_test/version.rb b/actionsystemtest/lib/action_system_test/version.rb new file mode 100644 index 0000000000..a0fda7d9da --- /dev/null +++ b/actionsystemtest/lib/action_system_test/version.rb @@ -0,0 +1,8 @@ +require_relative "gem_version" + +module ActionSystemTest + # Returns the version of the currently loaded Action System Test as a Gem::Version + def self.version + gem_version + end +end -- cgit v1.2.3