cucumber-core 1.5.0 → 2.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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +48 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +39 -0
  4. data/.travis.yml +2 -3
  5. data/HISTORY.md +12 -1
  6. data/README.md +37 -43
  7. data/cucumber-core.gemspec +2 -0
  8. data/lib/cucumber/core.rb +5 -2
  9. data/lib/cucumber/core/ast.rb +1 -0
  10. data/lib/cucumber/core/ast/background.rb +1 -0
  11. data/lib/cucumber/core/ast/comment.rb +1 -0
  12. data/lib/cucumber/core/ast/data_table.rb +1 -0
  13. data/lib/cucumber/core/ast/describes_itself.rb +1 -0
  14. data/lib/cucumber/core/ast/doc_string.rb +1 -0
  15. data/lib/cucumber/core/ast/empty_background.rb +1 -0
  16. data/lib/cucumber/core/ast/empty_multiline_argument.rb +1 -0
  17. data/lib/cucumber/core/ast/examples_table.rb +1 -0
  18. data/lib/cucumber/core/ast/feature.rb +1 -0
  19. data/lib/cucumber/core/ast/location.rb +1 -0
  20. data/lib/cucumber/core/ast/names.rb +1 -0
  21. data/lib/cucumber/core/ast/outline_step.rb +1 -0
  22. data/lib/cucumber/core/ast/scenario.rb +1 -0
  23. data/lib/cucumber/core/ast/scenario_outline.rb +1 -0
  24. data/lib/cucumber/core/ast/step.rb +1 -0
  25. data/lib/cucumber/core/ast/tag.rb +1 -0
  26. data/lib/cucumber/core/compiler.rb +1 -0
  27. data/lib/cucumber/core/event.rb +63 -0
  28. data/lib/cucumber/core/event_bus.rb +64 -0
  29. data/lib/cucumber/core/events.rb +69 -0
  30. data/lib/cucumber/core/filter.rb +1 -0
  31. data/lib/cucumber/core/gherkin/ast_builder.rb +14 -5
  32. data/lib/cucumber/core/gherkin/document.rb +6 -4
  33. data/lib/cucumber/core/gherkin/parser.rb +6 -4
  34. data/lib/cucumber/core/gherkin/tag_expression.rb +1 -0
  35. data/lib/cucumber/core/gherkin/writer.rb +1 -0
  36. data/lib/cucumber/core/gherkin/writer/helpers.rb +1 -0
  37. data/lib/cucumber/core/platform.rb +1 -0
  38. data/lib/cucumber/core/report/summary.rb +30 -6
  39. data/lib/cucumber/core/test/action.rb +1 -0
  40. data/lib/cucumber/core/test/around_hook.rb +1 -0
  41. data/lib/cucumber/core/test/case.rb +13 -0
  42. data/lib/cucumber/core/test/filters.rb +1 -0
  43. data/lib/cucumber/core/test/filters/locations_filter.rb +1 -0
  44. data/lib/cucumber/core/test/filters/name_filter.rb +1 -0
  45. data/lib/cucumber/core/test/filters/tag_filter.rb +1 -0
  46. data/lib/cucumber/core/test/result.rb +5 -0
  47. data/lib/cucumber/core/test/runner.rb +10 -10
  48. data/lib/cucumber/core/test/step.rb +1 -0
  49. data/lib/cucumber/core/test/timer.rb +1 -0
  50. data/lib/cucumber/core/version.rb +2 -1
  51. data/spec/capture_warnings.rb +3 -2
  52. data/spec/coverage.rb +1 -0
  53. data/spec/cucumber/core/ast/background_spec.rb +1 -0
  54. data/spec/cucumber/core/ast/data_table_spec.rb +1 -0
  55. data/spec/cucumber/core/ast/doc_string_spec.rb +2 -1
  56. data/spec/cucumber/core/ast/empty_multiline_argument_spec.rb +1 -0
  57. data/spec/cucumber/core/ast/examples_table_spec.rb +1 -0
  58. data/spec/cucumber/core/ast/location_spec.rb +1 -0
  59. data/spec/cucumber/core/ast/outline_step_spec.rb +1 -0
  60. data/spec/cucumber/core/ast/step_spec.rb +1 -0
  61. data/spec/cucumber/core/compiler_spec.rb +1 -0
  62. data/spec/cucumber/core/event_bus_spec.rb +151 -0
  63. data/spec/cucumber/core/event_spec.rb +40 -0
  64. data/spec/cucumber/core/filter_spec.rb +1 -0
  65. data/spec/cucumber/core/gherkin/parser_spec.rb +24 -0
  66. data/spec/cucumber/core/gherkin/writer_spec.rb +1 -0
  67. data/spec/cucumber/core/report/summary_spec.rb +127 -0
  68. data/spec/cucumber/core/test/action_spec.rb +1 -0
  69. data/spec/cucumber/core/test/case_spec.rb +26 -2
  70. data/spec/cucumber/core/test/duration_matcher.rb +2 -1
  71. data/spec/cucumber/core/test/filters/locations_filter_spec.rb +2 -0
  72. data/spec/cucumber/core/test/result_spec.rb +5 -0
  73. data/spec/cucumber/core/test/runner_spec.rb +44 -50
  74. data/spec/cucumber/core/test/step_spec.rb +1 -0
  75. data/spec/cucumber/core/test/timer_spec.rb +1 -0
  76. data/spec/cucumber/core_spec.rb +68 -8
  77. data/spec/readme_spec.rb +1 -0
  78. data/spec/report_api_spy.rb +1 -0
  79. metadata +42 -3
@@ -0,0 +1,64 @@
1
+ require 'cucumber/core/events'
2
+
3
+ module Cucumber
4
+ module Core
5
+
6
+ # Event Bus
7
+ #
8
+ # Implements an in-process pub-sub event broadcaster allowing multiple observers
9
+ # to subscribe to events that fire as your tests are executed.
10
+ #
11
+ class EventBus
12
+ attr_reader :event_types
13
+
14
+ # @param [Hash{Symbol => Class}] a hash of event types to use on the bus
15
+ def initialize(registry = Events.registry)
16
+ @event_types = registry.freeze
17
+ @handlers = {}
18
+ end
19
+
20
+ # Register for an event. The handler proc will be called back with each of the attributes
21
+ # of the event.
22
+ def on(event_id, handler_object = nil, &handler_proc)
23
+ handler = handler_proc || handler_object
24
+ validate_handler_and_event_id!(handler, event_id)
25
+ event_class = event_types[event_id]
26
+ handlers_for(event_class) << handler
27
+ end
28
+
29
+ # Broadcast an event
30
+ def broadcast(event)
31
+ raise ArgumentError, "Event type #{event.class} is not registered. Try one of these:\n#{event_types.values.join("\n")}" unless is_registered_type?(event.class)
32
+ handlers_for(event.class).each { |handler| handler.call(event) }
33
+ end
34
+
35
+ def method_missing(event_id, *args)
36
+ event_class = event_types.fetch(event_id) { super }
37
+ broadcast event_class.new(*args)
38
+ rescue NameError => error
39
+ raise error, error.message + "\nDid you get the ID of the event wrong? Try one of these:\n#{event_types.keys.join("\n")}", error.backtrace
40
+ end
41
+
42
+ private
43
+
44
+ def handlers_for(event_class)
45
+ @handlers[event_class.to_s] ||= []
46
+ end
47
+
48
+ def is_registered_id?(event_id)
49
+ event_types.keys.include?(event_id)
50
+ end
51
+
52
+ def is_registered_type?(event_type)
53
+ event_types.values.include?(event_type)
54
+ end
55
+
56
+ def validate_handler_and_event_id!(handler, event_id)
57
+ raise ArgumentError, "Please pass either an object or a handler block" unless handler
58
+ raise ArgumentError, "Please use a symbol for the event_id" unless event_id.is_a?(Symbol)
59
+ raise ArgumentError, "Event ID #{event_id} is not recognised. Try one of these:\n#{event_types.keys.join("\n")}" unless is_registered_id?(event_id)
60
+ end
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,69 @@
1
+ require 'cucumber/core/event'
2
+
3
+ module Cucumber
4
+ module Core
5
+ module Events
6
+
7
+ # Signals that a {Test::Case} is about to be executed
8
+ class TestCaseStarting < Event.new(:test_case)
9
+
10
+ # @return [Test::Case] the test case to be executed
11
+ attr_reader :test_case
12
+
13
+ end
14
+
15
+ # Signals that a {Test::Step} is about to be executed
16
+ class TestStepStarting < Event.new(:test_step)
17
+
18
+ # @return [Test::Step] the test step to be executed
19
+ attr_reader :test_step
20
+
21
+ end
22
+
23
+ # Signals that a {Test::Step} has finished executing
24
+ class TestStepFinished < Event.new(:test_step, :result)
25
+
26
+ # @return [Test::Step] the test step that was executed
27
+ attr_reader :test_step
28
+
29
+ # @return [Test::Result] the result of running the {Test::Step}
30
+ attr_reader :result
31
+
32
+ end
33
+
34
+
35
+ # Signals that a {Test::Case} has finished executing
36
+ class TestCaseFinished < Event.new(:test_case, :result)
37
+
38
+ # @return [Test::Case] that was executed
39
+ attr_reader :test_case
40
+
41
+ # @return [Test::Result] the result of running the {Test::Step}
42
+ attr_reader :result
43
+
44
+ end
45
+
46
+ # The registry contains all the events registered in the core,
47
+ # that will be used by the {EventBus} by default.
48
+ def self.registry
49
+ build_registry(
50
+ TestCaseStarting,
51
+ TestStepStarting,
52
+ TestStepFinished,
53
+ TestCaseFinished,
54
+ )
55
+ end
56
+
57
+ # Build an event registry to be passed to the {EventBus}
58
+ # constructor from a list of types.
59
+ #
60
+ # Each type must respond to `event_id` so that it can be added
61
+ # to the registry hash.
62
+ #
63
+ # @return [Hash{Symbol => Class}]
64
+ def self.build_registry(*types)
65
+ types.map { |type| [type.event_id, type] }.to_h
66
+ end
67
+ end
68
+ end
69
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Cucumber
2
3
  module Core
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'cucumber/core/ast'
2
3
  require 'cucumber/core/platform'
3
4
 
@@ -271,7 +272,7 @@ module Cucumber
271
272
  def initialize(*)
272
273
  super
273
274
  @step_builders = attributes[:steps].map { |step| OutlineStepBuilder.new(file, step) }
274
- @example_builders = attributes[:examples].map { |example| ExamplesTableBuilder.new(file, example) }
275
+ @example_builders = attributes[:examples] ? attributes[:examples].map { |example| ExamplesTableBuilder.new(file, example) } : []
275
276
  end
276
277
 
277
278
  def result(language)
@@ -305,10 +306,8 @@ module Cucumber
305
306
 
306
307
  def initialize(*)
307
308
  super
308
- @header_builder = HeaderBuilder.new(file, attributes[:table_header])
309
- @example_rows_builders = attributes[:table_body].map do |row_attributes|
310
- ExampleRowBuilder.new(file, row_attributes)
311
- end
309
+ @header_builder = attributes[:table_header] ? HeaderBuilder.new(file, attributes[:table_header]) : NullHeaderBuilder.new
310
+ @example_rows_builders = attributes[:table_body] ? attributes[:table_body].map { |row_attributes| ExampleRowBuilder.new(file, row_attributes) } : []
312
311
  end
313
312
 
314
313
  def result(language)
@@ -341,6 +340,16 @@ module Cucumber
341
340
  end
342
341
  end
343
342
 
343
+ class NullHeaderBuilder < Builder
344
+ def initialize
345
+ @line = -1 # this inhibits Builder#handle_comments to put any comments in this object.
346
+ end
347
+
348
+ def result
349
+ nil
350
+ end
351
+ end
352
+
344
353
  def children
345
354
  [header_builder] + example_rows_builders
346
355
  end
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module Cucumber
3
4
  module Core
4
5
  module Gherkin
5
6
  class Document
6
- attr_reader :uri, :body
7
+ attr_reader :uri, :body, :language
7
8
 
8
- def initialize(uri, body)
9
- @uri = uri
10
- @body = body
9
+ def initialize(uri, body, language=nil)
10
+ @uri = uri
11
+ @body = body
12
+ @language = language || 'en'
11
13
  end
12
14
 
13
15
  def to_s
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'gherkin/parser'
2
3
  require 'gherkin/token_scanner'
3
4
  require 'gherkin/errors'
@@ -18,12 +19,13 @@ module Cucumber
18
19
  end
19
20
 
20
21
  def document(document)
21
- parser = ::Gherkin::Parser.new
22
- scanner = ::Gherkin::TokenScanner.new(document.body)
23
- core_builder = AstBuilder.new(document.uri)
22
+ parser = ::Gherkin::Parser.new
23
+ scanner = ::Gherkin::TokenScanner.new(document.body)
24
+ token_matcher = ::Gherkin::TokenMatcher.new(document.language)
25
+ core_builder = AstBuilder.new(document.uri)
24
26
 
25
27
  begin
26
- result = parser.parse(scanner)
28
+ result = parser.parse(scanner, token_matcher)
27
29
 
28
30
  receiver.feature core_builder.feature(result)
29
31
  rescue *PARSER_ERRORS => e
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Cucumber
2
3
  module Core
3
4
  module Gherkin
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'cucumber/core/gherkin/writer/helpers'
2
3
  require 'cucumber/core/gherkin/document'
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Cucumber
2
3
  module Core
3
4
  module Gherkin
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Detect the platform we're running on so we can tweak behaviour
2
3
  # in various places.
3
4
  require 'rbconfig'
@@ -1,25 +1,49 @@
1
+ # frozen_string_literal: true
1
2
  module Cucumber
2
3
  module Core
3
4
  module Report
4
5
  class Summary
5
6
  attr_reader :test_cases, :test_steps
6
7
 
7
- def initialize
8
+ def initialize(event_bus)
8
9
  @test_cases = Test::Result::Summary.new
9
10
  @test_steps = Test::Result::Summary.new
11
+ subscribe_to(event_bus)
10
12
  end
11
13
 
12
- def after_test_case(test_case, result)
13
- result.describe_to test_cases
14
+ private
15
+
16
+ def subscribe_to(event_bus)
17
+ event_bus.on(:test_case_finished) do |event|
18
+ event.result.describe_to test_cases
19
+ end
20
+ event_bus.on(:test_step_finished) do |event|
21
+ event.result.describe_to test_steps if is_step?(event.test_step)
22
+ end
23
+ self
14
24
  end
15
25
 
16
- def after_test_step(test_step, result)
17
- result.describe_to test_steps
26
+ def is_step?(test_step)
27
+ StepQueryVisitor.new(test_step).is_step?
18
28
  end
29
+ end
19
30
 
20
- def method_missing(*)
31
+ class StepQueryVisitor
32
+ def initialize(test_step)
33
+ @step = false
34
+ test_step.source.last.describe_to(self)
35
+ end
36
+
37
+ def is_step?
38
+ @step
21
39
  end
22
40
 
41
+ def step(*)
42
+ @step = true
43
+ end
44
+
45
+ def method_missing(*)
46
+ end
23
47
  end
24
48
  end
25
49
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'cucumber/core/test/result'
2
3
  require 'cucumber/core/test/timer'
3
4
  require 'cucumber/core/test/result'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Cucumber
2
3
  module Core
3
4
  module Test
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'cucumber/core/test/result'
2
3
  require 'cucumber/core/gherkin/tag_expression'
3
4
  require 'cucumber/core/ast/location'
@@ -99,6 +100,18 @@ module Cucumber
99
100
  source.first
100
101
  end
101
102
 
103
+ def hash
104
+ location.hash
105
+ end
106
+
107
+ def eql?(other)
108
+ other.hash == hash
109
+ end
110
+
111
+ def ==(other)
112
+ eql?(other)
113
+ end
114
+
102
115
  private
103
116
 
104
117
  def compose_around_hooks(visitor, *args, &block)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'cucumber/core/test/filters/locations_filter'
2
3
  require 'cucumber/core/test/filters/name_filter'
3
4
  require 'cucumber/core/test/filters/tag_filter'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'cucumber/core/filter'
2
3
 
3
4
  module Cucumber
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'cucumber/core/filter'
2
3
 
3
4
  module Cucumber
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'cucumber/core/filter'
2
3
 
3
4
  module Cucumber
@@ -1,4 +1,5 @@
1
1
  # encoding: UTF-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Cucumber
4
5
  module Core
@@ -30,6 +31,10 @@ module Cucumber
30
31
  def describe_to(visitor, *args)
31
32
  self
32
33
  end
34
+
35
+ def with_filtered_backtrace(filter)
36
+ self
37
+ end
33
38
  end
34
39
 
35
40
  class Passed
@@ -1,43 +1,43 @@
1
+ # frozen_string_literal: true
1
2
  require 'cucumber/core/test/timer'
2
3
 
3
4
  module Cucumber
4
5
  module Core
5
6
  module Test
6
7
  class Runner
7
- attr_reader :report, :running_test_case, :running_test_step
8
- private :report, :running_test_case, :running_test_step
8
+ attr_reader :event_bus, :running_test_case, :running_test_step
9
+ private :event_bus, :running_test_case, :running_test_step
9
10
 
10
- def initialize(report)
11
- @report = report
11
+ def initialize(event_bus)
12
+ @event_bus = event_bus
12
13
  end
13
14
 
14
15
  def test_case(test_case, &descend)
15
16
  @running_test_case = RunningTestCase.new
16
17
  @running_test_step = nil
17
- report.before_test_case(test_case)
18
+ event_bus.test_case_starting(test_case)
18
19
  descend.call(self)
19
- report.after_test_case(test_case, running_test_case.result)
20
+ event_bus.test_case_finished(test_case, running_test_case.result)
20
21
  self
21
22
  end
22
23
 
23
24
  def test_step(test_step)
24
25
  @running_test_step = test_step
25
- report.before_test_step test_step
26
+ event_bus.test_step_starting test_step
26
27
  step_result = running_test_case.execute(test_step)
27
- report.after_test_step test_step, step_result
28
+ event_bus.test_step_finished test_step, step_result
28
29
  @running_test_step = nil
29
30
  self
30
31
  end
31
32
 
32
33
  def around_hook(hook, &continue)
33
34
  result = running_test_case.execute(hook, &continue)
34
- report.after_test_step running_test_step, result if running_test_step
35
+ event_bus.test_step_finished running_test_step, result if running_test_step
35
36
  @running_test_step = nil
36
37
  self
37
38
  end
38
39
 
39
40
  def done
40
- report.done
41
41
  self
42
42
  end
43
43
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'cucumber/core/test/result'
2
3
  require 'cucumber/core/test/action'
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'cucumber/core/test/result'
2
3
 
3
4
  module Cucumber