cucumber-rails 1.5.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE.md +52 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +42 -0
  4. data/.gitignore +3 -1
  5. data/.rspec +4 -1
  6. data/.rubocop.yml +68 -0
  7. data/.travis.yml +43 -33
  8. data/Appraisals +25 -92
  9. data/{History.md → CHANGELOG.md} +242 -40
  10. data/CONTRIBUTING.md +15 -24
  11. data/Gemfile +2 -4
  12. data/LICENSE +1 -1
  13. data/README.md +57 -27
  14. data/Rakefile +18 -14
  15. data/bin/install_geckodriver.sh +19 -0
  16. data/bin/install_webpacker.sh +9 -0
  17. data/config/cucumber.yml +2 -5
  18. data/cucumber-rails.gemspec +35 -28
  19. data/dev_tasks/cucumber.rake +2 -0
  20. data/dev_tasks/rspec.rake +2 -0
  21. data/dev_tasks/yard.rake +7 -14
  22. data/dev_tasks/yard/default/layout/html/setup.rb +6 -1
  23. data/features/allow_rescue.feature +16 -11
  24. data/features/annotations.feature +8 -11
  25. data/features/capybara_javascript_drivers.feature +38 -28
  26. data/features/choose_javascript_database_strategy.feature +37 -57
  27. data/features/configuration.feature +48 -0
  28. data/features/database_cleaner.feature +18 -18
  29. data/features/disable_automatic_database_cleaning.feature +12 -18
  30. data/features/emulate_javascript.feature +64 -47
  31. data/features/install_cucumber_rails.feature +4 -4
  32. data/features/no_database.feature +8 -19
  33. data/features/raising_errors.feature +9 -3
  34. data/features/rerun_profile.feature +17 -7
  35. data/features/rest_api.feature +12 -12
  36. data/features/step_definitions/cucumber_rails_steps.rb +44 -88
  37. data/features/support/aruba.rb +3 -1
  38. data/features/support/cucumber_rails_helper.rb +85 -0
  39. data/features/support/env.rb +4 -30
  40. data/features/support/hooks.rb +8 -0
  41. data/gemfiles/rails_4_2.gemfile +5 -13
  42. data/gemfiles/rails_5_0.gemfile +5 -12
  43. data/gemfiles/rails_5_1.gemfile +4 -9
  44. data/gemfiles/rails_5_2.gemfile +10 -0
  45. data/gemfiles/rails_6_0.gemfile +9 -0
  46. data/lib/cucumber/rails.rb +17 -15
  47. data/lib/cucumber/rails/action_dispatch.rb +21 -0
  48. data/lib/cucumber/rails/application.rb +16 -7
  49. data/lib/cucumber/rails/capybara.rb +2 -0
  50. data/lib/cucumber/rails/capybara/javascript_emulation.rb +45 -37
  51. data/lib/cucumber/rails/capybara/select_dates_and_times.rb +6 -4
  52. data/lib/cucumber/rails/database.rb +30 -8
  53. data/lib/cucumber/rails/hooks.rb +2 -0
  54. data/lib/cucumber/rails/hooks/active_record.rb +14 -12
  55. data/lib/cucumber/rails/hooks/allow_rescue.rb +2 -0
  56. data/lib/cucumber/rails/hooks/database_cleaner.rb +6 -4
  57. data/lib/cucumber/rails/hooks/mail.rb +4 -4
  58. data/lib/cucumber/rails/rspec.rb +3 -1
  59. data/lib/cucumber/rails/world.rb +24 -7
  60. data/lib/generators/cucumber/{install/USAGE → USAGE} +0 -0
  61. data/lib/generators/cucumber/{install/install_generator.rb → install_generator.rb} +23 -10
  62. data/lib/generators/cucumber/{install/templates → templates}/config/cucumber.yml.erb +4 -3
  63. data/lib/generators/cucumber/{install/templates → templates}/script/cucumber +1 -0
  64. data/lib/generators/cucumber/{install/templates → templates}/support/_rails_each_run.rb.erb +2 -2
  65. data/lib/generators/cucumber/{install/templates → templates}/support/_rails_prefork.rb.erb +0 -0
  66. data/lib/generators/cucumber/{install/templates → templates}/support/capybara.rb +2 -0
  67. data/lib/generators/cucumber/{install/templates → templates}/support/edit_warning.txt +0 -0
  68. data/lib/generators/cucumber/{install/templates → templates}/support/rails.rb.erb +0 -0
  69. data/lib/generators/cucumber/{install/templates → templates}/support/rails_spork.rb.erb +0 -0
  70. data/lib/generators/cucumber/{install/templates → templates}/tasks/cucumber.rake.erb +9 -9
  71. data/spec/cucumber/rails/database_spec.rb +46 -33
  72. data/spec/generators/cucumber/{install/install_generator_spec.rb → install_generator_spec.rb} +17 -9
  73. data/spec/spec_helper.rb +13 -4
  74. metadata +105 -139
  75. data/Gemfile.appraisal +0 -3
  76. data/features/support/bundler_pre_support.rb +0 -28
  77. data/features/support/fixtures/bundler-1.0.21.gem +0 -0
  78. data/features/support/fixtures/bundler-1.1.rc.gem +0 -0
  79. data/features/support/legacy_web_steps_support.rb +0 -289
  80. data/gemfiles/lowest_version_bounds.gemfile +0 -26
  81. data/gemfiles/rails_4_0.gemfile +0 -19
  82. data/gemfiles/rails_4_1.gemfile +0 -18
  83. data/lib/cucumber/rails/action_controller.rb +0 -12
