提交 3dfbe7e4 编写于 作者: E eileencodes

Rewrite API for ActionSystemTest

This is a major rewrite of what existed previously. After discussing
this feature with DHH I realized that I was looking at the setup all
wrong.

I had originally mentally broken it into "what Rails wants" and "what
Capybara already has".

What happened after looking at it from DHH's angle was that I saw there
was no reason to group settings by Driver but instead the following
groups:

- There will always be a `Driver`
  - This can selenium, poltergeist, or capybara webkit. Capybara already
    provides all of these and there's no reason to break them into a
    category of Rails' usese Selenium like this and Capybara uses it
    like that.
- Only Selenium drivers care about `Browser`
  - Because of this it was weird to set it only in the Rails end.
  - Therefore only `Browser`, and not `Driver` cares about
    `screen_size`.
- Puma is the default `Server` in Rails
  - Therefore there's no reason to explictly support Webkit

Once I looked at it from this angle I was able to abstract all the
settings away from grouping the drivers with their options.

Now all the driver, server, and browser settings are abstracted away and
not part of the public facing API.

This means there's no requirement to initialize new classes to change
the default settings and the public options API is much smaller.

All of Rails preferred defaults are still there (selenium with port
21800 using the chrome browser with a screen size of 1400x1400) but
changing these no longer requires initializing a new class or
understanding which driver you're using underneath (rails defaults or
capybaras defaults respectively). Rails opinions are now simple defaults
instead of doing a them versus us setup with Drivers and explicit
options.

Changing the defaults is simple. Call `driven_by` with different
settings to change the defaults which will on their own initialize new
classes and change the default settings.

Use poltergeist with port 3000 for Puma

```
driven_by :poltergeist, on: 3000
```

Use selenium with the Chrome browser and a screen size of 800x800
```
driven_by :selenium, using: :firefox, screen_size: [ 800, 800 ]
```

