cucumber-core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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