@@ -1,17 +1,26 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rails/application'
2
4
 
3
5
  # Make sure the ActionDispatch::ShowExceptions middleware is always enabled,
4
6
  # regardless of what is in config/environments/test.rb
5
7
  # Instead we are overriding ActionDispatch::ShowExceptions to be able to
6
8
  # toggle whether or not exceptions are raised.
7
- class Rails::Application
8
- alias __cucumber_orig_initialize__ initialize!
9
9
 
10
- def initialize!
11
- ad = config.action_dispatch
12
- def ad.show_exceptions
13
- true
10
+ module Cucumber
11
+ module Rails
12
+ module Application
13
+ def initialize!
14
+ ad = config.action_dispatch
15
+
16
+ def ad.show_exceptions
17
+ true
18
+ end
19
+
20
+ super
21
+ end
14
22
  end
15
- __cucumber_orig_initialize__
16
23
  end
17
24
  end
25
+
26
+ Rails::Application.prepend(Cucumber::Rails::Application)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'capybara'
2
4
  require 'capybara/rails'
3
5
  require 'capybara/cucumber'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Cucumber
2
4
  module Rails
3
5
  module Capybara
@@ -9,9 +11,11 @@ module Cucumber
9
11
  end
10
12
  end
11
13
 
12
- def click_with_javascript_emulation
14
+ def click_with_javascript_emulation(*)
13
15
  if link_with_non_get_http_method?
14
- ::Capybara::RackTest::Form.new(driver, js_form(element_node.document, self[:href], emulated_method)).submit(self)
16
+ ::Capybara::RackTest::Form.new(
17
+ driver, js_form(element_node.document, self[:href], emulated_method)
18
+ ).submit(self)
15
19
  else
16
20
  click_without_javascript_emulation
17
21
  end
@@ -44,61 +48,65 @@ module Cucumber
44
48
  js_form['action'] = action
45
49
  js_form['method'] = method
46
50
 
47
- if emulated_method and emulated_method.downcase != method.downcase
48
- input = document.create_element('input')
49
- input['type'] = 'hidden'
50
- input['name'] = '_method'
51
- input['value'] = emulated_method
52
- js_form.add_child(input)
53
- end
51
+ add_hidden_method_input(document, js_form) unless same?(emulated_method, method)
54
52
 
