cucumber 3.0.2 → 4.0.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 (145) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +216 -17
  3. data/CONTRIBUTING.md +4 -21
  4. data/README.md +8 -10
  5. data/bin/cucumber +1 -1
  6. data/lib/autotest/cucumber.rb +1 -0
  7. data/lib/autotest/cucumber_mixin.rb +35 -39
  8. data/lib/autotest/cucumber_rails.rb +1 -0
  9. data/lib/autotest/cucumber_rails_rspec.rb +1 -0
  10. data/lib/autotest/cucumber_rails_rspec2.rb +1 -0
  11. data/lib/autotest/cucumber_rspec.rb +1 -0
  12. data/lib/autotest/cucumber_rspec2.rb +1 -0
  13. data/lib/autotest/discover.rb +1 -0
  14. data/lib/cucumber.rb +2 -1
  15. data/lib/cucumber/cli/configuration.rb +6 -5
  16. data/lib/cucumber/cli/main.rb +14 -14
  17. data/lib/cucumber/cli/options.rb +113 -116
  18. data/lib/cucumber/cli/profile_loader.rb +50 -29
  19. data/lib/cucumber/cli/rerun_file.rb +1 -0
  20. data/lib/cucumber/configuration.rb +38 -29
  21. data/lib/cucumber/constantize.rb +8 -10
  22. data/lib/cucumber/core_ext/string.rb +1 -0
  23. data/lib/cucumber/deprecate.rb +32 -8
  24. data/lib/cucumber/encoding.rb +2 -1
  25. data/lib/cucumber/errors.rb +6 -7
  26. data/lib/cucumber/events.rb +14 -7
  27. data/lib/cucumber/events/envelope.rb +9 -0
  28. data/lib/cucumber/events/gherkin_source_parsed.rb +11 -0
  29. data/lib/cucumber/events/gherkin_source_read.rb +1 -4
  30. data/lib/cucumber/events/hook_test_step_created.rb +13 -0
  31. data/lib/cucumber/events/step_activated.rb +6 -6
  32. data/lib/cucumber/events/step_definition_registered.rb +4 -8
  33. data/lib/cucumber/events/test_case_created.rb +13 -0
  34. data/lib/cucumber/events/test_case_finished.rb +0 -4
  35. data/lib/cucumber/events/test_case_ready.rb +12 -0
  36. data/lib/cucumber/events/test_case_started.rb +0 -4
  37. data/lib/cucumber/events/test_run_finished.rb +2 -3
  38. data/lib/cucumber/events/test_run_started.rb +2 -4
  39. data/lib/cucumber/events/test_step_created.rb +13 -0
  40. data/lib/cucumber/events/test_step_finished.rb +0 -4
  41. data/lib/cucumber/events/test_step_started.rb +1 -5
  42. data/lib/cucumber/events/undefined_parameter_type.rb +10 -0
  43. data/lib/cucumber/file_specs.rb +7 -6
  44. data/lib/cucumber/filters.rb +2 -0
  45. data/lib/cucumber/filters/activate_steps.rb +6 -4
  46. data/lib/cucumber/filters/apply_after_hooks.rb +1 -0
  47. data/lib/cucumber/filters/apply_after_step_hooks.rb +1 -0
  48. data/lib/cucumber/filters/apply_around_hooks.rb +1 -0
  49. data/lib/cucumber/filters/apply_before_hooks.rb +1 -0
  50. data/lib/cucumber/filters/broadcast_test_case_ready_event.rb +12 -0
  51. data/lib/cucumber/filters/broadcast_test_run_started_event.rb +2 -1
  52. data/lib/cucumber/filters/gated_receiver.rb +1 -2
  53. data/lib/cucumber/filters/prepare_world.rb +6 -13
  54. data/lib/cucumber/filters/quit.rb +3 -6
  55. data/lib/cucumber/filters/randomizer.rb +6 -7
  56. data/lib/cucumber/filters/retry.rb +2 -2
  57. data/lib/cucumber/filters/tag_limits.rb +2 -2
  58. data/lib/cucumber/filters/tag_limits/test_case_index.rb +1 -2
  59. data/lib/cucumber/filters/tag_limits/verifier.rb +3 -6
  60. data/lib/cucumber/formatter/ansicolor.rb +33 -37
  61. data/lib/cucumber/formatter/ast_lookup.rb +165 -0
  62. data/lib/cucumber/formatter/backtrace_filter.rb +10 -10
  63. data/lib/cucumber/formatter/console.rb +65 -74
  64. data/lib/cucumber/formatter/console_counts.rb +4 -9
  65. data/lib/cucumber/formatter/console_issues.rb +9 -6
  66. data/lib/cucumber/formatter/duration.rb +2 -1
  67. data/lib/cucumber/formatter/duration_extractor.rb +4 -2
  68. data/lib/cucumber/formatter/errors.rb +6 -0
  69. data/lib/cucumber/formatter/fail_fast.rb +9 -6
  70. data/lib/cucumber/formatter/fanout.rb +3 -3
  71. data/lib/cucumber/formatter/html.rb +11 -602
  72. data/lib/cucumber/formatter/http_io.rb +146 -0
  73. data/lib/cucumber/formatter/ignore_missing_messages.rb +2 -3
  74. data/lib/cucumber/formatter/interceptor.rb +11 -18
  75. data/lib/cucumber/formatter/io.rb +18 -11
  76. data/lib/cucumber/formatter/json.rb +102 -109
  77. data/lib/cucumber/formatter/junit.rb +73 -68
  78. data/lib/cucumber/formatter/message.rb +22 -0
  79. data/lib/cucumber/formatter/message_builder.rb +255 -0
  80. data/lib/cucumber/formatter/pretty.rb +360 -153
  81. data/lib/cucumber/formatter/progress.rb +31 -32
  82. data/lib/cucumber/formatter/query/hook_by_test_step.rb +31 -0
  83. data/lib/cucumber/formatter/query/pickle_by_test.rb +26 -0
  84. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +26 -0
  85. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +40 -0
  86. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +40 -0
  87. data/lib/cucumber/formatter/rerun.rb +23 -4
  88. data/lib/cucumber/formatter/stepdefs.rb +2 -2
  89. data/lib/cucumber/formatter/steps.rb +4 -5
  90. data/lib/cucumber/formatter/summary.rb +17 -9
  91. data/lib/cucumber/formatter/unicode.rb +16 -18
  92. data/lib/cucumber/formatter/usage.rb +30 -26
  93. data/lib/cucumber/gherkin/data_table_parser.rb +18 -6
  94. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +83 -86
  95. data/lib/cucumber/gherkin/formatter/escaping.rb +13 -12
  96. data/lib/cucumber/gherkin/i18n.rb +1 -0
  97. data/lib/cucumber/gherkin/steps_parser.rb +18 -8
  98. data/lib/cucumber/glue/dsl.rb +2 -1
  99. data/lib/cucumber/glue/hook.rb +35 -11
  100. data/lib/cucumber/glue/invoke_in_world.rb +15 -20
  101. data/lib/cucumber/glue/proto_world.rb +47 -39
  102. data/lib/cucumber/glue/registry_and_more.rb +54 -23
  103. data/lib/cucumber/glue/snippet.rb +24 -27
  104. data/lib/cucumber/glue/step_definition.rb +51 -28
  105. data/lib/cucumber/glue/world_factory.rb +1 -3
  106. data/lib/cucumber/hooks.rb +24 -14
  107. data/lib/cucumber/load_path.rb +1 -0
  108. data/lib/cucumber/multiline_argument.rb +6 -8
  109. data/lib/cucumber/multiline_argument/data_table.rb +106 -73
  110. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +8 -11
  111. data/lib/cucumber/multiline_argument/doc_string.rb +2 -1
  112. data/lib/cucumber/platform.rb +4 -3
  113. data/lib/cucumber/project_initializer.rb +1 -1
  114. data/lib/cucumber/rake/task.rb +21 -18
  115. data/lib/cucumber/rspec/disable_option_parser.rb +10 -8
  116. data/lib/cucumber/rspec/doubles.rb +1 -0
  117. data/lib/cucumber/running_test_case.rb +4 -54
  118. data/lib/cucumber/runtime.rb +57 -61
  119. data/lib/cucumber/runtime/after_hooks.rb +9 -4
  120. data/lib/cucumber/runtime/before_hooks.rb +9 -4
  121. data/lib/cucumber/runtime/for_programming_languages.rb +12 -9
  122. data/lib/cucumber/runtime/step_hooks.rb +5 -2
  123. data/lib/cucumber/runtime/support_code.rb +16 -22
  124. data/lib/cucumber/runtime/user_interface.rb +8 -19
  125. data/lib/cucumber/step_definition_light.rb +6 -4
  126. data/lib/cucumber/step_definitions.rb +3 -2
  127. data/lib/cucumber/step_match.rb +20 -18
  128. data/lib/cucumber/step_match_search.rb +9 -9
  129. data/lib/cucumber/term/ansicolor.rb +39 -39
  130. data/lib/cucumber/unit.rb +1 -0
  131. data/lib/cucumber/version +1 -1
  132. data/lib/simplecov_setup.rb +1 -0
  133. metadata +214 -127
  134. data/lib/cucumber/formatter/cucumber.css +0 -286
  135. data/lib/cucumber/formatter/cucumber.sass +0 -247
  136. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -41
  137. data/lib/cucumber/formatter/html_builder.rb +0 -120
  138. data/lib/cucumber/formatter/inline-js.js +0 -30
  139. data/lib/cucumber/formatter/jquery-min.js +0 -154
  140. data/lib/cucumber/formatter/json_pretty.rb +0 -10
  141. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  142. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  143. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  144. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  145. data/lib/cucumber/step_argument.rb +0 -24
