cucumber-core 0.1.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 (69) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.travis.yml +16 -0
  6. data/Gemfile +2 -0
  7. data/LICENSE +20 -0
  8. data/README.md +9 -0
  9. data/Rakefile +24 -0
  10. data/cucumber-core.gemspec +32 -0
  11. data/lib/cucumber/core.rb +37 -0
  12. data/lib/cucumber/core/ast.rb +13 -0
  13. data/lib/cucumber/core/ast/background.rb +33 -0
  14. data/lib/cucumber/core/ast/comment.rb +17 -0
  15. data/lib/cucumber/core/ast/data_table.rb +326 -0
  16. data/lib/cucumber/core/ast/describes_itself.rb +16 -0
  17. data/lib/cucumber/core/ast/doc_string.rb +83 -0
  18. data/lib/cucumber/core/ast/empty_background.rb +12 -0
  19. data/lib/cucumber/core/ast/examples_table.rb +95 -0
  20. data/lib/cucumber/core/ast/feature.rb +62 -0
  21. data/lib/cucumber/core/ast/location.rb +140 -0
  22. data/lib/cucumber/core/ast/multiline_argument.rb +33 -0
  23. data/lib/cucumber/core/ast/names.rb +19 -0
  24. data/lib/cucumber/core/ast/outline_step.rb +51 -0
  25. data/lib/cucumber/core/ast/scenario.rb +43 -0
  26. data/lib/cucumber/core/ast/scenario_outline.rb +44 -0
  27. data/lib/cucumber/core/ast/step.rb +38 -0
  28. data/lib/cucumber/core/ast/tag.rb +14 -0
  29. data/lib/cucumber/core/compiler.rb +136 -0
  30. data/lib/cucumber/core/gherkin/ast_builder.rb +315 -0
  31. data/lib/cucumber/core/gherkin/document.rb +20 -0
  32. data/lib/cucumber/core/gherkin/parser.rb +45 -0
  33. data/lib/cucumber/core/gherkin/writer.rb +220 -0
  34. data/lib/cucumber/core/gherkin/writer/helpers.rb +178 -0
  35. data/lib/cucumber/core/platform.rb +30 -0
  36. data/lib/cucumber/core/test/case.rb +143 -0
  37. data/lib/cucumber/core/test/filters.rb +48 -0
  38. data/lib/cucumber/core/test/filters/tag_filter.rb +110 -0
  39. data/lib/cucumber/core/test/hook_compiler.rb +109 -0
  40. data/lib/cucumber/core/test/mapper.rb +56 -0
  41. data/lib/cucumber/core/test/mapping.rb +67 -0
  42. data/lib/cucumber/core/test/result.rb +191 -0
  43. data/lib/cucumber/core/test/runner.rb +149 -0
  44. data/lib/cucumber/core/test/step.rb +69 -0
  45. data/lib/cucumber/core/test/timer.rb +31 -0
  46. data/lib/cucumber/core/version.rb +9 -0
  47. data/lib/cucumber/initializer.rb +18 -0
  48. data/spec/capture_warnings.rb +68 -0
  49. data/spec/coverage.rb +10 -0
  50. data/spec/cucumber/core/ast/data_table_spec.rb +139 -0
  51. data/spec/cucumber/core/ast/doc_string_spec.rb +77 -0
  52. data/spec/cucumber/core/ast/examples_table_spec.rb +87 -0
  53. data/spec/cucumber/core/ast/location_spec.rb +105 -0
  54. data/spec/cucumber/core/ast/outline_step_spec.rb +77 -0
  55. data/spec/cucumber/core/ast/step_spec.rb +44 -0
  56. data/spec/cucumber/core/compiler_spec.rb +249 -0
  57. data/spec/cucumber/core/gherkin/parser_spec.rb +182 -0
  58. data/spec/cucumber/core/gherkin/writer_spec.rb +332 -0
  59. data/spec/cucumber/core/test/case_spec.rb +416 -0
  60. data/spec/cucumber/core/test/hook_compiler_spec.rb +78 -0
  61. data/spec/cucumber/core/test/mapper_spec.rb +68 -0
  62. data/spec/cucumber/core/test/mapping_spec.rb +103 -0
  63. data/spec/cucumber/core/test/result_spec.rb +178 -0
  64. data/spec/cucumber/core/test/runner_spec.rb +265 -0
  65. data/spec/cucumber/core/test/step_spec.rb +58 -0
  66. data/spec/cucumber/core/test/timer_spec.rb +13 -0
  67. data/spec/cucumber/core_spec.rb +419 -0
  68. data/spec/cucumber/initializer_spec.rb +49 -0
  69. metadata +221 -0