55
- # rails will wipe the session if the CSRF token is not sent
56
- # with non-GET requests
57
- if csrf? && emulated_method.downcase != 'get'
58
- input = document.create_element('input')
59
- input['type'] = 'hidden'
60
- input['name'] = csrf_param
61
- input['value'] = csrf_token
62
- js_form.add_child(input)
63
- end
53
+ # rails will wipe the session if the CSRF token is not sent with non-GET requests
54
+ add_hidden_csrf_input(document, js_form) if csrf? && !get?(emulated_method)
64
55
 
65
56
  js_form
66
57
  end
67
58
 
59
+ def same?(emulated_method, method)
60
+ emulated_method.casecmp(method).zero?
61
+ end
62
+
63
+ def add_hidden_method_input(document, js_form)
64
+ input = document.create_element('input')
65
+ input['type'] = 'hidden'
66
+ input['name'] = '_method'
67
+ input['value'] = emulated_method
68
+ js_form.add_child(input)
69
+ end
70
+
71
+ def get?(emulated_method)
72
+ same?(emulated_method, 'get')
73
+ end
74
+
75
+ def add_hidden_csrf_input(document, js_form)
76
+ input = document.create_element('input')
77
+ input['type'] = 'hidden'
78
+ input['name'] = csrf_param
79
+ input['value'] = csrf_token
80
+ js_form.add_child(input)
81
+ end
82
+
68
83
  def link_with_non_get_http_method?
69
- if ::Rails.version.to_f >= 3.0
70
- tag_name == 'a' && element_node['data-method'] && element_node['data-method'] =~ /(?:delete|put|post)/
71
- else
72
- tag_name == 'a' && element_node['onclick'] && element_node['onclick'] =~ /var f = document\.createElement\('form'\); f\.style\.display = 'none';/
73
- end
84
+ tag_name == 'a' &&
85
+ element_node['data-method'] &&
86
+ element_node['data-method'] =~ /(?:delete|put|post)/
74
87
  end
75
88
 
76
89
  def emulated_method
77
- if ::Rails.version.to_f >= 3.0
78
- element_node['data-method']
79
- else
80
- element_node['onclick'][/m\.setAttribute\('value', '([^']*)'\)/, 1]
81
- end
90
+ element_node['data-method']
82
91
  end
83
92
 
84
93
  def element_node
85
- if self.respond_to? :native
86
- self.native
87
- else
88
- warn 'DEPRECATED: cucumber-rails loves you, just not your version of Capybara. Please update Capybara to >= 0.4.0'
89
- self.node
90
- end
94
+ native
91
95
  end
92
96
  end
93
97
  end
94
98
  end
95
99
  end
96
100
 
97
- class Capybara::RackTest::Node
98
- include ::Cucumber::Rails::Capybara::JavascriptEmulation
101
+ module Capybara
102
+ module RackTest
103
+ class Node
104
+ include ::Cucumber::Rails::Capybara::JavascriptEmulation
105
+ end
106
+ end
99
107
  end
100
108
 
101
- Before('~@no-js-emulation') do
109
+ Before('not @no-js-emulation') do
102
110
  # Enable javascript emulation
103
111
  ::Capybara::RackTest::Node.class_eval do
104
112
  alias_method :click, :click_with_javascript_emulation
@@ -1,19 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Cucumber
2
4
  module Rails
3
5
  module Capybara
4
6
  # This module defines methods for selecting dates and times
5
7
  module SelectDatesAndTimes
6
- # Select a Rails date. Options hash must include :from => +label+
8
+ # Select a Rails date. Options hash must include from: +label+
7
9
  def select_date(date, options)
8
10
  date = Date.parse(date)
9
11
  base_dom_id = get_base_dom_id_from_label_tag(options[:from])
10
12
 
11
13
  find(:xpath, ".//select[@id='#{base_dom_id}_1i']").select(date.year.to_s)