@@ -3,8 +3,7 @@
3
3
  module Cucumber
4
4
  module Glue
5
5
  module Snippet
6
-
7
- ARGUMENT_PATTERNS = ['"([^"]*)"', '(\d+)']
6
+ ARGUMENT_PATTERNS = ['"([^"]*)"', '(\d+)'].freeze
8
7
 
9
8
  class Generator
10
9
  def self.register_on(configuration)
@@ -26,7 +25,6 @@ module Cucumber
26
25
  end
27
26
 
28
27
  class BaseSnippet
29
-
30
28
  def initialize(cucumber_expression_generator, code_keyword, step_name, multiline_argument)
31
29
  @number_of_arguments = 0
32
30
  @code_keyword = code_keyword
@@ -44,7 +42,7 @@ module Cucumber
44
42
  end
45
43
 
46
44
  def self.cli_option_string(type, cucumber_expression_generator)
47
- format('%-7s: %-28s e.g. %s', type, description, example(cucumber_expression_generator))
45
+ format('%<type>-7s: %<description>-28s e.g. %<example>s', type: type, description: description, example: example(cucumber_expression_generator))
48
46
  end
49
47
 
50
48
  private
@@ -63,7 +61,7 @@ module Cucumber
63
61
  end
64
62
 
65
63
  def do_block
