cucumber-rails 1.5.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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