12
- find(:xpath, ".//select[@id='#{base_dom_id}_2i']").select(I18n.l date, format: '%B')
14
+ find(:xpath, ".//select[@id='#{base_dom_id}_2i']").select(I18n.l(date, format: '%B'))
13
15
  find(:xpath, ".//select[@id='#{base_dom_id}_3i']").select(date.day.to_s)
14
16
  end
15
17
 
16
- # Select a Rails time. Options hash must include :from => +label+
18
+ # Select a Rails time. Options hash must include from: +label+
17
19
  def select_time(time, options)
18
20
  time = Time.zone.parse(time)
19
21
  base_dom_id = get_base_dom_id_from_label_tag(options[:from])
@@ -22,7 +24,7 @@ module Cucumber
22
24
  find(:xpath, ".//select[@id='#{base_dom_id}_5i']").select(time.min.to_s.rjust(2, '0'))
23
25
  end
24
26
 
25
- # Select a Rails datetime. Options hash must include :from => +label+
27
+ # Select a Rails datetime. Options hash must include from: +label+
26
28
  def select_datetime(datetime, options)
27
29
  select_date(datetime, options)
28
30
  select_time(datetime, options)
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Cucumber
2
4
  module Rails
3
5
  module Database
4
- CUSTOM_STRATEGY_INTERFACE = %w{ before_js before_non_js }
6
+ CUSTOM_STRATEGY_INTERFACE = %w[before_js before_non_js].freeze
5
7
 
6
8
  class InvalidStrategy < ArgumentError; end
7
9
 
@@ -13,12 +15,12 @@ module Cucumber
13
15
  strategy_type =
14
16
  case strategy
15
17
  when Symbol
16
- map[strategy] || raise(InvalidStrategy, "The strategy '#{strategy}' is not understood. Please use one of #{map.keys.join(',')}")
18
+ map[strategy] || throw_invalid_strategy_error(strategy)
17
19
  when Class
18
20
  strategy
19
21
  end
20
22
 
21
- @strategy = strategy_type.new(*strategy_opts)
23
+ @strategy = strategy_type.new(*strategy_opts)
22
24
 
23
25
  validate_interface!
24
26
  end
@@ -35,7 +37,7 @@ module Cucumber
35
37
  @strategy.after
36
38
  end
37
39
 
38
- private
40
+ private
39
41
 
40
42
  def map
41
43
  {
@@ -46,10 +48,25 @@ module Cucumber
46
48
  }
47
49
  end
48
50
 
51
+ def throw_invalid_strategy_error(strategy)
52
+ raise(InvalidStrategy, "The strategy '#{strategy}' is not understood. Please use one of #{mapped_keys}")
53
+ end
54
+
55
+ def mapped_keys
56
+ map.keys.join(', ')
57
+ end
58
+
49
59
  def validate_interface!