66
- do_block = String.new
64
+ do_block = String.new # rubocop:disable Style/EmptyLiteral
67
65
  do_block << "do#{parameters}\n"
68
66
  multiline_argument.append_comment_to(do_block)
69
67
  do_block << " pending # Write code here that turns the phrase above into concrete actions\n"
@@ -72,15 +70,18 @@ module Cucumber
72
70
  end
73
71
 
74
72
  def parameters
75
- block_args = (0...number_of_arguments).map { |n| "arg#{n+1}" }
73
+ block_args = (0...number_of_arguments).map { |n| "arg#{n + 1}" }
76
74
  multiline_argument.append_block_parameter_to(block_args)
77
- block_args.empty? ? '' : " |#{block_args.join(", ")}|"
75
+ block_args.empty? ? '' : " |#{block_args.join(', ')}|"
78
76
  end
79
77
 
80
- def self.example(cucumber_expression_generator)
81
- new(cucumber_expression_generator, 'Given', 'I have 2 cukes', MultilineArgument::None.new).step
82
- end
78
+ class << self
79
+ private
83
80
 
81
+ def example(cucumber_expression_generator)
82
+ new(cucumber_expression_generator, 'Given', 'I have 2 cukes', MultilineArgument::None.new).step
83
+ end
84
+ end
84
85
  end
85
86
 
86
87
  class CucumberExpression < BaseSnippet
@@ -90,11 +91,11 @@ module Cucumber
90
91
 
91
92
  def to_s
92
93
  header = generated_expressions.each_with_index.map do |expr, i|
93
- prefix = i == 0 ? '' : '# '
94
- "#{prefix}#{code_keyword}(\"#{expr.source}\") do#{parameters(expr)}"
94
+ prefix = i.zero? ? '' : '# '
95
+ "#{prefix}#{code_keyword}('#{expr.source}') do#{parameters(expr)}"
95
96
  end.join("\n")
96
97
 
97
- body = String.new
98
+ body = String.new # rubocop:disable Style/EmptyLiteral
98
99
  multiline_argument.append_comment_to(body)
99
100
  body << " pending # Write code here that turns the phrase above into concrete actions\n"
100
101
  body << 'end'
@@ -105,7 +106,7 @@ module Cucumber
105
106
  def parameters(expr)
106
107
  parameter_names = expr.parameter_names
107
108
  multiline_argument.append_block_parameter_to(parameter_names)
108
- parameter_names.empty? ? '' : " |#{parameter_names.join(", ")}|"
109
+ parameter_names.empty? ? '' : " |#{parameter_names.join(', ')}|"
109
110
  end
110
111
 
111
112
  def self.description
@@ -144,14 +145,13 @@ module Cucumber
144
145
  end
145
146
 
146
147
  SNIPPET_TYPES = {
147
- :cucumber_expression => CucumberExpression,
148
- :regexp => Regexp,
149
- :classic => Classic,
150
- :percent => Percent
151
- }
148
+ cucumber_expression: CucumberExpression,
149
+ regexp: Regexp,
150
+ classic: Classic,
151
+ percent: Percent
152
+ }.freeze
152
153
 
153
154
  module MultilineArgumentSnippet
154
-
155
155
  def self.new(multiline_argument)
156
156
  builder = Builder.new
157
157
  multiline_argument.describe_to(builder)
@@ -174,11 +174,10 @@ module Cucumber
174
174
 
175
175
  class DocString
176
176
  def append_block_parameter_to(array)
177
- array << 'string'
177
+ array << 'doc_string'
178
178
  end
179
179
 
180
- def append_comment_to(string)
181
- end
180
+ def append_comment_to(string); end
182
181
  end
183
182
 
184
183
  class DataTable
@@ -196,11 +195,9 @@ module Cucumber
196
195
  end
197
196
 
198
197
  class None
199
- def append_block_parameter_to(array)
200
- end
198
+ def append_block_parameter_to(array); end
201
199
 
202
- def append_comment_to(string)
203
- end
200
+ def append_comment_to(string); end
204
201
  end
205
202
  end
206
203
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'cucumber/step_match'
3
- require 'cucumber/step_argument'
4
4
  require 'cucumber/core_ext/string'
5
5
  require 'cucumber/glue/invoke_in_world'
6
6
 
@@ -17,7 +17,6 @@ module Cucumber
17
17
  # end
18
18
  #
19
19
  class StepDefinition
20
-
21
20
  class MissingProc < StandardError
22
21
  def message
23
22
  'Step definitions must always have a proc or symbol'
@@ -25,9 +24,9 @@ module Cucumber
25
24
  end
26
25
 
27
26
  class << self
28
- def new(registry, string_or_regexp, proc_or_sym, options)
27
+ def new(id, registry, string_or_regexp, proc_or_sym, options)
29
28
  raise MissingProc if proc_or_sym.nil?
30
- super registry, registry.create_expression(string_or_regexp), create_proc(proc_or_sym, options)
29
+ super id, registry, registry.create_expression(string_or_regexp), create_proc(proc_or_sym, options)
31
30
  end
32
31
 
33
32
  private
@@ -44,34 +43,60 @@ module Cucumber
44
43
  end
45
44
 
46
45
  def patch_location_onto(block)
47
- location = Core::Ast::Location.of_caller(5)
46
+ location = Core::Test::Location.of_caller(5)
48
47
  block.define_singleton_method(:source_location) { [location.file, location.line] }