The entire setup of how browser and drivers interact with each other are
abstracted away and the only required argument is the driver name.
上级 62c7d983
......@@ -50,18 +50,39 @@
# driver defaults whereas the <tt>RailsSeleniumDriver</tt> has pre-set
# configuration for browser, server, port, etc.
require "action_system_test/test_helper"
require "action_system_test/driver_adapter"
require "capybara/dsl"
require "action_controller"
require "action_system_test/driver"
require "action_system_test/browser"
require "action_system_test/server"
require "action_system_test/test_helpers/screenshot_helper"
module ActionSystemTest
include ActionSystemTest::TestHelper
include ActionSystemTest::DriverAdapter
DEFAULT_DRIVER = :rails_selenium_driver
include Capybara::DSL
include ActionSystemTest::TestHelpers::ScreenshotHelper
class Base < ActionDispatch::IntegrationTest
include ActionSystemTest
ActionSystemTest.driver = DEFAULT_DRIVER
def self.start_application # :nodoc:
Capybara.app = Rack::Builder.new do
map "/" do
run Rails.application
end
end
end
def self.driven_by(driver, using: :chrome, on: 21800, screen_size: [1400, 1400])
Driver.new(driver).run
Server.new(on).run
Browser.new(using, screen_size).run if selenium?(driver)
end
def self.selenium?(driver) # :nodoc:
driver == :selenium
end
end
Base.start_application
Base.driven_by :selenium
end
module ActionSystemTest
class Browser
def initialize(name, screen_size)
@name = name
@screen_size = screen_size
end
def run
register
setup
end
private
def register
Capybara.register_driver @name do |app|
Capybara::Selenium::Driver.new(app, browser: @name).tap do |driver|
driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size)
end
end
end
def setup
Capybara.default_driver = @name.to_sym
end
end
end
module ActionSystemTest
class Driver # :nodoc:
def initialize(name)
@name = name
end
def run
register
end
private
def register
Capybara.default_driver = @name
end
end
end
require "action_system_test/driver_adapters"
module ActionSystemTest
# The <tt>ActionSystemTest::DriverAdapter</tt> module is used to load the driver
# set in the +system_test_helper+ file generated with your application.
#
# The default driver adapter is the +:rails_selenium_driver+.
module DriverAdapter
extend ActiveSupport::Concern
module ClassMethods
# Returns the current driver that is set in the <tt>ActionSystemTestCase</tt>
# class generated with your Rails application. If no driver is set
# +: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.
#
# When set, the driver will be initialized.
def driver=(driver)
@driver = DriverAdapters.lookup(driver)
@driver.run
end
end
end
end
module ActionSystemTest
# == Action 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 class 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
require "action_system_test/driver_adapters/web_server"
module ActionSystemTest
module DriverAdapters
# == CapybaraDriver for System Testing
#
# The <tt>CapybaraDriver</tt> is a shim 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 <tt>RailsSeleniumDriver</tt>, but allows users to use Selenium
# directly.
#
# To set your system tests to use one of Capybara's default drivers, add
# the following to your +system_test_helper+ class file:
#
# class ActionSystemTestCase < ActionSystemTest::Base
# ActionSystemTest.driver = :rack_test
# end
#
# 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. Please see their README's for instructions on
# 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 <tt>CapybaraDriver</tt> are:
#
# #<ActionSystemTest::DriverAdapters::CapybaraDriver:0x007ff0e992c1d8
# @name=:rack_test,
# @server=:puma,
# @port=28100
# >
#
# The settings for the <tt>CapybaraDriver</tt> can be changed in the
# +system_test_helper+ file in your application's test directory.
#
# ActionSystemTest.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 run
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
require "action_system_test/driver_adapters/web_server"
module ActionSystemTest
module DriverAdapters
# == RailsSeleniumDriver for Action System Test
#
# The <tt>RailsSeleniumDriver</tt> uses the Selenium 2.0 webdriver. The
# selenium-webdriver gem is required by this driver.
#
# The <tt>RailsSeleniumDriver</tt> 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 <tt>RailsSeleniumDriver</tt>
# add the following to your +system_test_helper+:
#
# class ActionSystemTest < ActionSystemTest::Base
# ActionSystemTest.driver = :rails_selenium_driver
# end
#
# 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 <tt>RailsSeleniumDriver</tt>
# are as follows:
#
# #<ActionSystemTest::DriverAdapters::RailsSeleniumDriver:0x007ff0e992c1d8
# @browser=:chrome,
# @server=:puma,
# @port=28100,
# @screen_size=[ 1400, 1400 ]
# >
#
# The settings for the <tt>RailsSeleniumDriver</tt> can be changed in the
# +system_test_helper+.
#
# class ActionSystemTest < ActionSystemTest::Base
# ActionSystemTest.driver = ActionSystemTest::DriverAdapters::RailsSeleniumDriver.new(
# server: :webrick,
# port: 28100,
# screen_size: [ 800, 800 ]
# )
# end
#
# The default browser is set to Chrome. 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:
#
# class ActionSystemTest < ActionSystemTest::Base
# ActionSystemTest.driver = ActionSystemTest::DriverAdapters::RailsSeleniumDriver.new(
# browser: :firefox
# )
# end
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 run # :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
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:1")
end
def register_webrick(app, port)
Rack::Handler::WEBrick.run(app, Port: port, AccessLog: [], Logger: WEBrick::Log::new(nil, 0))
end
def set_server
Capybara.server = @server
end
def set_port
Capybara.server_port = @port
end
end
end
end
require "rack/handler/puma"
module ActionSystemTest
class Server # :nodoc:
def initialize(port)
@port = port
end
def run
register
setup
end
private
def register
Capybara.register_server :puma do |app, host|
Rack::Handler::Puma.run(app, Port: @port, Threads: "0:1")
end
end
def setup
set_server
set_port
end
def set_server
Capybara.server = :puma
end
def set_port
Capybara.server_port = @port
end
end
end
require "capybara/dsl"
require "action_system_test/test_helpers"
module ActionSystemTest
module TestHelper # :nodoc:
include TestHelpers::ScreenshotHelper
include Capybara::DSL
Capybara.app = Rack::Builder.new do
map "/" do
run Rails.application
end
end
end
end
module ActionSystemTest
module TestHelpers
extend ActiveSupport::Autoload
autoload :ScreenshotHelper
end
end
require "active_support/testing/autorun"
require "action_controller"
require "action_dispatch"
require "action_system_test"
# Set the Rails tests to use the +:rack_test+ driver because
# we're not testing Capybara or it's drivers, but rather that
# the methods accept the proper arguments.
class RoutedRackApp
attr_reader :routes
def initialize(routes, &blk)
@routes = routes
@stack = ActionDispatch::MiddlewareStack.new(&blk).build(@routes)
end
def call(env)
@stack.call(env)
end
end
class ActionSystemTestCase < ActionSystemTest::Base
ActionSystemTest.driver = :rack_test
def self.build_app(routes = nil)
RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new)
end
end
class PostsController < ActionController::Base
def index
render inline: <<HTML
<html>
<body>
<h1>This</h1>
<p title="the title" class="test">Paragraph 1</p>
<p title="the others" class="test">Paragraph 2</p>
</body>
</html>
HTML
end
end
CapybaraRoutes = ActionDispatch::Routing::RouteSet.new
CapybaraRoutes.draw do
resources :posts
end
# Initialize an application
APP = ActionSystemTestCase.build_app(CapybaraRoutes)
# Initialize an application for Capybara
RailsApp = ActionSystemTestCase.new(APP)
# Assign Capybara.app to original Rack Application
Capybara.app = APP
Capybara.add_selector :title_test do
xpath { |name| XPath.css(".test")[XPath.attr(:title).is(name.to_s)] }
end
# Skips the current run on Rubinius using Minitest::Assertions#skip
def rubinius_skip(message = "")
skip message if RUBY_ENGINE == "rbx"
end
# Skips the current run on JRuby using Minitest::Assertions#skip
def jruby_skip(message = "")
skip message if defined?(JRUBY_VERSION)
end
require "active_support/testing/autorun"
require "action_system_test"
class ActionSystemTestTest < ActiveSupport::TestCase
test "driven_by sets Capybara's default driver to poltergeist" do
ActionSystemTest::Base.driven_by :poltergeist
assert_equal :poltergeist, Capybara.default_driver
end
test "driven_by defaults to port 21800" do
ActionSystemTest::Base.driven_by :poltergeist
assert_equal 21800, Capybara.server_port
end
test "driven_by can change Capybara's server port" do
ActionSystemTest::Base.driven_by :selenium, on: 3000
assert_equal 3000, Capybara.server_port
end
test "driven_by sets Capybara's drivers respectively" do
ActionSystemTest::Base.driven_by :selenium, using: :chrome
assert_includes Capybara.drivers, :selenium
assert_includes Capybara.drivers, :chrome
assert_equal :chrome, Capybara.default_driver
end
test "selenium? returns false if driver is poltergeist" do
assert_not ActionSystemTest::Base.selenium?(:poltergeist)
end
end
require "active_support/testing/autorun"
require "action_system_test"
class BrowserTest < ActiveSupport::TestCase
test "initializing the browser" do
browser = ActionSystemTest::Browser.new(:chrome, [ 1400, 1400 ])
assert_equal :chrome, browser.instance_variable_get(:@name)
assert_equal [ 1400, 1400 ], browser.instance_variable_get(:@screen_size)
end
end
require "abstract_unit"
class CapybaraDriverTest < ActiveSupport::TestCase
def setup
ActionSystemTest.driver = :poltergeist
end
def test_default_driver_adapter
assert_kind_of ActionSystemTest::DriverAdapters::CapybaraDriver, ActionSystemTest.driver
end
def test_default_settings
assert_equal :poltergeist, ActionSystemTest.driver.name
assert_equal :puma, ActionSystemTest.driver.server
assert_equal 28100, ActionSystemTest.driver.port
end
def test_setting_driver
ActionSystemTest.driver = :webkit
assert_equal :webkit, ActionSystemTest.driver.name
end
def test_setting_server
ActionSystemTest.driver = ActionSystemTest::DriverAdapters::CapybaraDriver.new(
server: :webrick
)
assert_equal :webrick, ActionSystemTest.driver.server
end
def test_setting_port
ActionSystemTest.driver = ActionSystemTest::DriverAdapters::CapybaraDriver.new(
port: 3000
)
assert_equal 3000, ActionSystemTest.driver.port
end
end
require "abstract_unit"
class DriverAdapterTest < ActiveSupport::TestCase
test "only registered adapters are accepted" do
assert_raises(NameError) do
ActionSystemTest.driver = :whatever
end
assert_nothing_raised do
ActionSystemTest.driver = :rack_test
end
end
end
require "active_support/testing/autorun"
require "action_system_test"
class DriverTest < ActiveSupport::TestCase
test "initializing the driver" do
driver = ActionSystemTest::Driver.new(:selenium)
assert_equal :selenium, driver.instance_variable_get(:@name)
end
end
require "abstract_unit"
class RailsSeleniumDriverTest < ActiveSupport::TestCase
def setup
ActionSystemTest.driver = :rails_selenium_driver
end
def test_default_driver_adapter
assert_kind_of ActionSystemTest::DriverAdapters::RailsSeleniumDriver, ActionSystemTest.driver
end
end
require "abstract_unit"
require "active_support/testing/autorun"
require "action_system_test"
class ScreenshotHelperTest < ActiveSupport::TestCase
def test_driver_support_for_screenshots
ActionSystemTest.driver = :rails_selenium_driver
assert ActionSystemTest.driver.supports_screenshots?
test "image path is saved in tmp directory" do
new_test = ActionSystemTest::Base.new("x")
ActionSystemTest.driver = :rack_test
assert_not ActionSystemTest.driver.supports_screenshots?
ActionSystemTest.driver = :selenium
assert ActionSystemTest.driver.supports_screenshots?
ActionSystemTest.driver = :webkit
assert ActionSystemTest.driver.supports_screenshots?
ActionSystemTest.driver = :poltergeist
assert ActionSystemTest.driver.supports_screenshots?
assert_equal "tmp/screenshots/failures_x.png", new_test.send(:image_path)
end
end
require "active_support/testing/autorun"
require "action_system_test"
class ServerTest < ActiveSupport::TestCase
test "initializing the server port" do
server = ActionSystemTest::Server.new(21800)
assert_equal 21800, server.instance_variable_get(:@port)
end
end
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册