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.
- checksums.yaml +4 -4
- data/.yardopts +6 -0
- data/HISTORY.md +20 -0
- data/README.md +121 -3
- data/cucumber-core.gemspec +1 -0
- data/lib/cucumber/core.rb +19 -13
- data/lib/cucumber/core/ast/data_table.rb +1 -1
- data/lib/cucumber/core/ast/examples_table.rb +5 -4
- data/lib/cucumber/core/ast/feature.rb +7 -1
- data/lib/cucumber/core/ast/scenario.rb +1 -1
- data/lib/cucumber/core/ast/step.rb +2 -2
- data/lib/cucumber/core/compiler.rb +7 -0
- data/lib/cucumber/core/gherkin/ast_builder.rb +15 -10
- data/lib/cucumber/core/platform.rb +0 -15
- data/lib/cucumber/core/report/summary.rb +26 -0
- data/lib/cucumber/core/test/case.rb +1 -1
- data/lib/cucumber/core/test/filters.rb +2 -47
- data/lib/cucumber/core/test/filters/locations_filter.rb +21 -0
- data/lib/cucumber/core/test/filters/name_filter.rb +27 -0
- data/lib/cucumber/core/test/hooks.rb +17 -0
- data/lib/cucumber/core/test/mapper.rb +14 -2
- data/lib/cucumber/core/test/result.rb +40 -30
- data/lib/cucumber/core/test/timer.rb +3 -1
- data/lib/cucumber/core/version.rb +1 -1
- data/spec/cucumber/core/ast/examples_table_spec.rb +19 -12
- data/spec/cucumber/core/ast/outline_step_spec.rb +3 -3
- data/spec/cucumber/core/gherkin/parser_spec.rb +18 -0
- data/spec/cucumber/core/test/action_spec.rb +3 -2
- data/spec/cucumber/core/test/duration_matcher.rb +19 -0
- data/spec/cucumber/core/test/mapper_spec.rb +30 -10
- data/spec/cucumber/core/test/result_spec.rb +47 -5
- data/spec/cucumber/core/test/runner_spec.rb +3 -2
- data/spec/cucumber/core/test/timer_spec.rb +13 -0
- data/spec/cucumber/core_spec.rb +33 -49
- data/spec/readme_spec.rb +36 -0
- metadata +26 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92d80d8c2127d6475aef16ab947d0bb6123cb65d
|
4
|
+
data.tar.gz: 17c84ce6fef0048f020350837ea4ce963a2983d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1bd15d9ca8acc459edfe0be347c0de3e4e3a6fc60dbec47f03e3c71824ba71a08dd707c7363a98e55a1d1ea0fea3b43d519a9daf598fc81120061fb28d6d5179
|
7
|
+
data.tar.gz: 3dc08914e80026d9eedc136b4d47f0efda584ebc2094213aebcb06c6886df6cd6091f1da7458b7c6f27e5f706cd1f6f3b628736997c3d6059324ef289f293cc4
|
data/.yardopts
ADDED
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
|
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.
|
data/cucumber-core.gemspec
CHANGED
@@ -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
|
19
|
-
|
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)
|
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
|
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
|
@@ -4,7 +4,7 @@ require 'cucumber/core/ast/location'
|
|
4
4
|
module Cucumber
|
5
5
|
module Core
|
6
6
|
module Ast
|
7
|
-
class Step
|
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
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
|