49
48
  block
50
49
  end
51
50
 
52
51
  def parse_target_proc_from(options)
53
- return lambda { self } unless options.key?(:on)
52
+ return -> { self } unless options.key?(:on)
54
53
  target = options[:on]
55
54
  case target
56
55
  when Proc
57
56
  target
58
57
  when Symbol
59
- lambda { self.send(target) }
58
+ -> { send(target) }
60
59
  else
61
- lambda { raise ArgumentError, 'Target must be a symbol or a proc' }
60
+ -> { raise ArgumentError, 'Target must be a symbol or a proc' }
62
61
  end
63
62
  end
64
63
  end
65
64
 
66
- attr_reader :expression
65
+ attr_reader :id, :expression, :registry
67
66
 
68
- def initialize(registry, expression, proc)
67
+ def initialize(id, registry, expression, proc)
69
68
  raise 'No regexp' if expression.is_a?(Regexp)
70
- @registry, @expression, @proc = registry, expression, proc
69
+ @id = id
70
+ @registry = registry
71
+ @expression = expression
72
+ @proc = proc
71
73
  # @registry.available_step_definition(regexp_source, location)
72
74
  end
73
75
 
74
- # @api private
76
+ def to_envelope
77
+ Cucumber::Messages::Envelope.new(
78
+ step_definition: Cucumber::Messages::StepDefinition.new(
79
+ id: id,
80
+ pattern: Cucumber::Messages::StepDefinition::StepDefinitionPattern.new(
81
+ source: expression.source.to_s,
82
+ type: expression_type
83
+ ),
84
+ source_reference: Cucumber::Messages::SourceReference.new(
85
+ uri: location.file,
86
+ location: Cucumber::Messages::Location.new(
87
+ line: location.lines.first
88
+ )
89
+ )
90
+ )
91
+ )
92
+ end
93
+
94
+ def expression_type
95
+ return Cucumber::Messages::StepDefinition::StepDefinitionPattern::StepDefinitionPatternType::CUCUMBER_EXPRESSION if expression.is_a?(CucumberExpressions::CucumberExpression)
96
+ Cucumber::Messages::StepDefinition::StepDefinitionPattern::StepDefinitionPatternType::REGULAR_EXPRESSION
97
+ end
98
+
99
+ # @api private
75
100
  def to_hash
76
101
  type = expression.is_a?(CucumberExpressions::RegularExpression) ? 'regular expression' : 'cucumber expression'
77
102
  regexp = expression.regexp
@@ -91,35 +116,33 @@ module Cucumber
91
116
  }
92
117
  end
93
118
 
94
- # @api private
95
- def ==(step_definition)
96
- expression.source == step_definition.expression.source
119
+ # @api private
120
+ def ==(other)
121
+ expression.source == other.expression.source
97
122
  end
98
123
 
99
- # @api private
124
+ # @api private
100
125
  def arguments_from(step_name)
101
126
  args = @expression.match(step_name)
102
127
  # @registry.invoked_step_definition(regexp_source, location) if args
103
128
  args
104
129
  end
105
130
 
106
- # @api private
131
+ # @api private
107
132
  # TODO: inline this and step definition just be a value object
108
133
  def invoke(args)
109
- begin
110
- InvokeInWorld.cucumber_instance_exec_in(@registry.current_world, true, @expression.to_s, *args, &@proc)
111
- rescue ArityMismatchError => e
112
- e.backtrace.unshift(self.backtrace_line)
113
- raise e
114
- end
134
+ InvokeInWorld.cucumber_instance_exec_in(@registry.current_world, true, @expression.to_s, *args, &@proc)
135
+ rescue ArityMismatchError => e
136
+ e.backtrace.unshift(backtrace_line)
137
+ raise e
115
138
  end
116
139
 
117
- # @api private
140
+ # @api private
118
141
  def backtrace_line
119
142
  "#{location}:in `#{@expression}'"
120
143
  end
121
144
 
122
- # @api private
145
+ # @api private
123
146
  def file_colon_line
124
147
  case @proc
125
148
  when Proc
@@ -129,12 +152,12 @@ module Cucumber
129
152
  end
130
153
  end
131
154
 
132
- # The source location where the step defintion can be found
155
+ # The source location where the step definition can be found
133
156
  def location
134
- @location ||= Cucumber::Core::Ast::Location.from_source_location(*@proc.source_location)
157
+ @location ||= Cucumber::Core::Test::Location.from_source_location(*@proc.source_location)
135
158
  end
136
159
 
137
- # @api private
160
+ # @api private
138
161
  def file
139
162
  @file ||= location.file
140
163
  end
@@ -1,6 +1,5 @@
1
1
  module Cucumber
2
2
  module Glue
3
-
4
3
  class WorldFactory
5
4
  def initialize(proc)
6
5
  @proc = proc || -> { Object.new }
@@ -11,13 +10,12 @@ module Cucumber
11
10
  end
12
11
 
13
12
  def raise_nil_world
14
- raise NilWorld.new
13
+ raise NilWorld
15
14
  rescue NilWorld => e
16
15
  e.backtrace.clear
17
16
  e.backtrace.push(Glue.backtrace_line(@proc, 'World'))
18
17
  raise e
19
18
  end
20
19
  end
21
-
22
20
  end
23
21
  end
@@ -1,38 +1,37 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'pathname'
3
- require 'cucumber/core/ast/location'
4
+ require 'cucumber/core/test/location'
4
5
  require 'cucumber/core/test/around_hook'