50
- unless CUSTOM_STRATEGY_INTERFACE.all? { |m| @strategy.respond_to?(m) }
51
- raise(ArgumentError, "Strategy must respond to all of: #{CUSTOM_STRATEGY_INTERFACE.map{ |method| "##{method}" } * ' ' } !")
52
- end
60
+ return if CUSTOM_STRATEGY_INTERFACE.all? { |m| @strategy.respond_to?(m) }
61
+
62
+ throw_invalid_strategy_interface_error
63
+ end
64
+
65
+ def throw_invalid_strategy_interface_error
66
+ raise(
67
+ ArgumentError,
68
+ "Strategy must respond to all of: #{CUSTOM_STRATEGY_INTERFACE.map { |method| "##{method}" } * ' '} !"
69
+ )
53
70
  end
54
71
  end
55
72
 
@@ -59,7 +76,11 @@ module Cucumber
59
76
  end
60
77
 
61
78
  def before_js(strategy)
62
- @original_strategy = DatabaseCleaner.connections.first.strategy # that feels like a nasty hack
79
+ @original_strategy = if Gem::Version.new(DatabaseCleaner::VERSION) >= Gem::Version.new('1.8.0.beta')
80
+ DatabaseCleaner.cleaners.values.first.strategy # that feels like a nasty hack
81
+ else
82
+ DatabaseCleaner.connections.first.strategy # that feels like a nasty hack
83
+ end
63
84
  DatabaseCleaner.strategy = strategy, @options
64
85
  end
65
86
 
@@ -69,6 +90,7 @@ module Cucumber
69
90
 
70
91
  def after
71
92
  return unless @original_strategy
93
+
72
94
  DatabaseCleaner.strategy = @original_strategy
73
95
  @original_strategy = nil
74
96
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cucumber/rails/hooks/active_record'
2
4
  require 'cucumber/rails/hooks/database_cleaner'
3
5
  require 'cucumber/rails/hooks/allow_rescue'
@@ -1,21 +1,23 @@
1
- if defined?(ActiveRecord::Base)
2
- class ActiveRecord::Base
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class Base
3
5
  class_attribute :shared_connection
4
6
 
5
7
  def self.connection
6
- self.shared_connection || retrieve_connection
8
+ shared_connection || retrieve_connection
7
9
  end
8
10
  end
11
+ end
9
12
 
10
- Before('@javascript') do
11
- Cucumber::Rails::Database.before_js if Cucumber::Rails::Database.autorun_database_cleaner
12
- end
13
+ Before('@javascript') do
14
+ Cucumber::Rails::Database.before_js if Cucumber::Rails::Database.autorun_database_cleaner
15
+ end
13
16
 
14
- Before('~@javascript') do
15
- Cucumber::Rails::Database.before_non_js if Cucumber::Rails::Database.autorun_database_cleaner
16
- end
17
+ Before('not @javascript') do
18
+ Cucumber::Rails::Database.before_non_js if Cucumber::Rails::Database.autorun_database_cleaner
19
+ end
17
20
 
18
- After do
19
- Cucumber::Rails::Database.after if Cucumber::Rails::Database.autorun_database_cleaner
20
- end
21
+ After do
22
+ Cucumber::Rails::Database.after if Cucumber::Rails::Database.autorun_database_cleaner
21
23
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Before('@allow-rescue') do
2
4
  @__orig_allow_rescue = ActionController::Base.allow_rescue
3
5
  ActionController::Base.allow_rescue = true
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'database_cleaner'
3
5
 
4
- Before('~@no-database-cleaner') do
6
+ Before('not @no-database-cleaner') do
5
7
  DatabaseCleaner.start if Cucumber::Rails::Database.autorun_database_cleaner
6
8
  end
7
9
 
8
- After('~@no-database-cleaner') do
10
+ After('not @no-database-cleaner') do
9
11
  DatabaseCleaner.clean if Cucumber::Rails::Database.autorun_database_cleaner
10
12
  end
11
-
12
- rescue LoadError => ignore_if_database_cleaner_not_present
13
+ rescue LoadError
14
+ Cucumber.logger.debug('database_cleaner gem not present.')
13
15
  end
@@ -1,5 +1,5 @@
1
- if defined?(ActionMailer::Base)
2
- Before do
3
- ActionMailer::Base.deliveries = []
4
- end
1
+ # frozen_string_literal: true
2
+
3
+ Before do
4
+ ActionMailer::Base.deliveries = []
5
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cucumber/rails/world'
2
4
 
3
5
  begin
@@ -8,7 +10,7 @@ begin
8
10
  include RSpec::Matchers
9
11
  end
10
12
  end
11
- rescue LoadError => try_rspec_1
13
+ rescue LoadError
12
14
  require 'spec/expectations'
13
15
  require 'spec/rails'
14
16
 
@@ -1,21 +1,38 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  # Try to load it so we can assign @_result below if needed.
3
5
  require 'test/unit/testresult'
4
- rescue LoadError => ignore
6
+ rescue LoadError
7
+ Cucumber.logger.debug('Minitest not found.')
8
+ end
9
+
10
+ module Cucumber
11
+ module Rails
12
+ class << self
13
+ def include_rack_test_helpers?
14
+ # Using ActiveModel Boolean casting here will give false positives more often than not!
15
+ !ENV['CR_REMOVE_RACK_TEST_HELPERS']&.casecmp('true')&.zero?
16
+ end
17
+ end
18
+ end
5
19
  end
6
20
 
7
- module Cucumber #:nodoc:
8
- module Rails #:nodoc:
9
- class World < ActionDispatch::IntegrationTest #:nodoc:
10
- include Rack::Test::Methods
21
+ module Cucumber
22
+ module Rails
23
+ class World < ::ActionDispatch::IntegrationTest
24
+ include Rack::Test::Methods if Cucumber::Rails.include_rack_test_helpers?
11
25
  include ActiveSupport::Testing::SetupAndTeardown if ActiveSupport::Testing.const_defined?('SetupAndTeardown')
12
26
 
13
- def initialize #:nodoc:
27
+ def initialize
14
28
  @_result = Test::Unit::TestResult.new if defined?(Test::Unit::TestResult)
15
29
  end
16
30
 
17
31
  unless defined?(ActiveRecord::Base)
18
- def self.fixture_table_names; []; end # Workaround for projects that don't use ActiveRecord
32
+ # Workaround for projects that don't use ActiveRecord
33
+ def self.fixture_table_names
34
+ []
35
+ end
19
36
  end
20
37
  end
21
38
  end
@@ -1,13 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rbconfig'
2
4
 
3
5
  module Cucumber
4
6
  class InstallGenerator < ::Rails::Generators::Base
5
- source_root File.expand_path('../templates', __FILE__)
7
+ source_root File.expand_path('templates', __dir__)
6
8
 
7
9
  DEFAULT_SHEBANG = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
8
10
 
9
- class_option :spork, type: :boolean, desc: 'Use Spork'
10
- class_option :skip_database, type: :boolean, desc: 'Skip modification of database.yml', aliases: '-D', default: false
11
+ class_option :spork,
12
+ type: :boolean,
13
+ desc: 'Use Spork'
14
+
15
+ class_option :skip_database,
16
+ type: :boolean,
17
+ desc: 'Skip modification of database.yml',
18
+ aliases: '-D',
19
+ default: false
11
20
 
12
21
  def create_templates
13
22
  template 'config/cucumber.yml.erb', 'config/cucumber.yml'
@@ -39,13 +48,13 @@ module Cucumber
39
48
 
40
49
  def create_database
41
50
  return unless File.exist?('config/database.yml')
42
- unless File.read('config/database.yml').include? 'cucumber:'
43
- gsub_file 'config/database.yml', /^test:.*\n/, "test: &test\n"
44
- gsub_file 'config/database.yml', /\z/, "\ncucumber:\n <<: *test\n"
51
+ return unless File.read('config/database.yml').include? 'cucumber:'
45
52
 
46
- # Since gsub_file doesn't ask the user, just inform user that the file was overwritten.
47
- puts ' force config/database.yml'
48
- end
53
+ gsub_file 'config/database.yml', /^test:.*\n/, "test: &test\n"
54
+ gsub_file 'config/database.yml', /\z/, "\ncucumber:\n <<: *test\n"
55
+
56
+ # Since gsub_file doesn't ask the user, just inform user that the file was overwritten.
57
+ puts ' force config/database.yml'
49
58
  end
50
59
 
51
60
  protected
@@ -60,7 +69,11 @@ module Cucumber
60
69
 
61
70
  def embed_template(source, indent = '')
62
71
  template = File.join(self.class.source_root, source)
63
- ERB.new(IO.read(template), nil, '-').result(binding).gsub(/^/, indent)
72
+ if RUBY_VERSION >= '2.6'
73
+ ERB.new(IO.read(template), trim_mode: '-').result(binding).gsub(/^/, indent)
74
+ else
75
+ ERB.new(IO.read(template), nil, '-').result(binding).gsub(/^/, indent)
76
+ end
64
77
  end
65
78
  end
66
79
  end