gherkin-ruby 0.0.1 → 0.0.2
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.
- data/.travis.yml +8 -0
- data/Gemfile +1 -0
- data/Readme.md +19 -0
- data/lib/gherkin/ast.rb +8 -5
- data/lib/gherkin/parser.rb +7 -4
- data/lib/gherkin/transform.rb +2 -1
- data/lib/gherkin/version.rb +1 -1
- data/test/gherkin/ast_test.rb +41 -8
- data/test/gherkin/parser_test.rb +22 -9
- data/test/gherkin/transform_test.rb +18 -9
- metadata +7 -6
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/Readme.md
CHANGED
@@ -1,6 +1,25 @@
|
|
1
1
|
# gherkin-ruby
|
2
2
|
Gherkin-ruby is a pure Ruby implementation of a [Gherkin](http://github.com/cucumber/gherkin) parser, using [Parslet](http://github.com/kschiess/parslet).
|
3
3
|
|
4
|
+
## Why this one over the official, fast, Ragel-based Gherkin parser?
|
5
|
+
|
6
|
+
* Less than 200 LOC.
|
7
|
+
* No C-exts
|
8
|
+
* No Java/.NET crap.
|
9
|
+
* Fast enough for our purposes (using it for the [Spinach](http://github.com/codegram/spinach) project)
|
10
|
+
|
11
|
+
## Install
|
12
|
+
|
13
|
+
$ gem install gherkin-ruby
|
14
|
+
|
15
|
+
Or in your Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
# Gemfile
|
19
|
+
|
20
|
+
gem 'gherkin-ruby'
|
21
|
+
```
|
22
|
+
|
4
23
|
## Usage
|
5
24
|
You can easily implement your own visitors to traverse the Abstract Syntax Tree. The following example just prints the step names to standard output:
|
6
25
|
|
data/lib/gherkin/ast.rb
CHANGED
@@ -33,8 +33,10 @@ module Gherkin
|
|
33
33
|
include Enumerable
|
34
34
|
|
35
35
|
def initialize(steps=[])
|
36
|
-
|
37
|
-
|
36
|
+
if steps.any?
|
37
|
+
@line = steps.first.line - 1
|
38
|
+
@column = 3
|
39
|
+
end
|
38
40
|
|
39
41
|
@steps = steps
|
40
42
|
end
|
@@ -63,11 +65,12 @@ module Gherkin
|
|
63
65
|
end
|
64
66
|
|
65
67
|
class Step < Node
|
66
|
-
attr_reader :name
|
67
|
-
def initialize(name)
|
68
|
+
attr_reader :name, :keyword
|
69
|
+
def initialize(name, keyword)
|
68
70
|
@line, @column = name.line_and_column
|
69
71
|
|
70
|
-
@name
|
72
|
+
@name = name.to_s
|
73
|
+
@keyword = keyword.to_s
|
71
74
|
end
|
72
75
|
end
|
73
76
|
|
data/lib/gherkin/parser.rb
CHANGED
@@ -14,14 +14,17 @@ module Gherkin
|
|
14
14
|
|
15
15
|
rule(:feature_line) { str('Feature:') >> space? >> text.as(:name) }
|
16
16
|
rule(:scenario_line) { indent(2) >> str('Scenario:') >> space? >> text.as(:name) }
|
17
|
-
rule(:background_line) { indent(2) >> str('Background:') }
|
18
17
|
|
19
18
|
rule(:step_keyword) { str('Given') | str('When') | str('Then') | str('And') | str('But') }
|
20
19
|
|
21
20
|
rule(:comment) { str('#') >> text.as(:comment) }
|
22
|
-
rule(:description) { indent(2) >> text.as(:description) }
|
23
21
|
|
24
|
-
rule(:
|
22
|
+
rule(:description_text) { (str('Background:') | str('Scenario:') | str('@')).absent? >> text }
|
23
|
+
rule(:description) { (indent(2) >> description_text >> newline).repeat }
|
24
|
+
|
25
|
+
rule(:background_line) { indent(2) >> str('Background:') }
|
26
|
+
|
27
|
+
rule(:step) { indent(4) >> step_keyword.as(:keyword) >> space? >> text.as(:name) }
|
25
28
|
rule(:steps) { (step.as(:step) >> newline.maybe).repeat }
|
26
29
|
|
27
30
|
rule(:tags) { indent(2) >> (str('@') >> identifier.as(:tag) >> str(' ').maybe).repeat(1) }
|
@@ -31,7 +34,7 @@ module Gherkin
|
|
31
34
|
|
32
35
|
rule(:background) { background_line >> newline >> steps.as(:steps) }
|
33
36
|
|
34
|
-
rule(:feature) { feature_line >> newline >> background.as(:background).maybe >> scenarios.as(:scenarios) }
|
37
|
+
rule(:feature) { feature_line >> newline >> description >> background.as(:background).maybe >> scenarios.as(:scenarios) }
|
35
38
|
|
36
39
|
rule(:main) { feature.as(:feature) }
|
37
40
|
|
data/lib/gherkin/transform.rb
CHANGED
data/lib/gherkin/version.rb
CHANGED
data/test/gherkin/ast_test.rb
CHANGED
@@ -26,7 +26,7 @@ module Gherkin
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
[Feature, Scenario,
|
29
|
+
[Feature, Scenario, Tag].each do |node|
|
30
30
|
describe node do
|
31
31
|
it 'is a Node' do
|
32
32
|
node.ancestors.must_include Node
|
@@ -82,21 +82,54 @@ module Gherkin
|
|
82
82
|
end
|
83
83
|
|
84
84
|
describe Background do
|
85
|
-
|
86
|
-
Background.ancestors.must_include Node
|
87
|
-
end
|
88
|
-
|
89
|
-
it 'is Enumerable' do
|
85
|
+
let(:steps) do
|
90
86
|
steps = [
|
91
87
|
OpenStruct.new(line: 4),
|
92
88
|
OpenStruct.new(line: 5),
|
93
89
|
]
|
90
|
+
end
|
94
91
|
|
92
|
+
it 'is a Node' do
|
93
|
+
Background.ancestors.must_include Node
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'is Enumerable' do
|
95
97
|
instance = Background.new(steps)
|
96
|
-
instance.line.must_equal 3
|
97
|
-
instance.column.must_equal 3
|
98
98
|
instance.each.to_a.must_equal steps
|
99
99
|
end
|
100
|
+
|
101
|
+
describe 'when there are background steps' do
|
102
|
+
it 'records line and column' do
|
103
|
+
instance = Background.new(steps)
|
104
|
+
instance.line.must_equal 3
|
105
|
+
instance.column.must_equal 3
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'otherwise' do
|
110
|
+
it 'does not' do
|
111
|
+
instance = Background.new([])
|
112
|
+
instance.line.must_equal nil
|
113
|
+
instance.column.must_equal nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe Step do
|
119
|
+
it 'is a Node' do
|
120
|
+
Step.ancestors.must_include Node
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'has a line and column' do
|
124
|
+
name = OpenStruct.new(line_and_column: [2, 13])
|
125
|
+
def name.to_s; 'Name'; end
|
126
|
+
|
127
|
+
instance = Step.new(name, 'Given')
|
128
|
+
instance.name.must_equal 'Name'
|
129
|
+
instance.keyword.must_equal 'Given'
|
130
|
+
instance.line.must_equal 2
|
131
|
+
instance.column.must_equal 13
|
132
|
+
end
|
100
133
|
end
|
101
134
|
end
|
102
135
|
end
|
data/test/gherkin/parser_test.rb
CHANGED
@@ -19,23 +19,33 @@ module Gherkin
|
|
19
19
|
|
20
20
|
describe 'Steps' do
|
21
21
|
it 'parses a Given step' do
|
22
|
-
|
22
|
+
step = " Given I have an empty array"
|
23
|
+
p(:step, step, :name).must_equal "I have an empty array"
|
24
|
+
p(:step, step, :keyword).must_equal "Given"
|
23
25
|
end
|
24
26
|
|
25
27
|
it 'parses a When step' do
|
26
|
-
|
28
|
+
step = " When I have an empty array"
|
29
|
+
p(:step, step, :name).must_equal "I have an empty array"
|
30
|
+
p(:step, step, :keyword).must_equal "When"
|
27
31
|
end
|
28
32
|
|
29
33
|
it 'parses a Then step' do
|
30
|
-
|
34
|
+
step = " Then I have an empty array"
|
35
|
+
p(:step, step, :name).must_equal "I have an empty array"
|
36
|
+
p(:step, step, :keyword).must_equal "Then"
|
31
37
|
end
|
32
38
|
|
33
39
|
it 'parses an And step' do
|
34
|
-
|
40
|
+
step = " And I have an empty array"
|
41
|
+
p(:step, step, :name).must_equal "I have an empty array"
|
42
|
+
p(:step, step, :keyword).must_equal "And"
|
35
43
|
end
|
36
44
|
|
37
45
|
it 'parses a But step' do
|
38
|
-
|
46
|
+
step = " But I have an empty array"
|
47
|
+
p(:step, step, :name).must_equal "I have an empty array"
|
48
|
+
p(:step, step, :keyword).must_equal "But"
|
39
49
|
end
|
40
50
|
end
|
41
51
|
end
|
@@ -47,10 +57,13 @@ module Gherkin
|
|
47
57
|
end
|
48
58
|
|
49
59
|
describe 'Description parsing' do
|
50
|
-
it 'parses descriptions
|
51
|
-
|
52
|
-
|
53
|
-
|
60
|
+
it 'parses multiline descriptions' do
|
61
|
+
description = """ In order to know what the heck is Gherkin
|
62
|
+
As a developer
|
63
|
+
I want it to behave in an expected way
|
64
|
+
"""
|
65
|
+
parser = Gherkin::Parser.new
|
66
|
+
parser.description.parse(description)
|
54
67
|
end
|
55
68
|
end
|
56
69
|
|
@@ -5,6 +5,9 @@ module Gherkin
|
|
5
5
|
describe 'Feature parsing' do
|
6
6
|
before do
|
7
7
|
@scenario = """Feature: My Feature
|
8
|
+
In order to do something
|
9
|
+
As a developer
|
10
|
+
I want to be happy
|
8
11
|
|
9
12
|
Background:
|
10
13
|
Given something happens before anything else happens
|
@@ -32,34 +35,40 @@ module Gherkin
|
|
32
35
|
|
33
36
|
background = @result.background
|
34
37
|
background.must_be_kind_of AST::Background
|
35
|
-
background.line.must_equal
|
38
|
+
background.line.must_equal 6
|
36
39
|
background.column.must_equal 3
|
40
|
+
background.steps.first.keyword.must_equal 'Given'
|
37
41
|
background.steps.first.name.must_equal 'something happens before anything else happens'
|
38
|
-
background.steps.first.line.must_equal
|
42
|
+
background.steps.first.line.must_equal 7
|
43
|
+
background.steps.last.keyword.must_equal 'And'
|
39
44
|
background.steps.last.name.must_equal 'more things happens before anything else happens'
|
40
|
-
background.steps.last.line.must_equal
|
45
|
+
background.steps.last.line.must_equal 8
|
41
46
|
|
42
47
|
first_scenario = @result.scenarios.first
|
43
48
|
first_scenario.must_be_kind_of AST::Scenario
|
44
|
-
first_scenario.line.must_equal
|
49
|
+
first_scenario.line.must_equal 10
|
45
50
|
first_scenario.name.must_equal 'something happens'
|
51
|
+
first_scenario.steps.first.keyword.must_equal 'Given'
|
46
52
|
first_scenario.steps.first.name.must_equal 'something happens'
|
47
|
-
first_scenario.steps.first.line.must_equal
|
53
|
+
first_scenario.steps.first.line.must_equal 11
|
54
|
+
first_scenario.steps.last.keyword.must_equal 'Then'
|
48
55
|
first_scenario.steps.last.name.must_equal 'something cooler happens'
|
49
|
-
first_scenario.steps.last.line.must_equal
|
56
|
+
first_scenario.steps.last.line.must_equal 12
|
50
57
|
|
51
58
|
last_scenario = @result.scenarios.last
|
52
59
|
last_scenario.must_be_kind_of AST::Scenario
|
53
|
-
last_scenario.line.must_equal
|
60
|
+
last_scenario.line.must_equal 15
|
54
61
|
last_scenario.name.must_equal 'something else happens'
|
55
62
|
|
56
63
|
last_scenario.tags.first.name.must_equal 'javascript'
|
57
64
|
last_scenario.tags.last.name.must_equal 'wip'
|
58
65
|
|
66
|
+
last_scenario.steps.first.keyword.must_equal 'Given'
|
59
67
|
last_scenario.steps.first.name.must_equal 'foo'
|
60
|
-
last_scenario.steps.first.line.must_equal
|
68
|
+
last_scenario.steps.first.line.must_equal 16
|
69
|
+
last_scenario.steps.last.keyword.must_equal 'Then'
|
61
70
|
last_scenario.steps.last.name.must_equal 'bar'
|
62
|
-
last_scenario.steps.last.line.must_equal
|
71
|
+
last_scenario.steps.last.line.must_equal 17
|
63
72
|
end
|
64
73
|
end
|
65
74
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gherkin-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2011-11-
|
13
|
+
date: 2011-11-11 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: parslet
|
17
|
-
requirement: &
|
17
|
+
requirement: &2156944000 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *2156944000
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: minitest
|
28
|
-
requirement: &
|
28
|
+
requirement: &2156943540 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ! '>='
|
@@ -33,7 +33,7 @@ dependencies:
|
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *2156943540
|
37
37
|
description: Gherkin-ruby is a Gherkin parser in pure Ruby using Parslet
|
38
38
|
email:
|
39
39
|
- marcdivc@gmail.com
|
@@ -43,6 +43,7 @@ extensions: []
|
|
43
43
|
extra_rdoc_files: []
|
44
44
|
files:
|
45
45
|
- .gitignore
|
46
|
+
- .travis.yml
|
46
47
|
- Gemfile
|
47
48
|
- Rakefile
|
48
49
|
- Readme.md
|