mirror of
https://github.com/thoughtbot/shoulda-matchers.git
synced 2022-11-09 12:01:38 -05:00
Add new RSpec acceptance tests to replace Cucumber
This commit is contained in:
parent
f922613386
commit
3fb4cdb3b7
26 changed files with 1229 additions and 1 deletions
7
Rakefile
7
Rakefile
|
@ -15,6 +15,13 @@ RSpec::Core::RakeTask.new('spec:unit') do |t|
|
|||
t.verbose = false
|
||||
end
|
||||
|
||||
RSpec::Core::RakeTask.new('spec:acceptance') do |t|
|
||||
t.ruby_opts = '-w -r ./spec/report_warnings'
|
||||
t.pattern = "spec/acceptance/**/*_spec.rb"
|
||||
t.rspec_opts = '--color --format progress'
|
||||
t.verbose = false
|
||||
end
|
||||
|
||||
Cucumber::Rake::Task.new do |t|
|
||||
options = []
|
||||
|
||||
|
|
20
spec/acceptance/active_model_integration_spec.rb
Normal file
20
spec/acceptance/active_model_integration_spec.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
require 'acceptance_spec_helper'
|
||||
|
||||
describe 'shoulda-matchers integrates with an ActiveModel project' do
|
||||
specify 'and loads without errors' do
|
||||
create_active_model_project
|
||||
|
||||
add_shoulda_matchers_to_project
|
||||
|
||||
write_file 'load_dependencies.rb', <<-FILE
|
||||
require 'active_model'
|
||||
require 'shoulda-matchers'
|
||||
|
||||
puts ActiveModel::VERSION::STRING
|
||||
puts "Loaded all dependencies without errors"
|
||||
FILE
|
||||
|
||||
result = run_command('bundle exec ruby load_dependencies.rb')
|
||||
expect(result).to have_output('Loaded all dependencies without errors')
|
||||
end
|
||||
end
|
56
spec/acceptance/independent_matchers_spec.rb
Normal file
56
spec/acceptance/independent_matchers_spec.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
require 'acceptance_spec_helper'
|
||||
|
||||
describe 'shoulda-matchers has independent matchers' do
|
||||
context 'specifically delegate_method' do
|
||||
specify 'and integrates with a Ruby application that uses Minitest' do
|
||||
create_generic_bundler_project
|
||||
add_minitest_to_project
|
||||
|
||||
write_file 'lib/post_office.rb', <<-FILE
|
||||
class PostOffice
|
||||
end
|
||||
FILE
|
||||
|
||||
write_file 'lib/courier.rb', <<-FILE
|
||||
require 'forwardable'
|
||||
|
||||
class Courier
|
||||
extend Forwardable
|
||||
|
||||
def_delegators :post_office, :deliver
|
||||
|
||||
attr_reader :post_office
|
||||
|
||||
def initialize(post_office)
|
||||
@post_office = post_office
|
||||
end
|
||||
end
|
||||
FILE
|
||||
|
||||
write_minitest_test 'test/courier_test.rb' do |test_case_superclass|
|
||||
<<-FILE
|
||||
require "test_helper"
|
||||
require "courier"
|
||||
require "post_office"
|
||||
|
||||
class CourierTest < #{test_case_superclass}
|
||||
subject { Courier.new(post_office) }
|
||||
|
||||
should delegate_method(:deliver).to(:post_office)
|
||||
|
||||
def post_office
|
||||
PostOffice.new
|
||||
end
|
||||
end
|
||||
FILE
|
||||
end
|
||||
|
||||
result = run_n_unit_tests('test/courier_test.rb')
|
||||
|
||||
expect(result).to indicate_number_of_tests_was_run(1)
|
||||
expect(result).to have_output(
|
||||
'Courier should delegate #deliver to #post_office object'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
139
spec/acceptance/rails_integration_spec.rb
Normal file
139
spec/acceptance/rails_integration_spec.rb
Normal file
|
@ -0,0 +1,139 @@
|
|||
require 'acceptance_spec_helper'
|
||||
|
||||
describe 'shoulda-matchers integrates with Rails' do
|
||||
before do
|
||||
create_rails_application
|
||||
|
||||
write_file 'db/migrate/1_create_users.rb', <<-FILE
|
||||
class CreateUsers < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :users do |t|
|
||||
t.string :name
|
||||
end
|
||||
end
|
||||
end
|
||||
FILE
|
||||
|
||||
run_rake_tasks!('db:migrate', 'db:test:prepare')
|
||||
|
||||
write_file 'app/models/user.rb', <<-FILE
|
||||
class User < ActiveRecord::Base
|
||||
validates_presence_of :name
|
||||
end
|
||||
FILE
|
||||
|
||||
write_file 'app/controllers/examples_controller.rb', <<-FILE
|
||||
class ExamplesController < ApplicationController
|
||||
def show
|
||||
@example = 'hello'
|
||||
render nothing: true
|
||||
end
|
||||
end
|
||||
FILE
|
||||
|
||||
configure_routes_with_single_wildcard_route
|
||||
end
|
||||
|
||||
specify 'in a project that uses Test::Unit' do
|
||||
updating_bundle do
|
||||
add_gems_for_n_unit
|
||||
add_shoulda_matchers_to_project
|
||||
end
|
||||
|
||||
run_tests_for_n_unit
|
||||
end
|
||||
|
||||
specify 'in a project that uses RSpec' do
|
||||
updating_bundle do
|
||||
add_gems_for_rspec
|
||||
add_shoulda_matchers_to_project
|
||||
end
|
||||
|
||||
run_tests_for_rspec
|
||||
end
|
||||
|
||||
specify 'in a project that uses Spring' do
|
||||
unless bundle_includes?('spring')
|
||||
skip "Spring isn't a dependency of this Appraisal"
|
||||
end
|
||||
|
||||
updating_bundle do
|
||||
add_spring_to_project
|
||||
add_gems_for_rspec
|
||||
add_shoulda_matchers_to_project(manually: true)
|
||||
end
|
||||
|
||||
run_command_within_bundle!('spring stop')
|
||||
|
||||
run_tests_for_rspec
|
||||
end
|
||||
|
||||
specify 'in a project that combines both RSpec and Test::Unit' do
|
||||
updating_bundle do
|
||||
add_gems_for_n_unit
|
||||
add_gems_for_rspec
|
||||
add_shoulda_matchers_to_project
|
||||
end
|
||||
|
||||
run_tests_for_n_unit
|
||||
run_tests_for_rspec
|
||||
end
|
||||
|
||||
def add_gems_for_n_unit
|
||||
add_gem 'shoulda-context'
|
||||
end
|
||||
|
||||
def add_gems_for_rspec
|
||||
add_rspec_rails_to_project!
|
||||
end
|
||||
|
||||
def run_tests_for_n_unit
|
||||
write_file 'test/unit/user_test.rb', <<-FILE
|
||||
require 'test_helper'
|
||||
|
||||
class UserTest < ActiveSupport::TestCase
|
||||
should validate_presence_of(:name)
|
||||
end
|
||||
FILE
|
||||
|
||||
write_file 'test/functional/examples_controller_test.rb', <<-FILE
|
||||
require 'test_helper'
|
||||
|
||||
class ExamplesControllerTest < ActionController::TestCase
|
||||
def setup
|
||||
get :show
|
||||
end
|
||||
|
||||
should respond_with(:success)
|
||||
end
|
||||
FILE
|
||||
|
||||
result = run_n_unit_test_suite
|
||||
|
||||
expect(result).to indicate_that_tests_were_run(unit: 1, functional: 1)
|
||||
expect(result).to have_output('User should require name to be set')
|
||||
expect(result).to have_output('should respond with 200')
|
||||
end
|
||||
|
||||
def run_tests_for_rspec
|
||||
add_spec 'spec/models/user_spec.rb', <<-FILE
|
||||
describe User do
|
||||
it { should validate_presence_of(:name) }
|
||||
end
|
||||
FILE
|
||||
|
||||
add_spec 'spec/controllers/examples_controller_spec.rb', <<-FILE
|
||||
describe ExamplesController, "show" do
|
||||
before { get :show }
|
||||
|
||||
it { should respond_with(:success) }
|
||||
end
|
||||
FILE
|
||||
|
||||
result = run_rspec_suite
|
||||
|
||||
expect(result).to have_output('2 examples, 0 failures')
|
||||
expect(result).to have_output('should require name to be set')
|
||||
expect(result).to have_output('should respond with 200')
|
||||
end
|
||||
end
|
21
spec/acceptance_spec_helper.rb
Normal file
21
spec/acceptance_spec_helper.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
require 'rspec/core'
|
||||
|
||||
Dir[ File.join(File.expand_path('../support/acceptance/**/*.rb', __FILE__)) ].sort.each do |file|
|
||||
require file
|
||||
end
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.expect_with :rspec do |c|
|
||||
c.syntax = :expect
|
||||
end
|
||||
|
||||
if config.respond_to?(:infer_spec_type_from_file_location!)
|
||||
config.infer_spec_type_from_file_location!
|
||||
end
|
||||
|
||||
AcceptanceTests::Helpers.configure_example_group(config)
|
||||
|
||||
config.include AcceptanceTests::Matchers
|
||||
end
|
||||
|
||||
$VERBOSE = true
|
33
spec/support/acceptance/helpers.rb
Normal file
33
spec/support/acceptance/helpers.rb
Normal file
|
@ -0,0 +1,33 @@
|
|||
require_relative 'helpers/active_model_helpers'
|
||||
require_relative 'helpers/base_helpers'
|
||||
require_relative 'helpers/command_helpers'
|
||||
require_relative 'helpers/gem_helpers'
|
||||
require_relative 'helpers/rails_version_helpers'
|
||||
require_relative 'helpers/rspec_helpers'
|
||||
require_relative 'helpers/ruby_version_helpers'
|
||||
require_relative 'helpers/step_helpers'
|
||||
|
||||
module AcceptanceTests
|
||||
module Helpers
|
||||
def self.configure_example_group(example_group)
|
||||
example_group.include(self)
|
||||
|
||||
example_group.before do
|
||||
fs.clean
|
||||
end
|
||||
|
||||
# example_group.around do |example|
|
||||
# Bundler.with_clean_env { example.run }
|
||||
# end
|
||||
end
|
||||
|
||||
include ActiveModelHelpers
|
||||
include BaseHelpers
|
||||
include CommandHelpers
|
||||
include GemHelpers
|
||||
include RailsVersionHelpers
|
||||
include RspecHelpers
|
||||
include RubyVersionHelpers
|
||||
include StepHelpers
|
||||
end
|
||||
end
|
7
spec/support/acceptance/helpers/active_model_helpers.rb
Normal file
7
spec/support/acceptance/helpers/active_model_helpers.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
module AcceptanceTests
|
||||
module ActiveModelHelpers
|
||||
def active_model_version
|
||||
Bundler.definition.specs['activemodel'][0].version
|
||||
end
|
||||
end
|
||||
end
|
13
spec/support/acceptance/helpers/array_helpers.rb
Normal file
13
spec/support/acceptance/helpers/array_helpers.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
module AcceptanceTests
|
||||
module ArrayHelpers
|
||||
def to_sentence(array)
|
||||
if array.size == 1
|
||||
array[0]
|
||||
elsif array.size == 2
|
||||
array.join(' and ')
|
||||
else
|
||||
to_sentence(array[1..-2].join(', '), [array[-1]])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
14
spec/support/acceptance/helpers/base_helpers.rb
Normal file
14
spec/support/acceptance/helpers/base_helpers.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
require_relative '../../tests/filesystem'
|
||||
require_relative '../../tests/bundle'
|
||||
|
||||
module AcceptanceTests
|
||||
module BaseHelpers
|
||||
def fs
|
||||
@_fs ||= Tests::Filesystem.new
|
||||
end
|
||||
|
||||
def bundle
|
||||
@_bundle ||= Tests::Bundle.new
|
||||
end
|
||||
end
|
||||
end
|
51
spec/support/acceptance/helpers/command_helpers.rb
Normal file
51
spec/support/acceptance/helpers/command_helpers.rb
Normal file
|
@ -0,0 +1,51 @@
|
|||
require_relative 'base_helpers'
|
||||
require_relative '../../tests/command_runner'
|
||||
|
||||
module AcceptanceTests
|
||||
module CommandHelpers
|
||||
include BaseHelpers
|
||||
extend RSpec::Matchers::DSL
|
||||
|
||||
def run_command(*args)
|
||||
Tests::CommandRunner.run(*args) do |runner|
|
||||
runner.directory = fs.project_directory
|
||||
yield runner if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
def run_command!(*args)
|
||||
run_command(*args) do |runner|
|
||||
runner.run_successfully = true
|
||||
yield runner if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
def run_command_within_bundle(*args)
|
||||
run_command(*args) do |runner|
|
||||
runner.command_prefix = 'bundle exec'
|
||||
runner.env['BUNDLE_GEMFILE'] = fs.find_in_project('Gemfile').to_s
|
||||
|
||||
runner.around_command do |run_command|
|
||||
Bundler.with_clean_env(&run_command)
|
||||
end
|
||||
|
||||
yield runner if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
def run_command_within_bundle!(*args)
|
||||
run_command_within_bundle(*args) do |runner|
|
||||
runner.run_successfully = true
|
||||
yield runner if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
def run_rake_tasks(*tasks)
|
||||
run_command_within_bundle('rake', *tasks)
|
||||
end
|
||||
|
||||
def run_rake_tasks!(*tasks)
|
||||
run_command_within_bundle!('rake', *tasks)
|
||||
end
|
||||
end
|
||||
end
|
23
spec/support/acceptance/helpers/file_helpers.rb
Normal file
23
spec/support/acceptance/helpers/file_helpers.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
require_relative 'base_helpers'
|
||||
|
||||
module AcceptanceTests
|
||||
module FileHelpers
|
||||
include BaseHelpers
|
||||
|
||||
def append_to_file(path, content, options = {})
|
||||
fs.append_to_file(path, content, options)
|
||||
end
|
||||
|
||||
def append_to_file_following(path, content_to_add, insertion_point)
|
||||
fs.append_to_file_following(path, content_to_add, insertion_point)
|
||||
end
|
||||
|
||||
def remove_from_file(path, pattern)
|
||||
fs.remove_from_file(path, pattern)
|
||||
end
|
||||
|
||||
def write_file(path, content)
|
||||
fs.write(path, content)
|
||||
end
|
||||
end
|
||||
end
|
31
spec/support/acceptance/helpers/gem_helpers.rb
Normal file
31
spec/support/acceptance/helpers/gem_helpers.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
require_relative 'base_helpers'
|
||||
require_relative 'command_helpers'
|
||||
require_relative 'file_helpers'
|
||||
|
||||
module AcceptanceTests
|
||||
module GemHelpers
|
||||
include BaseHelpers
|
||||
include CommandHelpers
|
||||
include FileHelpers
|
||||
|
||||
def add_gem(gem, *args)
|
||||
bundle.add_gem(gem, *args)
|
||||
end
|
||||
|
||||
def install_gems
|
||||
bundle.install_gems
|
||||
end
|
||||
|
||||
def updating_bundle(&block)
|
||||
bundle.updating(&block)
|
||||
end
|
||||
|
||||
def bundle_version_of(gem)
|
||||
bundle.version_of(gem)
|
||||
end
|
||||
|
||||
def bundle_includes?(gem)
|
||||
bundle.includes?(gem)
|
||||
end
|
||||
end
|
||||
end
|
21
spec/support/acceptance/helpers/minitest_helpers.rb
Normal file
21
spec/support/acceptance/helpers/minitest_helpers.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
module AcceptanceTests
|
||||
module MinitestHelpers
|
||||
def minitest_test_case_superclass
|
||||
if minitest_gte_5?
|
||||
'Minitest::Test'
|
||||
else
|
||||
'MiniTest::Unit::TestCase'
|
||||
end
|
||||
end
|
||||
|
||||
def minitest_gte_5?
|
||||
if minitest_version
|
||||
Gem::Requirement.new('>= 5').satisfied_by?(minitest_version)
|
||||
end
|
||||
end
|
||||
|
||||
def minitest_version
|
||||
Bundler.definition.specs['minitest'][0].version
|
||||
end
|
||||
end
|
||||
end
|
13
spec/support/acceptance/helpers/pluralization_helpers.rb
Normal file
13
spec/support/acceptance/helpers/pluralization_helpers.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
module AcceptanceTests
|
||||
module PluralizationHelpers
|
||||
def pluralize(count, singular_version, plural_version = nil)
|
||||
plural_version ||= singular_version + 's'
|
||||
|
||||
if count == 1
|
||||
"#{count} #{singular_version}"
|
||||
else
|
||||
"#{count} #{plural_version}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
9
spec/support/acceptance/helpers/rails_version_helpers.rb
Normal file
9
spec/support/acceptance/helpers/rails_version_helpers.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
module AcceptanceTests
|
||||
module RailsVersionHelpers
|
||||
include GemHelpers
|
||||
|
||||
def rails_version
|
||||
bundle_version_of('rails')
|
||||
end
|
||||
end
|
||||
end
|
26
spec/support/acceptance/helpers/rspec_helpers.rb
Normal file
26
spec/support/acceptance/helpers/rspec_helpers.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
module AcceptanceTests
|
||||
module RspecHelpers
|
||||
include GemHelpers
|
||||
|
||||
def rspec_rails_version
|
||||
bundle_version_of('rspec-rails')
|
||||
end
|
||||
|
||||
def add_spec(path, content)
|
||||
content = "require '#{spec_helper_require_path}'\n#{content}"
|
||||
write_file path, content
|
||||
end
|
||||
|
||||
def spec_helper_require_path
|
||||
if rspec_rails_version >= 3
|
||||
'rails_helper'
|
||||
else
|
||||
'spec_helper'
|
||||
end
|
||||
end
|
||||
|
||||
def spec_helper_file_path
|
||||
"spec/#{spec_helper_require_path}.rb"
|
||||
end
|
||||
end
|
||||
end
|
9
spec/support/acceptance/helpers/ruby_version_helpers.rb
Normal file
9
spec/support/acceptance/helpers/ruby_version_helpers.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
require_relative '../../tests/version'
|
||||
|
||||
module AcceptanceTests
|
||||
module RubyVersionHelpers
|
||||
def ruby_version
|
||||
Tests::Version.new(RUBY_VERSION)
|
||||
end
|
||||
end
|
||||
end
|
110
spec/support/acceptance/helpers/step_helpers.rb
Normal file
110
spec/support/acceptance/helpers/step_helpers.rb
Normal file
|
@ -0,0 +1,110 @@
|
|||
require_relative 'file_helpers'
|
||||
require_relative 'gem_helpers'
|
||||
require_relative 'minitest_helpers'
|
||||
|
||||
module AcceptanceTests
|
||||
module StepHelpers
|
||||
include FileHelpers
|
||||
include GemHelpers
|
||||
include MinitestHelpers
|
||||
|
||||
extend RSpec::Matchers::DSL
|
||||
|
||||
def create_active_model_project
|
||||
create_generic_bundler_project
|
||||
add_gem 'activemodel', active_model_version
|
||||
end
|
||||
|
||||
def create_generic_bundler_project
|
||||
fs.create
|
||||
run_command! 'bundle init'
|
||||
end
|
||||
|
||||
def add_shoulda_matchers_to_project(options = {})
|
||||
gem_options = { path: fs.root_directory }
|
||||
|
||||
if options[:manually]
|
||||
gem_options[:require] = false
|
||||
end
|
||||
|
||||
add_gem 'shoulda-matchers', gem_options
|
||||
|
||||
if options[:manually]
|
||||
append_to_file spec_helper_file_path,
|
||||
"require 'shoulda/matchers'",
|
||||
following: "require 'rspec/rails'"
|
||||
end
|
||||
end
|
||||
|
||||
def add_minitest_to_project
|
||||
add_gem 'shoulda-context'
|
||||
add_gem 'minitest-reporters'
|
||||
write_file 'test/test_helper.rb', <<-FILE
|
||||
require 'minitest/autorun'
|
||||
require 'minitest/reporters'
|
||||
require 'shoulda/context'
|
||||
require 'shoulda/matchers'
|
||||
|
||||
Minitest::Reporters.use!(Minitest::Reporters::SpecReporter.new)
|
||||
FILE
|
||||
end
|
||||
|
||||
def write_minitest_test(path, &block)
|
||||
contents = block.call(minitest_test_case_superclass)
|
||||
write_file(path, contents)
|
||||
end
|
||||
|
||||
def run_n_unit_tests(*paths)
|
||||
run_command 'ruby -I lib -I test', *paths
|
||||
end
|
||||
|
||||
def run_n_unit_test_suite
|
||||
run_rake_tasks('test', env: { TESTOPTS: '-v' })
|
||||
end
|
||||
|
||||
def create_rails_application
|
||||
command = "bundle exec rails new #{fs.project_directory} --skip-bundle"
|
||||
|
||||
run_command!(command) do |runner|
|
||||
runner.directory = nil
|
||||
end
|
||||
|
||||
updating_bundle do |bundle|
|
||||
bundle.remove_gem 'turn'
|
||||
bundle.remove_gem 'coffee-rails'
|
||||
bundle.remove_gem 'uglifier'
|
||||
|
||||
# if ruby_version >= '1.9.3'
|
||||
# bundle.add_gem 'rake', '~> 0.9'
|
||||
# run_command! 'bundle update rake --local'
|
||||
# end
|
||||
end
|
||||
end
|
||||
|
||||
def configure_routes_with_single_wildcard_route
|
||||
write_file 'config/routes.rb', <<-FILE
|
||||
Rails.application.routes.draw do
|
||||
get ':controller(/:action(/:id(.:format)))'
|
||||
end
|
||||
FILE
|
||||
end
|
||||
|
||||
def add_rspec_rails_to_project!
|
||||
add_gem 'rspec-rails', rspec_rails_version
|
||||
run_command_within_bundle!('rails g rspec:install')
|
||||
remove_from_file '.rspec', '--warnings'
|
||||
end
|
||||
|
||||
def run_rspec_suite
|
||||
run_rake_tasks('spec', env: { SPEC_OPTS: '-fd' })
|
||||
end
|
||||
|
||||
def add_spring_to_project
|
||||
if rails_version < 4
|
||||
add_gem 'spring'
|
||||
end
|
||||
|
||||
add_gem 'spring-commands-rspec'
|
||||
end
|
||||
end
|
||||
end
|
31
spec/support/acceptance/matchers/have_output.rb
Normal file
31
spec/support/acceptance/matchers/have_output.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
module AcceptanceTests
|
||||
module Matchers
|
||||
def have_output(output)
|
||||
HaveOutputMatcher.new(output)
|
||||
end
|
||||
|
||||
class HaveOutputMatcher
|
||||
def initialize(output)
|
||||
@output = output
|
||||
end
|
||||
|
||||
def matches?(runner)
|
||||
@runner = runner
|
||||
runner.has_output?(output)
|
||||
end
|
||||
|
||||
def failure_message
|
||||
"Expected command to have output, but did not.\n\n" +
|
||||
"Command: #{runner.formatted_command}\n\n" +
|
||||
"Expected output:\n" +
|
||||
output + "\n\n" +
|
||||
"Actual output:\n" +
|
||||
runner.elided_output
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :output, :runner
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,55 @@
|
|||
require_relative '../helpers/pluralization_helpers'
|
||||
require_relative '../helpers/rails_version_helpers'
|
||||
|
||||
module AcceptanceTests
|
||||
module Matchers
|
||||
def indicate_number_of_tests_was_run(expected_output)
|
||||
IndicateNumberOfTestsWasRunMatcher.new(expected_output)
|
||||
end
|
||||
|
||||
class IndicateNumberOfTestsWasRunMatcher
|
||||
include PluralizationHelpers
|
||||
include RailsVersionHelpers
|
||||
|
||||
def initialize(number)
|
||||
@number = number
|
||||
end
|
||||
|
||||
def matches?(runner)
|
||||
@runner = runner
|
||||
expected_output === actual_output
|
||||
end
|
||||
|
||||
def failure_message
|
||||
message = "Expected output to indicate that #{some_tests_were_run}.\n" +
|
||||
"Expected output: #{expected_output}\n"
|
||||
|
||||
if actual_output.empty?
|
||||
message << 'Actual output: (empty)'
|
||||
else
|
||||
message << "Actual output:\n#{actual_output}"
|
||||
end
|
||||
|
||||
message
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :number, :runner
|
||||
|
||||
private
|
||||
|
||||
def expected_output
|
||||
/#{number} (tests|runs), #{number} assertions, 0 failures, 0 errors(, 0 skips)?/
|
||||
end
|
||||
|
||||
def actual_output
|
||||
runner.output
|
||||
end
|
||||
|
||||
def some_tests_were_run
|
||||
pluralize(number, 'test was', 'tests were') + ' run'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,103 @@
|
|||
require_relative '../helpers/array_helpers'
|
||||
require_relative '../helpers/pluralization_helpers'
|
||||
require_relative '../helpers/rails_version_helpers'
|
||||
|
||||
module AcceptanceTests
|
||||
module Matchers
|
||||
def indicate_that_tests_were_run(series)
|
||||
IndicateThatTestsWereRunMatcher.new(series)
|
||||
end
|
||||
|
||||
class IndicateThatTestsWereRunMatcher
|
||||
include ArrayHelpers
|
||||
include PluralizationHelpers
|
||||
include RailsVersionHelpers
|
||||
|
||||
def initialize(args)
|
||||
@args = args
|
||||
@series = args.values
|
||||
end
|
||||
|
||||
def matches?(runner)
|
||||
@runner = runner
|
||||
!matching_expected_output.nil?
|
||||
end
|
||||
|
||||
def failure_message
|
||||
"Expected output to indicate that #{some_tests_were_run}.\n" +
|
||||
"#{formatted_expected_output}\n" +
|
||||
"#{formatted_actual_output}\n"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :args, :series, :runner
|
||||
|
||||
private
|
||||
|
||||
def expected_outputs
|
||||
[
|
||||
expected_output_for_rails_3,
|
||||
expected_output_for_turn,
|
||||
expected_output_for_rails_4
|
||||
]
|
||||
end
|
||||
|
||||
def matching_expected_output
|
||||
@_matching_expected_output ||=
|
||||
expected_outputs.detect do |expected_output|
|
||||
actual_output =~ expected_output
|
||||
end
|
||||
end
|
||||
|
||||
def expected_output_for_rails_3
|
||||
full_report = series.map do |number|
|
||||
"#{number} tests, #{number} assertions, 0 failures, 0 errors(, 0 skips)?"
|
||||
end.join('.+')
|
||||
|
||||
Regexp.new(full_report, Regexp::MULTILINE)
|
||||
end
|
||||
|
||||
def expected_output_for_turn
|
||||
full_report = series.map do |number|
|
||||
"pass: #{number}, fail: 0, error: 0"
|
||||
end.join('.+')
|
||||
|
||||
Regexp.new(full_report, Regexp::MULTILINE)
|
||||
end
|
||||
|
||||
def expected_output_for_rails_4
|
||||
total = series.inject(:+)
|
||||
/#{total} (tests|runs), #{total} assertions, 0 failures, 0 errors(, 0 skips)?/
|
||||
end
|
||||
|
||||
def formatted_expected_output
|
||||
if matching_expected_output
|
||||
"Expected output:\n#{matching_actual_output}"
|
||||
else
|
||||
"Expected output: (n/a)"
|
||||
end
|
||||
end
|
||||
|
||||
def actual_output
|
||||
runner.output
|
||||
end
|
||||
|
||||
def formatted_actual_output
|
||||
if actual_output.empty?
|
||||
"Actual output: (empty)"
|
||||
else
|
||||
"Actual output:\n#{actual_output}"
|
||||
end
|
||||
end
|
||||
|
||||
def some_tests_were_run
|
||||
clauses = args.map do |type, number|
|
||||
pluralize(number, "#{type} test was run", "#{type} tests were run")
|
||||
end
|
||||
|
||||
to_sentence(clauses)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
94
spec/support/tests/bundle.rb
Normal file
94
spec/support/tests/bundle.rb
Normal file
|
@ -0,0 +1,94 @@
|
|||
require_relative 'filesystem'
|
||||
require_relative 'command_runner'
|
||||
require_relative 'version'
|
||||
|
||||
module Tests
|
||||
class Bundle
|
||||
def initialize
|
||||
@already_updating = false
|
||||
@fs = Filesystem.new
|
||||
end
|
||||
|
||||
def updating(&block)
|
||||
if already_updating?
|
||||
yield self
|
||||
return
|
||||
end
|
||||
|
||||
@already_updating = true
|
||||
|
||||
yield self
|
||||
|
||||
@already_updating = false
|
||||
|
||||
install_gems
|
||||
end
|
||||
|
||||
def add_gem(gem, *args)
|
||||
updating do
|
||||
options = args.last.is_a?(Hash) ? args.pop : {}
|
||||
version = args.shift
|
||||
line = assemble_gem_line(gem, version, options)
|
||||
fs.append_to_file('Gemfile', line)
|
||||
end
|
||||
end
|
||||
|
||||
def remove_gem(gem)
|
||||
updating do
|
||||
fs.remove_from_file('Gemfile', /^gem ("|')gem\1/)
|
||||
end
|
||||
end
|
||||
|
||||
def install_gems
|
||||
CommandRunner.run!('bundle install --local') do |runner|
|
||||
runner.retries = 5
|
||||
end
|
||||
end
|
||||
|
||||
def version_of(gem)
|
||||
Version.new(Bundler.definition.specs[gem][0].version)
|
||||
end
|
||||
|
||||
def includes?(gem)
|
||||
Bundler.definition.dependencies.any? do |dependency|
|
||||
dependency.name == gem
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :fs
|
||||
|
||||
private
|
||||
|
||||
def already_updating?
|
||||
@already_updating
|
||||
end
|
||||
|
||||
def assemble_gem_line(gem, version, options)
|
||||
formatted_options = options.
|
||||
map { |key, value| "#{key}: #{formatted_value(value)}" }.
|
||||
join(', ')
|
||||
|
||||
line = %(gem '#{gem}')
|
||||
|
||||
if version
|
||||
line << %(, '#{version}')
|
||||
end
|
||||
|
||||
if options.any?
|
||||
line << %(, #{formatted_options})
|
||||
end
|
||||
|
||||
line << "\n"
|
||||
end
|
||||
|
||||
def formatted_value(value)
|
||||
if value.is_a?(Pathname)
|
||||
value.to_s.inspect
|
||||
else
|
||||
value.inspect
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
213
spec/support/tests/command_runner.rb
Normal file
213
spec/support/tests/command_runner.rb
Normal file
|
@ -0,0 +1,213 @@
|
|||
require 'timeout'
|
||||
require 'shellwords'
|
||||
|
||||
module Tests
|
||||
class CommandRunner
|
||||
TimeoutError = Class.new(StandardError)
|
||||
|
||||
def self.run(*args)
|
||||
new(*args).tap do |runner|
|
||||
yield runner
|
||||
runner.call
|
||||
end
|
||||
end
|
||||
|
||||
def self.run!(*args)
|
||||
run(*args) do |runner|
|
||||
runner.run_successfully = true
|
||||
yield runner if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :status, :options, :env, :directory
|
||||
attr_accessor :command_prefix, :run_quickly, :run_successfully, :retries,
|
||||
:timeout
|
||||
|
||||
def initialize(*args)
|
||||
@reader, @writer = IO.pipe
|
||||
options = (args.last.is_a?(Hash) ? args.pop : {})
|
||||
@args = args
|
||||
@options = options.merge(
|
||||
err: [:child, :out],
|
||||
out: writer
|
||||
)
|
||||
@env = extract_env_from(@options)
|
||||
|
||||
@wrapper = ->(block) { block.call }
|
||||
@command_prefix = ''
|
||||
@directory = Dir.pwd
|
||||
@run_quickly = false
|
||||
@run_successfully = false
|
||||
@retries = 1
|
||||
@num_times_run = 0
|
||||
@timeout = 20
|
||||
end
|
||||
|
||||
def around_command(&block)
|
||||
@wrapper = block
|
||||
end
|
||||
|
||||
def directory=(directory)
|
||||
@directory = directory || Dir.pwd
|
||||
end
|
||||
|
||||
def formatted_command
|
||||
[formatted_env, Shellwords.join(command)].
|
||||
select { |value| !value.empty? }.
|
||||
join(' ')
|
||||
end
|
||||
|
||||
def call
|
||||
possibly_retrying do
|
||||
possibly_running_quickly do
|
||||
debug { "\n\e[32mRunning command:\e[0m #{formatted_command}" }
|
||||
wrapper.call(-> { run })
|
||||
debug { "\n" + divider('START') + output + divider('END') }
|
||||
|
||||
if run_successfully && !success?
|
||||
fail!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def stop
|
||||
unless writer.closed?
|
||||
writer.close
|
||||
end
|
||||
end
|
||||
|
||||
def output
|
||||
@_output ||= begin
|
||||
stop
|
||||
without_colors(reader.read)
|
||||
end
|
||||
end
|
||||
|
||||
def elided_output
|
||||
lines = output.split(/\n/)
|
||||
new_lines = lines[0..4]
|
||||
|
||||
if lines.size > 10
|
||||
new_lines << "(...#{lines.size - 10} more lines...)"
|
||||
end
|
||||
|
||||
new_lines << lines[-5..-1]
|
||||
new_lines.join("\n")
|
||||
end
|
||||
|
||||
def success?
|
||||
status.success?
|
||||
end
|
||||
|
||||
def exit_status
|
||||
status.exitstatus
|
||||
end
|
||||
|
||||
def fail!
|
||||
raise <<-MESSAGE
|
||||
Command #{command.inspect} exited with status #{exit_status}.
|
||||
Output:
|
||||
#{divider('START') + output + divider('END')}
|
||||
MESSAGE
|
||||
end
|
||||
|
||||
def has_output?(expected_output)
|
||||
output.include?(expected_output)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :args, :reader, :writer, :wrapper
|
||||
|
||||
private
|
||||
|
||||
def extract_env_from(options)
|
||||
options.delete(:env) { {} }.inject({}) do |hash, (key, value)|
|
||||
hash[key.to_s] = value
|
||||
hash
|
||||
end
|
||||
end
|
||||
|
||||
def command
|
||||
([command_prefix] + args).flatten.flat_map do |word|
|
||||
Shellwords.split(word)
|
||||
end
|
||||
end
|
||||
|
||||
def formatted_env
|
||||
env.map { |key, value| "#{key}=#{value.inspect}" }.join(' ')
|
||||
end
|
||||
|
||||
def run
|
||||
Dir.chdir(directory) do
|
||||
system(env, *command, options)
|
||||
end
|
||||
|
||||
@status = $?
|
||||
end
|
||||
|
||||
def possibly_running_quickly(&block)
|
||||
if run_quickly
|
||||
begin
|
||||
Timeout.timeout(timeout, &block)
|
||||
rescue Timeout::Error
|
||||
stop
|
||||
|
||||
message =
|
||||
"Command timed out after #{timeout} seconds: #{formatted_command}\n" +
|
||||
"Output:\n" +
|
||||
elided_output
|
||||
|
||||
raise TimeoutError, message
|
||||
end
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def possibly_retrying
|
||||
begin
|
||||
@num_times_run += 1
|
||||
yield
|
||||
rescue => error
|
||||
debug { "#{error.class}: #{error.message}" }
|
||||
|
||||
if @num_times_run < @retries
|
||||
sleep @num_times_run
|
||||
retry
|
||||
else
|
||||
raise error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def divider(title = '')
|
||||
total_length = 72
|
||||
start_length = 3
|
||||
|
||||
string = ''
|
||||
string << ('-' * start_length)
|
||||
string << title
|
||||
string << '-' * (total_length - start_length - title.length)
|
||||
string << "\n"
|
||||
string
|
||||
end
|
||||
|
||||
def without_colors(string)
|
||||
string.gsub(/\e\[\d+(?:;\d+)?m(.+?)\e\[0m/, '\1')
|
||||
end
|
||||
|
||||
def debugging_enabled?
|
||||
ENV['DEBUG_COMMANDS'] == '1'
|
||||
end
|
||||
|
||||
def debug(&block)
|
||||
if debugging_enabled?
|
||||
puts block.call
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
84
spec/support/tests/filesystem.rb
Normal file
84
spec/support/tests/filesystem.rb
Normal file
|
@ -0,0 +1,84 @@
|
|||
require 'fileutils'
|
||||
|
||||
module Tests
|
||||
class Filesystem
|
||||
PROJECT_NAME = 'test-project'
|
||||
ROOT_DIRECTORY = Pathname.new('../../../..').expand_path(__FILE__)
|
||||
TEMP_DIRECTORY = ROOT_DIRECTORY.join('tmp/acceptance')
|
||||
PROJECT_DIRECTORY = TEMP_DIRECTORY.join(PROJECT_NAME)
|
||||
|
||||
def root_directory
|
||||
ROOT_DIRECTORY
|
||||
end
|
||||
|
||||
def temp_directory
|
||||
TEMP_DIRECTORY
|
||||
end
|
||||
|
||||
def project_directory
|
||||
PROJECT_DIRECTORY
|
||||
end
|
||||
|
||||
def within_project(&block)
|
||||
Dir.chdir(project_directory, &block)
|
||||
end
|
||||
|
||||
def clean
|
||||
if temp_directory.exist?
|
||||
temp_directory.rmtree
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
project_directory.mkpath
|
||||
end
|
||||
|
||||
def find_in_project(path)
|
||||
project_directory.join(path)
|
||||
end
|
||||
|
||||
def open(path, *args, &block)
|
||||
find_in_project(path).open(*args, &block)
|
||||
end
|
||||
|
||||
def read(path)
|
||||
find_in_project(path).read
|
||||
end
|
||||
|
||||
def write(path, content)
|
||||
pathname = find_in_project(path)
|
||||
pathname.dirname.mkpath
|
||||
pathname.open('w') { |f| f.write(content) }
|
||||
end
|
||||
|
||||
def append_to_file(path, content, options = {})
|
||||
if options[:following]
|
||||
append_to_file_following(path, content, options[:following])
|
||||
else
|
||||
open(path, 'a') { |f| f.puts(content + "\n") }
|
||||
end
|
||||
end
|
||||
|
||||
def append_to_file_following(path, content_to_add, insertion_point)
|
||||
content_to_add = content_to_add + "\n"
|
||||
|
||||
file_content = read(path)
|
||||
file_lines = file_content.split("\n")
|
||||
insertion_index = file_lines.find_index(insertion_point)
|
||||
|
||||
if insertion_index.nil?
|
||||
raise "Cannot find #{insertion_point.inspect} in #{path}"
|
||||
end
|
||||
|
||||
file_lines.insert(insertion_index + 1, content_to_add)
|
||||
new_file_content = file_lines.join("\n")
|
||||
write(path, new_file_content)
|
||||
end
|
||||
|
||||
def remove_from_file(path, pattern)
|
||||
content = read(path)
|
||||
content.sub!(/#{pattern}\n/, '')
|
||||
write(path, content)
|
||||
end
|
||||
end
|
||||
end
|
45
spec/support/tests/version.rb
Normal file
45
spec/support/tests/version.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
module Tests
|
||||
class Version
|
||||
def initialize(version)
|
||||
@version = Gem::Version.new(version.to_s + '')
|
||||
end
|
||||
|
||||
def <(other_version)
|
||||
compare?(:<, other_version)
|
||||
end
|
||||
|
||||
def <=(other_version)
|
||||
compare?(:<=, other_version)
|
||||
end
|
||||
|
||||
def ==(other_version)
|
||||
compare?(:==, other_version)
|
||||
end
|
||||
|
||||
def >=(other_version)
|
||||
compare?(:>=, other_version)
|
||||
end
|
||||
|
||||
def >(other_version)
|
||||
compare?(:>, other_version)
|
||||
end
|
||||
|
||||
def =~(other_version)
|
||||
Gem::Requirement.new(other_version).satisfied_by?(version)
|
||||
end
|
||||
|
||||
def to_s
|
||||
version.to_s
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :version
|
||||
|
||||
private
|
||||
|
||||
def compare?(op, other_version)
|
||||
Gem::Requirement.new("#{op} #{other_version}").satisfied_by?(version)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -29,7 +29,7 @@ require 'rspec/rails'
|
|||
PROJECT_ROOT = File.expand_path('../..', __FILE__)
|
||||
$LOAD_PATH << File.join(PROJECT_ROOT, 'lib')
|
||||
|
||||
Dir[ File.join(File.expand_path('../support/unit/**/*.rb', __FILE__)) ].each do |file|
|
||||
Dir[ File.join(File.expand_path('../support/unit/**/*.rb', __FILE__)) ].sort.each do |file|
|
||||
require file
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue