cucumber-core 1.0.0.beta.3 → 1.0.0.beta.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -0
  3. data/HISTORY.md +20 -0
  4. data/README.md +121 -3
  5. data/cucumber-core.gemspec +1 -0
  6. data/lib/cucumber/core.rb +19 -13
  7. data/lib/cucumber/core/ast/data_table.rb +1 -1
  8. data/lib/cucumber/core/ast/examples_table.rb +5 -4
  9. data/lib/cucumber/core/ast/feature.rb +7 -1
  10. data/lib/cucumber/core/ast/scenario.rb +1 -1
  11. data/lib/cucumber/core/ast/step.rb +2 -2
  12. data/lib/cucumber/core/compiler.rb +7 -0
  13. data/lib/cucumber/core/gherkin/ast_builder.rb +15 -10
  14. data/lib/cucumber/core/platform.rb +0 -15
  15. data/lib/cucumber/core/report/summary.rb +26 -0
  16. data/lib/cucumber/core/test/case.rb +1 -1
  17. data/lib/cucumber/core/test/filters.rb +2 -47
  18. data/lib/cucumber/core/test/filters/locations_filter.rb +21 -0
  19. data/lib/cucumber/core/test/filters/name_filter.rb +27 -0
  20. data/lib/cucumber/core/test/hooks.rb +17 -0
  21. data/lib/cucumber/core/test/mapper.rb +14 -2
  22. data/lib/cucumber/core/test/result.rb +40 -30
  23. data/lib/cucumber/core/test/timer.rb +3 -1
  24. data/lib/cucumber/core/version.rb +1 -1
  25. data/spec/cucumber/core/ast/examples_table_spec.rb +19 -12
  26. data/spec/cucumber/core/ast/outline_step_spec.rb +3 -3
  27. data/spec/cucumber/core/gherkin/parser_spec.rb +18 -0
  28. data/spec/cucumber/core/test/action_spec.rb +3 -2
  29. data/spec/cucumber/core/test/duration_matcher.rb +19 -0
  30. data/spec/cucumber/core/test/mapper_spec.rb +30 -10
  31. data/spec/cucumber/core/test/result_spec.rb +47 -5
  32. data/spec/cucumber/core/test/runner_spec.rb +3 -2
  33. data/spec/cucumber/core/test/timer_spec.rb +13 -0
  34. data/spec/cucumber/core_spec.rb +33 -49
  35. data/spec/readme_spec.rb +36 -0
  36. metadata +26 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 216ba512564acf2ba3726b04c0c8a59cee4a10fc
4
- data.tar.gz: 7ce2adb926a31742c55b1e2956e6e7a4173a00a2
3
+ metadata.gz: 92d80d8c2127d6475aef16ab947d0bb6123cb65d
4
+ data.tar.gz: 17c84ce6fef0048f020350837ea4ce963a2983d8
5
5
  SHA512:
6
- metadata.gz: 9cbfeff310db927f86e1660216fab2960e21020dc926e8279b56b860834b79351c40764f47594309137646cc1267ac115205c6b50b405f7d1528cb592b3a8709
7
- data.tar.gz: ad2d596044985b3c7e7679af0933a7a4cc9e92d5f72d3509a9089b516a866bfbf2afc76e9ba1b82c482e7b096c9dde3d1f2661833856bd2bcb8ef507f6707338
6
+ metadata.gz: 1bd15d9ca8acc459edfe0be347c0de3e4e3a6fc60dbec47f03e3c71824ba71a08dd707c7363a98e55a1d1ea0fea3b43d519a9daf598fc81120061fb28d6d5179
7
+ data.tar.gz: 3dc08914e80026d9eedc136b4d47f0efda584ebc2094213aebcb06c6886df6cd6091f1da7458b7c6f27e5f706cd1f6f3b628736997c3d6059324ef289f293cc4
data/.yardopts ADDED
@@ -0,0 +1,6 @@
1
+ --exclude spec
2
+ --markup markdown
3
+ --no-private
4
+ -
5
+ HISTORY.md
6
+ LICENSE
data/HISTORY.md ADDED
@@ -0,0 +1,20 @@
1
+ ## [In Git](https://github.com/cucumber/cucumber-ruby-core/compare/v1.0.0.beta.3...master)
2
+
3
+ ### New Features
4
+
5
+ * Introduce a Duration object (#[71](https://github.com/cucumber/cucumber-ruby-core/pull/71) [@brasmusson](https://github.com/brasmusson))
6
+ * BeforeStep hooks (#[70](https://github.com/cucumber/cucumber-ruby-core/pull/70) [@almostwhitehat](https://github.com/almostwhitehat))
7
+ * Expose `Test::Case#test_steps` (@mattwynne)
8
+
9
+ ### Bugfixes
10
+
11
+ * Handle empty feature files (#[77](https://github.com/cucumber/cucumber-ruby-core/pull/77), [cucumber/cucumber#771](https://github.com/cucumber/cucumber/issues/771) [@brasmusson](https://github.com/brasmusson))
12
+ * Run after hooks in reverse order (#[69](https://github.com/cucumber/cucumber-ruby-core/pull/69) [@erran](https://github.com/erran))
13
+
14
+ ## [v1.0.0.beta.3](https://github.com/cucumber/cucumber-ruby-core/compare/v1.0.0.beta.2...v1.0.0.beta.3)
15
+
16
+ Changes were not logged.
17
+
18
+ ## [v1.0.0.beta.2](https://github.com/cucumber/cucumber-ruby-core/compare/v1.0.0.beta.1...v1.0.0.beta.2)
19
+
20
+ Changes were not logged.
data/README.md CHANGED
@@ -1,9 +1,127 @@
1
- cucumber-core
2
- ==================
1
+ # cucumber-core
3
2
 
4
3
  [![Build Status](https://secure.travis-ci.org/cucumber/cucumber-ruby-core.png)](http://travis-ci.org/cucumber/cucumber-ruby-core)
5
4
  [![Code Climate](https://codeclimate.com/github/cucumber/cucumber-ruby-core.png)](https://codeclimate.com/github/cucumber/cucumber-ruby-core)
6
5
  [![Coverage Status](https://coveralls.io/repos/cucumber/cucumber-ruby-core/badge.png?branch=master)](https://coveralls.io/r/cucumber/cucumber-ruby-core?branch=master)
7
6
  [![Dependency Status](https://gemnasium.com/cucumber/cucumber-ruby-core.png)](https://gemnasium.com/cucumber/cucumber-ruby-core)
8
7
 
9
- Core library for the Ruby flavour of Cucumber.
8
+ Cucumber Core is the [inner hexagon](http://alistair.cockburn.us/Hexagonal+architecture) for the [Ruby flavour of Cucumber](https://github.com/cucumber/cucumber).
9
+
10
+ It contains the core domain logic to execute Cucumber features. It has no user interface, just a Ruby API. If you're interested in how Cucumber works, or in building other tools that work with Gherkin documents, you've come to the right place.
11
+
12
+ ## An overview
13
+
14
+ The entry-point is a single method on the module `Cucumber::Core` called [`#execute`](http://rubydoc.info/gems/cucumber-core/Cucumber/Core#execute-instance_method). Here's what it does:
15
+
16
+ 1. Parses the plain-text Gherkin documents into an **AST**
17
+ 2. Compiles the AST down to **test cases**
18
+ 3. Passes the test cases through your **mappings** to activate them
19
+ 4. Passes the activated test cases through any **filters**
20
+ 5. Executes the test cases, calling back to the **report**
21
+
22
+ We've introduced a number of concepts here, so let's go through them in detail.
23
+
24
+ ### The AST
25
+
26
+ The Abstract Syntax Tree or [AST](http://rubydoc.info/gems/cucumber-core/Cucumber/Core/Ast) is an object graph that represents the Gherkin documents you've passed into the core. Things like [Feature](http://rubydoc.info/gems/cucumber-core/Cucumber/Core/Ast/Feature), [Scenario](http://rubydoc.info/gems/cucumber-core/Cucumber/Core/Ast/Scenario) and [ExamplesTable](ExamplesTable).
27
+
28
+ These are immutable value objects.
29
+
30
+ ### Test cases
31
+
32
+ Your gherkin might contain scenarios, as well as examples from tables beneath a scenario outline.
33
+
34
+ Test cases represent the general case of both of these. We compile the AST down to instances of [`Cucumber::Core::Test::Case`](http://rubydoc.info/gems/cucumber-core/Cucumber/Core/Test/Case), each containing a number of instances of [`Cucumber::Core::Test::Step`](http://rubydoc.info/gems/cucumber-core/Cucumber/Core/Test/Step). It's these that are then mapped, filtered, and executed.
35
+
36
+ Test cases and their test steps are also immutable value objects.
37
+
38
+ ### Mappings
39
+
40
+ When test cases are first compiled from the AST, they contain no information about how to execute them. Mappings are how we connect them to the code they need to run in order to execute your tests.
41
+
42
+ ### Filters
43
+
44
+ Once we have the test cases, and they've been activated by the mappings, you may want to pass them through a filter or two. Filters can be used to do things like sort, replace or remove some of the test cases or their steps before they're executed.
45
+
46
+ In fact, the mappings are just a special case of a filter.
47
+
48
+ ### Report
49
+
50
+ A report is how you find out what is happening during your test run. As the test cases and steps are executed, messages are sent to the report.
51
+
52
+ A report needs to respond to the following methods:
53
+
54
+ * `before_test_case(test_case)`
55
+ * `after_test_case(test_case, result)`
56
+ * `before_test_step(test_step)`
57
+ * `after_test_step(test_test, result)`
58
+ * `done`
59
+
60
+ That's probably best illustrated with an example.
61
+
62
+ ## Example
63
+
64
+ Here's an example of how you might use [`Cucumber::Core#execute`](http://rubydoc.info/gems/cucumber-core/Cucumber/Core#execute-instance_method)
65
+
66
+ ```ruby
67
+ require 'cucumber/core'
68
+
69
+ class MyRunner
70
+ include Cucumber::Core
71
+
72
+ def run(features)
73
+ execute features, Mappings.new, Report.new
74
+ end
75
+
76
+ class Mappings
77
+ def test_case(test_case, mapper)
78
+ end
79
+
80
+ def test_step(test_step, mapper)
81
+ mapper.map { fail } if test_step.name =~ /fail/
82
+ mapper.map { } if test_step.name =~ /pass/
83
+ end
84
+ end
85
+
86
+ class Report
87
+ def before_test_step(test_step)
88
+ end
89
+
90
+ def after_test_step(test_step, result)
91
+ puts "#{test_step.name} #{result}"
92
+ end
93
+
94
+ def before_test_case(test_case)
95
+ end
96
+
97
+ def after_test_case(test_case, result)
98
+ end
99
+
100
+ def done
101
+ end
102
+ end
103
+
104
+ end
105
+
106
+ feature = Cucumber::Core::Gherkin::Document.new(__FILE__, <<-GHERKIN)
107
+ Feature:
108
+ Scenario:
109
+ Given passing
110
+ And failing
111
+ And undefined
112
+ GHERKIN
113
+
114
+ MyRunner.new.run([feature])
115
+ ```
116
+
117
+ If you run this little Ruby script, you should see the following output:
118
+
119
+ ```
120
+ passing ✓
121
+ failing ✗
122
+ undefined ?
123
+ ```
124
+
125
+ ## Copyright
126
+
127
+ Copyright (c) 2013-2014 Cucumber Limited.
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.add_development_dependency 'rake', '>= 0.9.2'
21
21
  s.add_development_dependency 'rspec', '~> 3'
22
22
  s.add_development_dependency 'unindent', '>= 1.0'
23
+ s.add_development_dependency 'kramdown', '~> 1.4.2'
23
24
 
24
25
  # For coverage reports
25
26
  s.add_development_dependency 'coveralls', '~> 0.7'
data/lib/cucumber/core.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'cucumber/core/gherkin/parser'
2
+ require 'cucumber/core/gherkin/document'
2
3
  require 'cucumber/core/compiler'
3
4
  require 'cucumber/core/test/runner'
4
5
  require 'cucumber/core/test/mapper'
@@ -6,6 +7,22 @@ require 'cucumber/core/test/mapper'
6
7
  module Cucumber
7
8
  module Core
8
9
 
10
+ def execute(gherkin_documents, mapping_definition, report, filters = [])
11
+ receiver = Test::Runner.new(report)
12
+ filters << [Test::Mapper, [mapping_definition]]
13
+ compile gherkin_documents, receiver, filters
14
+ self
15
+ end
16
+
17
+ def compile(gherkin_documents, last_receiver, filters = [])
18
+ first_receiver = compose(filters, last_receiver)
19
+ compiler = Compiler.new(first_receiver)
20
+ parse gherkin_documents, compiler
21
+ self
22
+ end
23
+
24
+ private
25
+
9
26
  def parse(gherkin_documents, compiler)
10
27
  parser = Core::Gherkin::Parser.new(compiler)
11
28
  gherkin_documents.each do |document|
@@ -15,21 +32,10 @@ module Cucumber
15
32
  self
16
33
  end
17
34
 
18
- def compile(gherkin_documents, last_receiver, filters = [])
19
- first_receiver = filters.reverse.reduce(last_receiver) do |receiver, (filter_type, args)|
35
+ def compose(filters, last_receiver)
36
+ filters.reverse.reduce(last_receiver) do |receiver, (filter_type, args)|
20
37
  filter_type.new(*args + [receiver])
21
38
  end
22
- compiler = Compiler.new(first_receiver)
23
- parse gherkin_documents, compiler
24
- self
25
- end
26
-
27
- def execute(gherkin_documents, mapping_definition, report, filters = [], run_options = {})
28
- receiver = Test::Runner.new(report)
29
- filters << [Test::Mapper, [mapping_definition]]
30
- filters << [Test::DebugFilter, []] if run_options[:debug]
31
- compile gherkin_documents, receiver, filters
32
- self
33
39
  end
34
40
 
35
41
  end
@@ -100,7 +100,7 @@ module Cucumber
100
100
  Hash === array[0] ? hashes_to_array(array) : array
101
101
  end
102
102
 
103
- def hashes_to_array(hashes) #:nodoc:
103
+ def hashes_to_array(hashes)
104
104
  header = hashes[0].keys.sort
105
105
  [header] + hashes.map{|hash| header.map{|key| hash[key]}}
106
106
  end
@@ -40,8 +40,8 @@ module Cucumber
40
40
  @cells
41
41
  end
42
42
 
43
- def build_row(row_cells, number, location)
44
- Row.new(Hash[@cells.zip(row_cells)], number, location)
43
+ def build_row(row_cells, number, location, language)
44
+ Row.new(Hash[@cells.zip(row_cells)], number, location, language)
45
45
  end
46
46
  end
47
47
 
@@ -49,13 +49,14 @@ module Cucumber
49
49
  include DescribesItself
50
50
  include HasLocation
51
51
 
52
- attr_reader :number
52
+ attr_reader :number, :language
53
53
 
54
- def initialize(data, number, location)
54
+ def initialize(data, number, location, language)
55
55
  raise ArgumentError, data.to_s unless data.is_a?(Hash)
56
56
  @data = data
57
57
  @number = number
58
58
  @location = location
59
+ @language = language
59
60
  end
60
61
 
61
62
  def ==(other)
@@ -7,7 +7,7 @@ module Cucumber
7
7
  module Core
8
8
  module Ast
9
9
  # Represents the root node of a parsed feature.
10
- class Feature #:nodoc:
10
+ class Feature
11
11
  include Names
12
12
  include HasLocation
13
13
  include DescribesItself
@@ -51,6 +51,12 @@ module Cucumber
51
51
  end
52
52
 
53
53
  end
54
+
55
+ class NullFeature
56
+ def method_missing(*args, &block)
57
+ self
58
+ end
59
+ end
54
60
  end
55
61
  end
56
62
  end
@@ -7,7 +7,7 @@ require 'cucumber/core/ast/location'
7
7
  module Cucumber
8
8
  module Core
9
9
  module Ast
10
- class Scenario #:nodoc:
10
+ class Scenario
11
11
  include Names
12
12
  include HasLocation
13
13
  include DescribesItself
@@ -4,7 +4,7 @@ require 'cucumber/core/ast/location'
4
4
  module Cucumber
5
5
  module Core
6
6
  module Ast
7
- class Step #:nodoc:
7
+ class Step
8
8
  include HasLocation
9
9
  include DescribesItself
10
10
 
@@ -33,7 +33,7 @@ module Cucumber
33
33
  end
34
34
  end
35
35
 
36
- class ExpandedOutlineStep < Step #:nodoc:
36
+ class ExpandedOutlineStep < Step
37
37
 
38
38
  def initialize(outline_step, gherkin_statement, language, location, keyword, name, multiline_arg)
39
39
  @outline_step, @gherkin_statement, @location, @keyword, @name, @multiline_arg = outline_step, gherkin_statement, location, keyword, name, multiline_arg
@@ -4,6 +4,8 @@ require 'cucumber/core/test/step'
4
4
 
5
5
  module Cucumber
6
6
  module Core
7
+
8
+ # Compiles the AST into test cases
7
9
  class Compiler
8
10
  include Cucumber.initializer(:receiver)
9
11
 
@@ -18,6 +20,7 @@ module Cucumber
18
20
  self
19
21
  end
20
22
 
23
+ # @private
21
24
  class TestCaseBuilder
22
25
  include Cucumber.initializer(:receiver)
23
26
 
@@ -48,6 +51,7 @@ module Cucumber
48
51
  end
49
52
  end
50
53
 
54
+ # @private
51
55
  class FeatureCompiler
52
56
  include Cucumber.initializer(:receiver)
53
57
 
@@ -80,6 +84,7 @@ module Cucumber
80
84
  end
81
85
  end
82
86
 
87
+ # @private
83
88
  class ScenarioOutlineCompiler
84
89
  include Cucumber.initializer(:source, :receiver)
85
90
 
@@ -113,6 +118,7 @@ module Cucumber
113
118
  end
114
119
  end
115
120
 
121
+ # @private
116
122
  class ScenarioCompiler
117
123
  include Cucumber.initializer(:source, :receiver)
118
124
 
@@ -122,6 +128,7 @@ module Cucumber
122
128
  end
123
129
  end
124
130
 
131
+ # @private
125
132
  class BackgroundCompiler
126
133
  include Cucumber.initializer(:source, :receiver)
127
134
 
@@ -12,10 +12,11 @@ module Cucumber
12
12
 
13
13
  def initialize(path)
14
14
  @path = path
15
+ @feature_builder = nil
15
16
  end
16
17
 
17
18
  def result
18
- return nil unless @feature_builder
19
+ return Ast::NullFeature.new unless @feature_builder
19
20
  @feature_builder.result(language)
20
21
  end
21
22
 
@@ -220,7 +221,7 @@ module Cucumber
220
221
 
221
222
  class ScenarioOutlineBuilder < Builder
222
223
  def result(background, language, feature_tags)
223
- raise ParseError.new("Missing Examples section for Scenario Outline at #{location}") if examples_tables.empty?
224
+ raise ParseError.new("Missing Examples section for Scenario Outline at #{location}") if examples_builders.empty?
224
225
  Ast::ScenarioOutline.new(
225
226
  node,
226
227
  language,
@@ -233,12 +234,12 @@ module Cucumber
233
234
  node.name,
234
235
  node.description,
235
236
  steps(language),
236
- examples_tables
237
+ examples_tables(language)
237
238
  )
238
239
  end
239
240
 
240
241
  def add_examples(file, node)
241
- examples_tables << ExamplesTableBuilder.new(file, node).result
242
+ examples_builders << ExamplesTableBuilder.new(file, node)
242
243
  end
243
244
 
244
245
  def add_step(file, node)
@@ -255,13 +256,17 @@ module Cucumber
255
256
  @step_builders ||= []
256
257
  end
257
258
 
258
- def examples_tables
259
- @examples_tables ||= []
259
+ def examples_tables(language)
260
+ examples_builders.map { |examples_builder| examples_builder.result(language) }
261
+ end
262
+
263
+ def examples_builders
264
+ @examples_builders ||= []
260
265
  end
261
266
 
262
267
  class ExamplesTableBuilder < Builder
263
268
 
264
- def result
269
+ def result(language)
265
270
  Ast::ExamplesTable.new(
266
271
  node,
267
272
  location,
@@ -271,7 +276,7 @@ module Cucumber
271
276
  node.name,
272
277
  node.description,
273
278
  header,
274
- example_rows
279
+ example_rows(language)
275
280
  )
276
281
  end
277
282
 
@@ -282,10 +287,10 @@ module Cucumber
282
287
  Ast::ExamplesTable::Header.new(row.cells, location)
283
288
  end
284
289
 
285
- def example_rows
290
+ def example_rows(language)
286
291
  _, *raw_examples = *node.rows
287
292
  raw_examples.each_with_index.map do |row, index|
288
- header.build_row(row.cells, index + 1, location.on_line(row.line))
293
+ header.build_row(row.cells, index + 1, location.on_line(row.line), language)
289
294
  end
290
295
  end
291
296