@@ -0,0 +1,19 @@
1
+ module Cucumber
2
+ module Core
3
+ module Ast
4
+ module Names
5
+ attr_reader :title, :description
6
+
7
+ def name
8
+ s = @title
9
+ s += "\n#{@description}" if @description != ""
10
+ s
11
+ end
12
+
13
+ def to_s
14
+ @title
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,51 @@
1
+ require 'cucumber/core/ast/location'
2
+ require 'cucumber/core/ast/describes_itself'
3
+ require 'cucumber/core/ast/step'
4
+
5
+ module Cucumber
6
+ module Core
7
+ module Ast
8
+
9
+ class OutlineStep
10
+ include HasLocation
11
+ include DescribesItself
12
+
13
+ attr_reader :language, :location, :keyword, :name, :multiline_arg
14
+
15
+ def initialize(language, location, keyword, name, multiline_arg=nil)
16
+ @language, @location, @keyword, @name, @multiline_arg = language, location, keyword, name, multiline_arg
17
+ @language || raise("Language is required!")
18
+ end
19
+
20
+ def gherkin_statement(statement=nil)
21
+ @gherkin_statement ||= statement
22
+ end
23
+
24
+ def to_step(row)
25
+ Ast::Step.new(language, location, keyword, row.expand(name), replace_multiline_arg(row))
26
+ end
27
+
28
+ private
29
+
30
+ def description_for_visitors
31
+ :outline_step
32
+ end
33
+
34
+ def children
35
+ # TODO use a null object
36
+ # TODO remove duplication with Step
37
+ # TODO spec
38
+ return [] unless @multiline_arg
39
+ [@multiline_arg]
40
+ end
41
+
42
+ def replace_multiline_arg(example_row)
43
+ return unless multiline_arg
44
+ multiline_arg.map { |cell| example_row.expand(cell) }
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,43 @@
1
+ require 'cucumber/initializer'
2
+ require 'cucumber/core/ast/describes_itself'
3
+ require 'cucumber/core/ast/names'
4
+ require 'cucumber/core/ast/empty_background'
5
+ require 'cucumber/core/ast/location'
6
+
7
+ module Cucumber
8
+ module Core
9
+ module Ast
10
+ class Scenario #:nodoc:
11
+ include Names
12
+ include HasLocation
13
+ include DescribesItself
14
+
15
+ attr_reader :feature_tags
16
+ attr_accessor :feature
17
+ attr_reader :comments, :tags, :keyword, :background, :title, :location, :gherkin_statement
18
+
19
+ include Cucumber.initializer(:gherkin_statement, :language, :location, :background, :comments, :tags, :feature_tags, :keyword, :title, :description, :raw_steps)
20
+
21
+ def children
22
+ raw_steps
23
+ end
24
+
25
+ def to_sexp
26
+ sexp = [:scenario, line, keyword, name]
27
+ comment = comment.to_sexp
28
+ sexp += [comment] if comment
29
+ tags = tags.to_sexp
30
+ sexp += tags if tags.any?
31
+ sexp += step_invocations.to_sexp if step_invocations.any?
32
+ sexp
33
+ end
34
+
35
+ private
36
+
37
+ def description_for_visitors
38
+ :scenario
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,44 @@
1
+ require 'cucumber/core/ast/names'
2
+ require 'cucumber/core/ast/location'
3
+ require 'cucumber/core/ast/empty_background'
4
+ require 'cucumber/core/ast/describes_itself'
5
+
6
+ module Cucumber
7
+ module Core
8
+ module Ast
9
+ class ScenarioOutline
10
+ include Names
11
+ include HasLocation
12
+ include DescribesItself
13
+
14
+ MissingExamples = Class.new(StandardError)
15
+
16
+ attr_accessor :feature
17
+ attr_reader :feature_tags
18
+
19
+ attr_reader :line
20
+ private :line
21
+
22
+ include Cucumber.initializer(:language, :location, :background, :comments, :tags, :feature_tags, :keyword, :title, :description, :steps, :examples_tables)
23
+
24
+ attr_reader :comments, :tags, :keyword, :background, :location
25
+
26
+ def gherkin_statement(node)
27
+ @gherkin_statement = node
28
+ end
29
+
30
+ private
31
+
32
+ def children
33
+ @steps + @examples_tables
34
+ end
35
+
36
+ def description_for_visitors
37
+ :scenario_outline
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,38 @@
1
+ require 'cucumber/core/ast/describes_itself'
2
+ require 'cucumber/core/ast/location'
3
+
4
+ module Cucumber
5
+ module Core
6
+ module Ast
7
+ class Step #:nodoc:
8
+ include HasLocation
9
+ include DescribesItself
10
+
11
+ attr_reader :keyword, :name, :language, :exception, :multiline_arg
12
+
13
+ def initialize(language, location, keyword, name, multiline_arg=nil)
14
+ @location, @keyword, @name, @multiline_arg = location, keyword, name, multiline_arg
15
+ end
16
+
17
+ def gherkin_statement(statement=nil)
18
+ @gherkin_statement ||= statement
19
+ end
20
+
21
+ def to_sexp
22
+ [:step, line, keyword, name, (@multiline_arg.nil? ? nil : @multiline_arg.to_sexp)].compact
23
+ end
24
+
25
+ private
26
+
27
+ def children
28
+ return [] unless @multiline_arg # TODO: use a null object
29
+ [@multiline_arg]
30
+ end
31
+
32
+ def description_for_visitors
33
+ :step
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,14 @@
1
+ require 'cucumber/initializer'
2
+ module Cucumber
3
+ module Core
4
+ module Ast
5
+ class Tag
6
+ include HasLocation
7
+
8
+ include Cucumber.initializer(:location, :name)
9
+
10
+ attr_reader :name
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,136 @@
1
+ require 'cucumber/initializer'
2
+ require 'cucumber/core/test/case'
3
+ require 'cucumber/core/test/step'
4
+
5
+ module Cucumber
6
+ module Core
7
+ class Compiler
8
+ include Cucumber.initializer(:receiver)
9
+
10
+ def feature(feature)
11
+ compiler = FeatureCompiler.new(TestCaseBuilder.new(receiver))
12
+ feature.describe_to(compiler)
13
+ self
14
+ end
15
+
16
+ def done
17
+ receiver.done
18
+ self
19
+ end
20
+
21
+ class TestCaseBuilder
22
+ include Cucumber.initializer(:receiver)
23
+
24
+ def on_background_step(source)
25
+ background_test_steps << Test::Step.new(source)
26
+ self
27
+ end
28
+
29
+ def on_step(source)
30
+ test_steps << Test::Step.new(source)
31
+ self
32
+ end
33
+
34
+ def on_test_case(source)
35
+ Test::Case.new(test_steps, source).describe_to(receiver)
36
+ @test_steps = nil
37
+ self
38
+ end
39
+
40
+ private
41
+
42
+ def background_test_steps
43
+ @background_test_steps ||= []
44
+ end
45
+
46
+ def test_steps
47
+ @test_steps ||= background_test_steps.dup
48
+ end
49
+ end
50
+
51
+ class FeatureCompiler
52
+ include Cucumber.initializer(:receiver)
53
+
54
+ def feature(feature, &descend)
55
+ @feature = feature
56
+ descend.call(self)
57
+ self
58
+ end
59
+
60
+ def background(background, &descend)
61
+ source = [@feature, background]
62
+ compiler = BackgroundCompiler.new(source, receiver)
63
+ descend.call(compiler)
64
+ self
65
+ end
66
+
67
+ def scenario(scenario, &descend)
68
+ source = [@feature, scenario]
69
+ scenario_compiler = ScenarioCompiler.new(source, receiver)
70
+ descend.call(scenario_compiler)
71
+ receiver.on_test_case(source)
72
+ self
73
+ end
74
+
75
+ def scenario_outline(scenario_outline, &descend)
76
+ source = [@feature, scenario_outline]
77
+ compiler = ScenarioOutlineCompiler.new(source, receiver)
78
+ descend.call(compiler)
79
+ self
80
+ end
81
+ end
82
+
83
+ class ScenarioOutlineCompiler
84
+ include Cucumber.initializer(:source, :receiver)
85
+
86
+ def outline_step(outline_step)
87
+ outline_steps << outline_step
88
+ self
89
+ end
90
+
91
+ def examples_table(examples_table, &descend)
92
+ @examples_table = examples_table
93
+ descend.call(self)
94
+ self
95
+ end
96
+
97
+ def examples_table_row(row)
98
+ steps(row).each do |step|
99
+ receiver.on_step(source + [@examples_table, row, step])
100
+ end
101
+ receiver.on_test_case(source + [@examples_table, row])
102
+ self
103
+ end
104
+
105
+ private
106
+
107
+ def steps(row)
108
+ outline_steps.map { |s| s.to_step(row) }
109
+ end
110
+
111
+ def outline_steps
112
+ @outline_steps ||= []
113
+ end
114
+ end
115
+
116
+ class ScenarioCompiler
117
+ include Cucumber.initializer(:source, :receiver)
118
+
119
+ def step(step)
120
+ receiver.on_step(source + [step])
121
+ self
122
+ end
123
+ end
124
+
125
+ class BackgroundCompiler
126
+ include Cucumber.initializer(:source, :receiver)
127
+
128
+ def step(step)
129
+ receiver.on_background_step(source + [step])
130
+ self
131
+ end
132
+ end
133
+
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,315 @@
1
+ require 'cucumber/initializer'
2
+ require 'cucumber/core/ast'
3
+ require 'cucumber/core/platform'
4
+
5
+ module Cucumber
6
+ module Core
7
+ module Gherkin
8
+ # Builds an AST of a feature by listening to events from the
9
+ # Gherkin parser.
10
+ class AstBuilder
11
+
12
+ def initialize(path)
13
+ @path = path
14
+ end
15
+
16
+ def result
17
+ return nil unless @feature_builder
18
+ @feature_builder.result(language)
19
+ end
20
+
21
+ def language=(language)
22
+ @language = language
23
+ end
24
+
25
+ def uri(uri)
26
+ @path = uri
27
+ end
28
+
29
+ def feature(node)
30
+ @feature_builder = FeatureBuilder.new(file, node)
31
+ end
32
+
33
+ def background(node)
34
+ builder = BackgroundBuilder.new(file, node)
35
+ @feature_builder.background_builder = builder
36
+ @current = builder
37
+ end
38
+
39
+ def scenario(node)
40
+ builder = ScenarioBuilder.new(file, node)
41
+ @feature_builder.add_child builder
42
+ @current = builder
43
+ end
44
+
45
+ def scenario_outline(node)
46
+ builder = ScenarioOutlineBuilder.new(file, node)
47
+ @feature_builder.add_child builder
48
+ @current = builder
49
+ end
50
+
51
+ def examples(node)
52
+ @current.add_examples file, node
53
+ end
54
+
55
+ def step(node)
56
+ @current.add_step file, node
57
+ end
58
+
59
+ def eof
60
+ end
61
+
62
+ def syntax_error(state, event, legal_events, line)
63
+ # raise "SYNTAX ERROR"
64
+ end
65
+
66
+ private
67
+
68
+ def language
69
+ @language || raise("Language has not been set")
70
+ end
71
+
72
+ def file
73
+ if Cucumber::WINDOWS && !ENV['CUCUMBER_FORWARD_SLASH_PATHS']
74
+ @path.gsub(/\//, '\\')
75
+ else
76
+ @path
77
+ end
78
+ end
79
+
80
+ class Builder
81
+ include Cucumber.initializer(:file, :node)
82
+
83
+ private
84
+
85
+ def tags
86
+ node.tags.map do |tag|
87
+ Ast::Tag.new(
88
+ Ast::Location.new(file, tag.line),
89
+ tag.name)
90
+ end
91
+ end
92
+
93
+ def location
94
+ Ast::Location.new(file, node.line)
95
+ end
96
+
97
+ def comments
98
+ node.comments.map do |comment|
99
+ Ast::Comment.new(
100
+ Ast::Location.new(file, comment.line),
101
+ comment.value
102
+ )
103
+ end
104
+ end
105
+ end
106
+
107
+ class FeatureBuilder < Builder
108
+ attr_accessor :background_builder
109
+ private :background_builder
110
+
111
+ def initialize(*)
112
+ super
113
+ @background_builder = nil
114
+ end
115
+
116
+ def result(language)
117
+ background = background(language)
118
+ feature = Ast::Feature.new(
119
+ location,
120
+ background,
121
+ comments,
122
+ tags,
123
+ node.keyword,
124
+ node.name.lstrip,
125
+ node.description.rstrip,
126
+ children.map { |builder| builder.result(background, language, tags) }
127
+ )
128
+ feature.gherkin_statement(node)
129
+ feature.language = language
130
+ feature
131
+ end
132
+
133
+ def add_child(child)
134
+ children << child
135
+ end
136
+
137
+ def children
138
+ @children ||= []
139
+ end
140
+
141
+ private
142
+
143
+ def background(language)
144
+ return Ast::EmptyBackground.new unless background_builder
145
+ @background ||= background_builder.result(language)
146
+ end
147
+ end
148
+
149
+ class BackgroundBuilder < Builder
150
+ def result(language)
151
+ Ast::Background.new(
152
+ node,
153
+ language,
154
+ location,
155
+ comments,
156
+ node.keyword,
157
+ node.name,
158
+ node.description,
159
+ steps(language)
160
+ )
161
+ end
162
+
163
+ def add_step(file, node)
164
+ step_builders << ScenarioBuilder::StepBuilder.new(file, node)
165
+ end
166
+
167
+ private
168
+
169
+ def steps(language)
170
+ step_builders.map { |step_builder| step_builder.result(language) }
171
+ end
172
+
173
+ def step_builders
174
+ @step_builders ||= []
175
+ end
176
+
177
+ end
178
+
179
+ class ScenarioBuilder < Builder
180
+ def result(background, language, feature_tags)
181
+ Ast::Scenario.new(
182
+ node,
183
+ language,
184
+ location,
185
+ background,
186
+ comments,
187
+ tags,
188
+ feature_tags,
189
+ node.keyword,
190
+ node.name,
191
+ node.description,
192
+ steps(language)
193
+ )
194
+ end
195
+
196
+ def add_step(file, node)
197
+ step_builders << StepBuilder.new(file, node)
198
+ end
199
+
200
+ private
201
+
202
+ def steps(language)
203
+ step_builders.map { |step_builder| step_builder.result(language) }
204
+ end
205
+
206
+ def step_builders
207
+ @step_builders ||= []
208
+ end
209
+
210
+ class StepBuilder < Builder
211
+ def result(language)
212
+ step = Ast::Step.new(
213
+ language,
214
+ location,
215
+ node.keyword,
216
+ node.name,
217
+ Ast::MultilineArgument.from(node.doc_string || node.rows, location)
218
+ )
219
+ step.gherkin_statement(node)
220
+ step
221
+ end
222
+ end
223
+ end
224
+
225
+ class ScenarioOutlineBuilder < Builder
226
+ def result(background, language, feature_tags)
227
+ scenario_outline = Ast::ScenarioOutline.new(
228
+ language,
229
+ location,
230
+ background,
231
+ comments,
232
+ tags,
233
+ feature_tags,
234
+ node.keyword,
235
+ node.name,
236
+ node.description,
237
+ steps(language),
238
+ examples_tables
239
+ )
240
+ scenario_outline.gherkin_statement(node)
241
+ scenario_outline
242
+ end
243
+
244
+ def add_examples(file, node)
245
+ examples_tables << ExamplesTableBuilder.new(file, node).result
246
+ end
247
+
248
+ def add_step(file, node)
249
+ step_builders << StepBuilder.new(file, node)
250
+ end
251
+
252
+ private
253
+
254
+ def steps(language)
255
+ step_builders.map { |step_builder| step_builder.result(language) }
256
+ end
257
+
258
+ def step_builders
259
+ @step_builders ||= []
260
+ end
261
+
262
+ def examples_tables
263
+ @examples_tables ||= []
264
+ end
265
+
266
+ class ExamplesTableBuilder < Builder
267
+
268
+ def result
269
+ Ast::ExamplesTable.new(
270
+ location,
271
+ comments,
272
+ tags,
273
+ node.keyword,
274
+ node.name,
275
+ node.description,
276
+ header,
277
+ example_rows
278
+ )
279
+ end
280
+
281
+ private
282
+
283
+ def header
284
+ row = node.rows[0]
285
+ Ast::ExamplesTable::Header.new(row.cells, location)
286
+ end
287
+
288
+ def example_rows
289
+ _, *raw_examples = *node.rows
290
+ raw_examples.each_with_index.map do |row, index|
291
+ header.build_row(row.cells, index + 1, location.on_line(row.line))
292
+ end
293
+ end
294
+
295
+ end
296
+
297
+ class StepBuilder < Builder
298
+ def result(language)
299
+ step = Ast::OutlineStep.new(
300
+ language,
301
+ location,
302
+ node.keyword,
303
+ node.name,
304
+ Ast::MultilineArgument.from(node.doc_string || node.rows, location)
305
+ )
306
+ step.gherkin_statement(node)
307
+ step
308
+ end
309
+ end
310
+ end
311
+
312
+ end
313
+ end
314
+ end
315
+ end