5
6
 
6
7
  module Cucumber
7
-
8
8
  # Hooks quack enough like `Cucumber::Core::Ast` source nodes that we can use them as
9
9
  # source for test steps
10
10
  module Hooks
11
-
12
11
  class << self
13
- def before_hook(source, location, &block)
14
- build_hook_step(source, location, block, BeforeHook, Core::Test::UnskippableAction)
12
+ def before_hook(id, location, &block)
13
+ build_hook_step(id, location, block, BeforeHook, Core::Test::UnskippableAction)
15
14
  end
16
15
 
17
- def after_hook(source, location, &block)
18
- build_hook_step(source, location, block, AfterHook, Core::Test::UnskippableAction)
16
+ def after_hook(id, location, &block)
17
+ build_hook_step(id, location, block, AfterHook, Core::Test::UnskippableAction)
19
18
  end
20
19
 
21
- def after_step_hook(source, location, &block)
22
- raise ArgumentError unless source.last.is_a?(Core::Ast::Step)
23
- build_hook_step(source, location, block, AfterStepHook, Core::Test::Action)
20
+ def after_step_hook(id, test_step, location, &block)
21
+ raise ArgumentError if test_step.hook?
22
+ build_hook_step(id, location, block, AfterStepHook, Core::Test::Action)
24
23
  end
25
24
 
26
- def around_hook(_source, &block)
25
+ def around_hook(&block)
27
26
  Core::Test::AroundHook.new(&block)
28
27
  end
29
28
 
30
29
  private
31
30
 
32
- def build_hook_step(source, location, block, hook_type, action_type)
31
+ def build_hook_step(id, location, block, hook_type, action_type)
33
32
  action = action_type.new(location, &block)
34
33
  hook = hook_type.new(action.location)
35
- Core::Test::Step.new(source + [hook], action)
34
+ Core::Test::HookStep.new(id, hook.text, location, action)
36
35
  end
37
36
  end
38
37
 
@@ -47,6 +46,10 @@ module Cucumber
47
46
  'After hook'
48
47
  end
49
48
 
49
+ def to_s
50
+ "#{text} at #{location}"
51
+ end
52
+
50
53
  def match_locations?(queried_locations)
51
54
  queried_locations.any? { |other_location| other_location.match?(location) }
52
55
  end
@@ -67,6 +70,10 @@ module Cucumber
67
70
  'Before hook'
68
71
  end
69
72
 
73
+ def to_s
74
+ "#{text} at #{location}"
75
+ end
76
+
70
77
  def match_locations?(queried_locations)
71
78
  queried_locations.any? { |other_location| other_location.match?(location) }
72
79
  end
@@ -87,6 +94,10 @@ module Cucumber
87
94
  'AfterStep hook'
88
95
  end
89
96
 
97
+ def to_s
98
+ "#{text} at #{location}"
99
+ end
100
+
90
101
  def match_locations?(queried_locations)
91
102
  queried_locations.any? { |other_location| other_location.match?(location) }
92
103
  end
@@ -95,6 +106,5 @@ module Cucumber
95
106
  visitor.after_step_hook(self, *args)
96
107
  end
97
108
  end
98
-
99
109
  end
100
110
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Cucumber
3
4
  module LoadPath
4
5
  def add_dirs(*dirs)
@@ -1,21 +1,21 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'delegate'
3
4
  require 'cucumber/multiline_argument/data_table'
4
5
  require 'cucumber/multiline_argument/doc_string'
5
6
 
6
7
  module Cucumber
7
8
  module MultilineArgument
8
-
9
9
  class << self
10
10
  def from_core(node)
11
11
  builder.wrap(node)
12
12
  end
13
13
 
14
- def from(argument, location=nil, content_type=nil)
15
- location ||= Core::Ast::Location.of_caller
14
+ def from(argument, location = nil, content_type = nil)
15
+ location ||= Core::Test::Location.of_caller
16
16
  case argument
17
17
  when String
18
- builder.doc_string(Core::Ast::DocString.new(argument, content_type, location))
18
+ builder.doc_string(Core::Test::DocString.new(argument, content_type))
19
19
  when Array
20
20
  location = location.on_line(argument.first.line..argument.last.line)
21
21
  builder.data_table(argument.map(&:cells), location)
@@ -52,11 +52,9 @@ module Cucumber
52
52
  end
53
53
 
54
54
  class None
55
- def append_to(array)
56
- end
55
+ def append_to(array); end
57
56
 
58
- def describe_to(visitor)
59
- end
57
+ def describe_to(visitor); end
60
58
  end
61
59
  end
62
60
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'forwardable'
3
4
  require 'cucumber/gherkin/data_table_parser'
4
5
  require 'cucumber/gherkin/formatter/escaping'
5
- require 'cucumber/core/ast/describes_itself'
6
6
  require 'cucumber/multiline_argument/data_table/diff_matrices'
7
7
 
8
8
  module Cucumber
@@ -27,19 +27,21 @@ module Cucumber
27
27
  # This will store <tt>[['a', 'b'], ['c', 'd']]</tt> in the <tt>data</tt> variable.
28
28
  #
29
29
  class DataTable
30
- include Core::Ast::DescribesItself
31
-
32
30
  def self.default_arg_name #:nodoc:
33
31
  'table'
34
32
  end
35
33
 
34
+ def describe_to(visitor, *args)
35
+ visitor.legacy_table(self, *args)
36
+ end
37
+
36
38
  class << self
37
- def from(data, location = Core::Ast::Location.of_caller)
39
+ def from(data)
38
40
  case data
39
41
  when Array
40
- from_array(data, location)
42
+ from_array(data)
41
43
  when String
42
- parse(data, location)
44
+ parse(data)
43
45
  else
44
46
  raise ArgumentError, 'expected data to be a String or an Array.'
45
47
  end
@@ -47,15 +49,15 @@ module Cucumber
47
49
 
48
50
  private
49
51
 
50
- def parse(text, location = Core::Ast::Location.of_caller)
52
+ def parse(text)
51
53
  builder = Builder.new
52
54
  parser = Cucumber::Gherkin::DataTableParser.new(builder)
53
55
  parser.parse(text)
54
- from_array(builder.rows, location)
56
+ from_array(builder.rows)
55
57
  end
56
58
 
57
- def from_array(data, location = Core::Ast::Location.of_caller)
58
- new Core::Ast::DataTable.new(data, location)
59
+ def from_array(data)
60
+ new Core::Test::DataTable.new(data)
59
61
  end
60
62
  end
61
63
 
@@ -70,19 +72,17 @@ module Cucumber
70
72
  @rows << row
71
73
  end
72
74
 
73
- def eof
74
- end
75
+ def eof; end
75
76
  end
76
77
 
78
+ NULL_CONVERSIONS = Hash.new(strict: false, proc: ->(cell_value) { cell_value }).freeze
77
79
 
78
- NULL_CONVERSIONS = Hash.new({ :strict => false, :proc => lambda{ |cell_value| cell_value } }).freeze
79
-
80
- # @param data [Core::Ast::DataTable] the data for the table
80
+ # @param data [Core::Test::DataTable] the data for the table
81
81
  # @param conversion_procs [Hash] see map_columns!
82
82
  # @param header_mappings [Hash] see map_headers!
83
83
  # @param header_conversion_proc [Proc] see map_headers!
84
84
  def initialize(data, conversion_procs = NULL_CONVERSIONS.dup, header_mappings = {}, header_conversion_proc = nil)
85
- raise ArgumentError, 'data must be a Core::Ast::DataTable' unless data.is_a? Core::Ast::DataTable
85
+ raise ArgumentError, 'data must be a Core::Test::DataTable' unless data.is_a? Core::Test::DataTable
86
86
  ast_table = data
87
87
  # Verify that it's square
88
88
  ast_table.transpose
@@ -111,7 +111,7 @@ module Cucumber
111
111
  # registered with #map_column! and #map_headers!.
112
112
  #
113
113
  def dup
114
- self.class.new(Core::Ast::DataTable.new(raw, location), @conversion_procs.dup, @header_mappings.dup, @header_conversion_proc)
114
+ self.class.new(Core::Test::DataTable.new(raw), @conversion_procs.dup, @header_mappings.dup, @header_conversion_proc)
115
115
  end
116
116
 
117
117
  # Returns a new, transposed table. Example:
@@ -126,7 +126,7 @@ module Cucumber
126
126
  # | 4 | 2 |
127
127
  #
128
128
  def transpose
129
- self.class.new(Core::Ast::DataTable.new(raw.transpose, location), @conversion_procs.dup, @header_mappings.dup, @header_conversion_proc)
129
+ self.class.new(Core::Test::DataTable.new(raw.transpose), @conversion_procs.dup, @header_mappings.dup, @header_conversion_proc)
130
130
  end
131
131
 
132
132
  # Converts this table into an Array of Hash where the keys of each
@@ -160,8 +160,8 @@ module Cucumber
160
160
  #
161
161
  def symbolic_hashes
162
162
  @symbolic_hashes ||=
163
- self.hashes.map do |string_hash|
164
- Hash[string_hash.map{ |a,b| [symbolize_key(a), b] }]
163
+ hashes.map do |string_hash|
164
+ Hash[string_hash.map { |a, b| [symbolize_key(a), b] }]
165
165
  end
166
166
  end
167
167
 
@@ -180,7 +180,7 @@ module Cucumber
180
180
  def rows_hash
181
181
  return @rows_hash if @rows_hash
182
182
  verify_table_width(2)
183
- @rows_hash = self.transpose.hashes[0]
183
+ @rows_hash = transpose.hashes[0]
184
184
  end
185
185
 
186
186
  # Gets the raw data of this table. For example, a Table built from
@@ -200,7 +200,7 @@ module Cucumber
200
200
  end
201
201
 
202
202
  def column_names #:nodoc:
203
- @col_names ||= cell_matrix[0].map(&:value)
203
+ @column_names ||= cell_matrix[0].map(&:value)
204
204
  end
205
205
 
206
206
  def rows
@@ -260,7 +260,7 @@ module Cucumber
260
260
  # table.hashes.keys
261
261
  # # => ['phone number', 'ADDRESS']
262
262
  #
263
- def map_headers!(mappings={}, &block)
263
+ def map_headers!(mappings = {}, &block)
264
264
  # TODO: Remove this method for 2.0
265
265
  clear_cache!
266
266
  @header_mappings = mappings
@@ -268,8 +268,8 @@ module Cucumber
268
268
  end
269
269
 
270
270
  # Returns a new Table where the headers are redefined. See #map_headers!
271
- def map_headers(mappings={}, &block)
272
- self.class.new(Core::Ast::DataTable.new(raw, location), @conversion_procs.dup, mappings, block)
271
+ def map_headers(mappings = {}, &block)
272
+ self.class.new(Core::Test::DataTable.new(raw), @conversion_procs.dup, mappings, block)
273
273
  end
274
274
 
275
275
  # Change how #hashes converts column values. The +column_name+ argument identifies the column
@@ -284,17 +284,17 @@ module Cucumber
284
284
  # end
285
285
  # end
286
286
  #
287
- def map_column!(column_name, strict=true, &conversion_proc)
287
+ def map_column!(column_name, strict = true, &conversion_proc)
288
288
  # TODO: Remove this method for 2.0
289
- @conversion_procs[column_name.to_s] = { :strict => strict, :proc => conversion_proc }
289
+ @conversion_procs[column_name.to_s] = { strict: strict, proc: conversion_proc }
290
290
  self
291
291
  end
292
292
 
293
293
  # Returns a new Table with an additional column mapping. See #map_column!
294
- def map_column(column_name, strict=true, &conversion_proc)
294
+ def map_column(column_name, strict = true, &conversion_proc)
295
295
  conversion_procs = @conversion_procs.dup
296
- conversion_procs[column_name.to_s] = { :strict => strict, :proc => conversion_proc }
297
- self.class.new(Core::Ast::DataTable.new(raw, location), conversion_procs, @header_mappings.dup, @header_conversion_proc)
296
+ conversion_procs[column_name.to_s] = { strict: strict, proc: conversion_proc }
297
+ self.class.new(Core::Test::DataTable.new(raw), conversion_procs, @header_mappings.dup, @header_conversion_proc)
298
298
  end
299
299
 
300
300
  # Compares +other_table+ to self. If +other_table+ contains columns
@@ -333,7 +333,7 @@ module Cucumber
333
333
  # Calling this method is particularly useful in <tt>Then</tt> steps that take
334
334
  # a Table argument, if you want to compare that table to some actual values.
335
335
  #
336
- def diff!(other_table, options={})
336
+ def diff!(other_table, options = {})
337
337
  other_table = ensure_table(other_table)
338
338
  other_table.convert_headers!
339
339
  other_table.convert_columns!
@@ -352,9 +352,13 @@ module Cucumber
352
352
  end
353
353
  end
354
354
 
355
- def to_hash(cells) #:nodoc:
356
- hash = Hash.new do |hash, key|
357
- hash[key.to_s] if key.is_a?(Symbol)
355
+ def to_hash
356
+ cells_rows.map { |cells| cells.map(&:value) }
357
+ end
358
+
359
+ def cells_to_hash(cells) #:nodoc:
360
+ hash = Hash.new do |hash_inner, key|
361
+ hash_inner[key.to_s] if key.is_a?(Symbol)
358
362
  end
359
363
  column_names.each_with_index do |column_name, column_index|
360
364
  hash[column_name] = cells.value(column_index)
@@ -367,19 +371,21 @@ module Cucumber
367
371
  end
368
372
 
369
373
  def verify_column(column_name) #:nodoc:
370
- raise %{The column named "#{column_name}" does not exist} unless raw[0].include?(column_name)
374
+ raise %(The column named "#{column_name}" does not exist) unless raw[0].include?(column_name)
371
375
  end
372
376
 
373
377
  def verify_table_width(width) #:nodoc:
374
- raise %{The table must have exactly #{width} columns} unless raw[0].size == width
378
+ raise %(The table must have exactly #{width} columns) unless raw[0].size == width
375
379
  end
376
380
 
377
- def has_text?(text) #:nodoc:
378
- raw.flatten.compact.detect{|cell_value| cell_value.index(text)}
381
+ # TODO: remove the below function if it's not actually being used.
382
+ # Nothing else in this repo calls it.
383
+ def text?(text) #:nodoc:
384
+ raw.flatten.compact.detect { |cell_value| cell_value.index(text) }
379
385
  end
380
386
 
381
387
  def cells_rows #:nodoc:
382
- @rows ||= cell_matrix.map do |cell_row|
388
+ @rows ||= cell_matrix.map do |cell_row| # rubocop:disable Naming/MemoizedInstanceVariableName
383
389
  Cells.new(self, cell_row)
384
390
  end
385
391
  end
@@ -399,26 +405,48 @@ module Cucumber
399
405
  end
400
406
 
401
407
  def to_s(options = {}) #:nodoc:
402
- # TODO: factor out this code so we don't depend on such a big lump of old cruft
403
- require 'cucumber/formatter/pretty'
404
- require 'cucumber/formatter/legacy_api/adapter'
405
- options = {:color => true, :indent => 2, :prefixes => TO_S_PREFIXES}.merge(options)
406
- io = StringIO.new
407
-
408
- c = Cucumber::Term::ANSIColor.coloring?
409
- Cucumber::Term::ANSIColor.coloring = options[:color]
410
- runtime = Struct.new(:configuration).new(Configuration.new)
411
- formatter = Formatter::Pretty.new(runtime, io, options)
412
- formatter.instance_variable_set('@indent', options[:indent])
413
- Formatter::LegacyApi::Ast::MultilineArg.for(self).accept(Formatter::Fanout.new([formatter]))
414
- Cucumber::Term::ANSIColor.coloring = c
415
- io.rewind
416
- s = "\n" + io.read + (' ' * (options[:indent] - 2))
417
- s
408
+ indentation = options.key?(:indent) ? options[:indent] : 2
409
+ prefixes = options.key?(:prefixes) ? options[:prefixes] : TO_S_PREFIXES
410
+ DataTablePrinter.new(self, indentation, prefixes).to_s
418
411
  end
419
412
 
420
- def description_for_visitors
421
- :legacy_table
413
+ class DataTablePrinter #:nodoc:
414
+ include Cucumber::Gherkin::Formatter::Escaping
415
+ attr_reader :data_table, :indentation, :prefixes
416
+ private :data_table, :indentation, :prefixes
417
+
418
+ def initialize(data_table, indentation, prefixes)
419
+ @data_table = data_table
420
+ @indentation = indentation
421
+ @prefixes = prefixes
422
+ end
423
+
424
+ def to_s
425
+ leading_row = "\n"
426
+ end_indentation = indentation - 2
427
+ trailing_row = "\n" + (' ' * end_indentation)
428
+ table_rows = data_table.cell_matrix.map { |row| format_row(row) }
429
+ leading_row + table_rows.join("\n") + trailing_row
430
+ end
431
+
432
+ private
433
+
434
+ def format_row(row)
435
+ row_start = (' ' * indentation) + '| '
436
+ row_end = '|'
437
+ cells = row.map.with_index do |cell, i|
438
+ format_cell(cell, data_table.col_width(i))
439
+ end
440
+ row_start + cells.join('| ') + row_end
441
+ end
442
+
443
+ def format_cell(cell, col_width)
444
+ cell_text = escape_cell(cell.value.to_s)
445
+ cell_text_width = cell_text.unpack('U*').length
446
+ padded_text = cell_text + (' ' * (col_width - cell_text_width))
447
+ prefix = prefixes[cell.status]
448
+ "#{prefix}#{padded_text} "
449
+ end
422
450
  end
423
451
 
424
452
  def columns #:nodoc:
@@ -446,7 +474,11 @@ module Cucumber
446
474
 
447
475
  def create_cell_matrix(ast_table) #:nodoc:
448
476
  ast_table.raw.map do |raw_row|
449
- line = raw_row.line rescue -1
477
+ line = begin
478
+ raw_row.line
479
+ rescue StandardError
480
+ -1
481
+ end
450
482
  raw_row.map do |raw_cell|
451
483
  Cell.new(raw_cell, self, line)
452
484
  end
@@ -476,22 +508,20 @@ module Cucumber
476
508
  end
477
509
 
478
510
  @header_mappings.each_pair do |pre, post|
479
- mapped_cells = header_cells.select { |cell| pre === cell.value }
511
+ mapped_cells = header_cells.reject { |cell| cell.value.match(pre).nil? }
480
512
  raise "No headers matched #{pre.inspect}" if mapped_cells.empty?
481
513
  raise "#{mapped_cells.length} headers matched #{pre.inspect}: #{mapped_cells.map(&:value).inspect}" if mapped_cells.length > 1
482
514
  mapped_cells[0].value = post
483
- if @conversion_procs.key?(pre)
484
- @conversion_procs[post] = @conversion_procs.delete(pre)
485
- end
515
+ @conversion_procs[post] = @conversion_procs.delete(pre) if @conversion_procs.key?(pre)
486
516
  end
487
517
  end
488
518
 
489
519
  def clear_cache! #:nodoc:
490
- @hashes = @rows_hash = @col_names = @rows = @columns = nil
520
+ @hashes = @rows_hash = @column_names = @rows = @columns = nil
491
521
  end
492
522
 
493
523
  def ensure_table(table_or_array) #:nodoc:
494
- return table_or_array if DataTable === table_or_array
524
+ return table_or_array if DataTable == table_or_array.class
495
525
  DataTable.from(table_or_array)
496
526
  end
497
527
 
@@ -507,7 +537,8 @@ module Cucumber
507
537
  attr_reader :exception
508
538
 
509
539
  def initialize(table, cells)
510
- @table, @cells = table, cells
540
+ @table = table
541
+ @cells = cells
511
542
  end
512
543
 
513
544
  def accept(visitor)
@@ -524,7 +555,7 @@ module Cucumber
524
555
  end
525
556
 
526
557
  def to_hash #:nodoc:
527
- @to_hash ||= @table.to_hash(self)
558
+ @to_hash ||= @table.cells_to_hash(self)
528
559
  end
529
560
 
530
561
  def value(n) #:nodoc:
@@ -554,7 +585,7 @@ module Cucumber
554
585
  end
555
586
 
556
587
  def width
557
- map{|cell| cell.value ? escape_cell(cell.value.to_s).unpack('U*').length : 0}.max
588
+ map { |cell| cell.value ? escape_cell(cell.value.to_s).unpack('U*').length : 0 }.max
558
589
  end
559
590
  end
560
591
 
@@ -563,19 +594,21 @@ module Cucumber
563
594
  attr_accessor :status, :value
564
595
 
565
596
  def initialize(value, table, line)
566
- @value, @table, @line = value, table, line
597
+ @value = value
598
+ @table = table
599
+ @line = line
567
600
  end
568
601
 
569
602
  def inspect!
570
603
  @value = "(i) #{value.inspect}"
571
604
  end
572
605
 
573
- def ==(o)
574
- SurplusCell === o || value == o.value
606
+ def ==(other)
607
+ SurplusCell == other.class || value == other.value
575
608
  end
576
609
 
577
- def eql?(o)
578
- self == o
610
+ def eql?(other)
611
+ self == other
579
612
  end
580
613
 
581
614
  def hash
@@ -593,7 +626,7 @@ module Cucumber
593
626
  :comment
594
627
  end
595
628
 
596
- def ==(_o)
629
+ def ==(_other)
597
630
  true
598
631
  end
599
632