macros4cuke 0.3.42 → 0.4.00

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +8 -8
  2. data/.rubocop.yml +20 -3
  3. data/CHANGELOG.md +15 -1
  4. data/examples/i18n/fr/features/step_definitions/use_macro_steps.rb +1 -1
  5. data/features/demo06.feature +75 -75
  6. data/features/demo07.feature +15 -0
  7. data/features/support/env.rb +6 -0
  8. data/features/support/macro_support.rb +2 -2
  9. data/lib/macro_steps.rb +19 -2
  10. data/lib/macros4cuke/coll-walker-factory.rb +119 -0
  11. data/lib/macros4cuke/constants.rb +1 -1
  12. data/lib/macros4cuke/exceptions.rb +23 -1
  13. data/lib/macros4cuke/formatter/all-notifications.rb +30 -0
  14. data/lib/macros4cuke/formatter/to-gherkin.rb +80 -0
  15. data/lib/macros4cuke/formatter/to-null.rb +86 -0
  16. data/lib/macros4cuke/formatter/to-trace.rb +100 -0
  17. data/lib/macros4cuke/formatting-service.rb +74 -0
  18. data/lib/macros4cuke/macro-collection.rb +3 -3
  19. data/lib/macros4cuke/macro-step-support.rb +1 -1
  20. data/lib/macros4cuke/macro-step.rb +10 -26
  21. data/lib/macros4cuke/templating/engine.rb +74 -29
  22. data/spec/macros4cuke/coll-walker-factory_spec.rb +269 -0
  23. data/spec/macros4cuke/formatter/to-gherkin_spec.rb +129 -0
  24. data/spec/macros4cuke/formatter/to-null_spec.rb +62 -0
  25. data/spec/macros4cuke/formatter/to-trace_spec.rb +155 -0
  26. data/spec/macros4cuke/formatting-service_spec.rb +138 -0
  27. data/spec/macros4cuke/macro-collection_spec.rb +7 -4
  28. data/spec/macros4cuke/macro-step-support_spec.rb +41 -24
  29. data/spec/macros4cuke/macro-step_spec.rb +13 -6
  30. data/spec/macros4cuke/templating/engine_spec.rb +11 -7
  31. data/spec/macros4cuke/templating/placeholder_spec.rb +1 -1
  32. data/spec/macros4cuke/templating/section_spec.rb +3 -1
  33. data/spec/macros4cuke/use-sample-collection.rb +79 -0
  34. data/spec/spec_helper.rb +3 -0
  35. metadata +15 -2
@@ -23,6 +23,9 @@ class MacroStep
23
23
  # A template engine that expands the sub-steps upon request.
24
24
  attr_reader(:renderer)
25
25
 
26
+ # The sentence fragment that defines the syntax of the macro-step
27
+ attr_reader(:phrase)
28
+
26
29
  # Unique key of the macro as derived from the macro phrase.
27
30
  attr_reader(:key)
28
31
 
@@ -30,7 +33,7 @@ class MacroStep
30
33
  attr_reader(:phrase_args)
31
34
 
32
35
  # The list of macro argument names (as appearing in the substeps
33
- # and in the macro phrase).
36
+ # AND in the macro phrase).
34
37
  attr_reader(:args)
35
38
 
36
39
  # Constructor.
@@ -41,15 +44,12 @@ class MacroStep
41
44
  # @param useTable [boolean] A flag indicating whether a data table
42
45
  # must be used to pass actual values.
43
46
  def initialize(aMacroPhrase, theSubsteps, useTable)
47
+ @phrase = aMacroPhrase
44
48
  @key = self.class.macro_key(aMacroPhrase, useTable, :definition)
45
49
 
46
50
  # Retrieve the macro arguments embedded in the phrase.
47
51
  @phrase_args = scan_arguments(aMacroPhrase, :definition)
48
-
49
- # Manipulate the substeps source text
50
- substeps_processed = preprocess(theSubsteps)
51
-
52
- @renderer = Templating::Engine.new(substeps_processed)
52
+ @renderer = Templating::Engine.new(theSubsteps)
53
53
  substeps_vars = renderer.variables
54
54
 
55
55
 
@@ -165,17 +165,15 @@ private
165
165
  # @param params [Hash] The pairs phrase argument name => value
166
166
  def validate_row(a_row, params)
167
167
  (a_key, value) = a_row
168
- raise UnknownArgumentError.new(a_key) unless args.include? a_key
168
+ fail(UnknownArgumentError.new(a_key)) unless args.include? a_key
169
169
  if (phrase_args.include? a_key) && (params[a_key] != value)
170
- raise AmbiguousArgumentValue.new(a_key, params[a_key], value)
170
+ fail(AmbiguousArgumentValue.new(a_key, params[a_key], value))
171
171
  end
172
172
 
173
173
  return a_row
174
174
  end
175
175
 
176
176
 
177
-
178
-
179
177
  # Retrieve from the macro phrase, all the text between <..> or double quotes.
180
178
  # Returns an array. Each of its elements corresponds to quoted text.
181
179
  # Example:
@@ -201,20 +199,6 @@ private
201
199
 
202
200
  return args
203
201
  end
204
-
205
- # Return the substeps text after some transformation
206
- # [theSubstepsSource] The source text of the steps
207
- # to be expanded upon macro invokation.
208
- def preprocess(theSubstepsSource)
209
- # Split text into lines
210
- lines = theSubstepsSource.split(/\r\n?|\n/)
211
-
212
- # Reject comment lines. This is necessary because
213
- # Cucumber::RbSupport::RbWorld#steps complains when it sees a comment.
214
- processed = lines.reject { |a_line| a_line =~ /\s*#/ }
215
-
216
- return processed.join("\n")
217
- end
218
202
 
219
203
  # Check for inconsistencies between the argument names
220
204
  # in the phrase and the substeps part.
@@ -222,7 +206,7 @@ private
222
206
  # Error when the phrase names an argument that never occurs in the substeps
223
207
  thePhraseArgs.each do |phrase_arg|
224
208
  unless substepsVars.include? phrase_arg
225
- raise UselessPhraseArgument.new(phrase_arg)
209
+ fail(UselessPhraseArgument.new(phrase_arg))
226
210
  end
227
211
  end
228
212
  # Error when a substep has an argument that never appears in the phrase
@@ -231,7 +215,7 @@ private
231
215
  substepsVars.each do |substep_arg|
232
216
  unless thePhraseArgs.include?(substep_arg) ||
233
217
  BuiltinParameters.include?(substep_arg)
234
- raise UnreachableSubstepArgument.new(substep_arg)
218
+ fail(UnreachableSubstepArgument.new(substep_arg))
235
219
  end
236
220
  end
237
221
  end
@@ -38,6 +38,34 @@ class StaticText
38
38
  end # class
39
39
 
40
40
 
41
+ # Class used internally by the template engine.
42
+ # Represents a comment from a template.
43
+ # A static text is a text that is reproduced verbatim
44
+ # when rendering a template.
45
+ class Comment
46
+ # The comment as extracted from the original template.
47
+ attr_reader(:source)
48
+
49
+
50
+ # @param aSourceText [String] A piece of text extracted
51
+ # from the template that must be rendered verbatim.
52
+ def initialize(aSourceText)
53
+ @source = aSourceText
54
+ end
55
+
56
+ public
57
+
58
+ # Render the comment.
59
+ # Comments are rendered as empty text. This is necessary because
60
+ # Cucumber::RbSupport::RbWorld#steps complains when it sees a comment.
61
+ # This method has the same signature as the {Engine#render} method.
62
+ # @return [String] Empty string ("as is")
63
+ def render(aContextObject, theLocals)
64
+ return ''
65
+ end
66
+ end # class
67
+
68
+
41
69
  # Class used internally by the template engine.
42
70
  # Represents an end of line that must be rendered as such.
43
71
  class EOLine
@@ -160,7 +188,7 @@ class Section < UnaryElement
160
188
  # Returns an empty string when no value is assigned to the placeholder.
161
189
  def render(aContextObject, theLocals)
162
190
  msg = "Method Section.#{__method__} must be implemented in subclass."
163
- raise NotImplementedError, msg
191
+ fail(NotImplementedError, msg)
164
192
  end
165
193
 
166
194
  end # class
@@ -239,6 +267,9 @@ class Engine
239
267
  # The original text of the template is kept here.
240
268
  attr_reader(:source)
241
269
 
270
+ # The internal representation of the template text
271
+ attr_reader(:representation)
272
+
242
273
  # Builds an Engine and compiles the given template text into
243
274
  # an internal representation.
244
275
  # @param aSourceTemplate [String] The template source text.
@@ -260,11 +291,19 @@ public
260
291
  # the passed argument values.
261
292
  def render(aContextObject = Object.new, theLocals)
262
293
  return '' if @representation.empty?
263
-
294
+
295
+ prev = nil
264
296
  result = @representation.each_with_object('') do |element, subResult|
265
- subResult << element.render(aContextObject, theLocals)
297
+ # Output compaction rules:
298
+ # -In case of consecutive eol's only one is rendered.
299
+ # -In case of comment followed by one eol, both aren't rendered
300
+ unless element.is_a?(EOLine) &&
301
+ (prev.is_a?(EOLine) || prev.is_a?(Comment))
302
+ subResult << element.render(aContextObject, theLocals)
303
+ end
304
+ prev = element
266
305
  end
267
-
306
+
268
307
  return result
269
308
  end
270
309
 
@@ -295,24 +334,28 @@ public
295
334
 
296
335
  # Class method. Parse the given line text into a raw representation.
297
336
  # @return [Array] Couples of the form:
298
- # [:static, text] or [:dynamic, tag text]
337
+ # [:static, text], [:comment, text] or [:dynamic, tag text]
299
338
  def self.parse(aTextLine)
300
339
  scanner = StringScanner.new(aTextLine)
301
340
  result = []
302
-
303
- until scanner.eos?
304
- # Scan tag at current position...
305
- tag_literal = scanner.scan(/<(?:[^\\<>]|\\.)*>/)
306
- unless tag_literal.nil?
307
- result << [:dynamic, tag_literal.gsub(/^<|>$/, '')]
341
+
342
+ if scanner.check(/\s*#/) # Detect comment line
343
+ result << [:comment, aTextLine]
344
+ else
345
+ until scanner.eos?
346
+ # Scan tag at current position...
347
+ tag_literal = scanner.scan(/<(?:[^\\<>]|\\.)*>/)
348
+ unless tag_literal.nil?
349
+ result << [:dynamic, tag_literal.gsub(/^<|>$/, '')]
350
+ end
351
+
352
+ # ... or scan plain text at current position
353
+ literal = scanner.scan(/(?:[^\\<>]|\\.)+/)
354
+ result << [:static, literal] unless literal.nil?
355
+ identify_parse_error(aTextLine) if tag_literal.nil? && literal.nil?
308
356
  end
309
-
310
- # ... or scan plain text at current position
311
- text_literal = scanner.scan(/(?:[^\\<>]|\\.)+/)
312
- result << [:static, text_literal] unless text_literal.nil?
313
- identify_parse_error(aTextLine) if tag_literal.nil? && text_literal.nil?
314
357
  end
315
-
358
+
316
359
  return result
317
360
  end
318
361
 
@@ -336,11 +379,11 @@ private
336
379
  when '>' then unbalance -= 1
337
380
  end
338
381
 
339
- raise StandardError, "Nested opening chevron '<'." if unbalance > 1
340
- raise StandardError, "Missing opening chevron '<'." if unbalance < 0
382
+ fail(StandardError, "Nested opening chevron '<'.") if unbalance > 1
383
+ fail(StandardError, "Missing opening chevron '<'.") if unbalance < 0
341
384
  end
342
385
 
343
- raise StandardError, "Missing closing chevron '>'." if unbalance == 1
386
+ fail(StandardError, "Missing closing chevron '>'.") if unbalance == 1
344
387
  end
345
388
 
346
389
 
@@ -355,7 +398,7 @@ private
355
398
  line_items.each do |(kind, text)|
356
399
  # A tag text cannot be empty nor blank
357
400
  if (kind == :dynamic) && text.strip.empty?
358
- raise EmptyArgumentError.new(line.strip)
401
+ fail(EmptyArgumentError.new(line.strip))
359
402
  end
360
403
  end
361
404
 
@@ -390,7 +433,7 @@ private
390
433
  false
391
434
  end
392
435
  end
393
- if line_to_squeeze && ! section_item.nil?
436
+ if line_to_squeeze && !section_item.nil?
394
437
  line_rep = [section_item]
395
438
  else
396
439
  line_rep_ending(line_rep)
@@ -398,7 +441,7 @@ private
398
441
 
399
442
  return line_rep
400
443
  end
401
-
444
+
402
445
 
403
446
  # Apply rule: if last item in line is an end of section marker,
404
447
  # then place eoline before that item.
@@ -418,15 +461,17 @@ private
418
461
  # Where kind must be one of :static, :dynamic
419
462
  def compile_couple(aCouple)
420
463
  (kind, text) = aCouple
421
-
464
+
422
465
  result = case kind
423
466
  when :static then StaticText.new(text)
467
+ when :comment then Comment.new(text)
424
468
  when :dynamic then parse_tag(text)
425
469
  end
426
470
 
427
471
  return result
428
472
  end
429
-
473
+
474
+
430
475
  # Parse the contents of a tag entry.
431
476
  # @param aText [String] The text that is enclosed between chevrons.
432
477
  def parse_tag(aText)
@@ -437,7 +482,7 @@ private
437
482
  # Disallow punctuation and delimiter signs in tags.
438
483
  matching = DisallowedSigns.match(aText)
439
484
  end
440
- raise InvalidCharError.new(aText, matching[0]) if matching
485
+ fail(InvalidCharError.new(aText, matching[0])) if matching
441
486
 
442
487
  result = case aText[0, 1]
443
488
  when '?'
@@ -477,7 +522,7 @@ private
477
522
 
478
523
  unless open_sections.empty?
479
524
  error_message = "Unterminated section #{open_sections.last}."
480
- raise StandardError, error_message
525
+ fail(StandardError, error_message)
481
526
  end
482
527
 
483
528
  return compiled
@@ -490,11 +535,11 @@ private
490
535
 
491
536
  if sections.empty?
492
537
  msg = 'found while no corresponding section is open.'
493
- raise StandardError, msg_prefix + msg
538
+ fail(StandardError, msg_prefix + msg)
494
539
  end
495
540
  if marker.name != sections.last.name
496
541
  msg = "doesn't match current section '#{sections.last.name}'."
497
- raise StandardError, msg_prefix + msg
542
+ fail(StandardError, msg_prefix + msg)
498
543
  end
499
544
  end
500
545
 
@@ -0,0 +1,269 @@
1
+ # File: collection-walker_spec.rb
2
+
3
+ require_relative '../spec_helper'
4
+
5
+ # Load mix-in module for creating a sample collection of macro-steps
6
+ require_relative 'use-sample-collection'
7
+
8
+ # Load the class under test
9
+ require_relative '../../lib/macros4cuke/coll-walker-factory'
10
+
11
+ module Macros4Cuke # Open this namespace to avoid module qualifier prefixes
12
+
13
+ describe CollWalkerFactory do
14
+ include UseSampleCollection # Add convenience methods for sample collection
15
+
16
+
17
+ before(:all) do
18
+ # Fill the collection of macro-steps with sample steps
19
+ fill_collection
20
+ end
21
+
22
+ after(:all) do
23
+ # Clear the collection to prevent interference between spec files
24
+ macro_coll.clear
25
+ end
26
+
27
+ context 'Initialization:' do
28
+ it 'should be created without parameter' do
29
+ expect { CollWalkerFactory.new }.not_to raise_error
30
+ end
31
+
32
+ end # context
33
+
34
+ context 'Provided factory services:' do
35
+ # Default factory instantiation
36
+ subject { CollWalkerFactory.new }
37
+
38
+ it 'should build a walker for the given macro collection' do
39
+ walker = subject.build_walker(macro_coll)
40
+ expect(walker).to be_kind_of(Enumerator)
41
+ end
42
+ end # context
43
+
44
+ context 'Provided walker services:' do
45
+ # Default walker instantiation
46
+ subject do
47
+ factory = CollWalkerFactory.new
48
+ factory.build_walker(macro_coll)
49
+ end
50
+
51
+
52
+ it 'should notify the start of the visit of the collection' do
53
+ initial_event = subject.next
54
+ expect(initial_event).to eq([:on_collection, 0, macro_coll])
55
+ end
56
+
57
+ it 'should notify the visit of a first macro step' do
58
+ 1.times { subject.next }
59
+ first_step = subject.next
60
+ step1 = macro_coll.macro_steps.values[0]
61
+ expect(first_step).to eq([:on_step, 1, step1])
62
+ end
63
+
64
+ it 'should notify the visit of the phrase of the first macro step' do
65
+ 2.times { subject.next }
66
+ first_phrase = subject.next
67
+ sample_phrase1 = UseSampleCollection::SamplePhrase1
68
+ expect(first_phrase).to eq([:on_phrase, 2, sample_phrase1, true])
69
+ end
70
+
71
+ it 'should notify the visit of the substeps renderer of the first step' do
72
+ 3.times { subject.next }
73
+ first_step = macro_coll.macro_steps.values[0]
74
+ first_renderer = subject.next
75
+ expectation = [:on_renderer, 2, first_step.renderer]
76
+ expect(first_renderer).to eq(expectation)
77
+ end
78
+
79
+ it 'should notify the visit of the internal representation of substeps' do
80
+ first_step = macro_coll.macro_steps.values[0]
81
+ substep_chunks = first_step.renderer.representation
82
+ 4.times { subject.next }
83
+ text_representation = subject.next
84
+ expectation = [:on_source, 3, first_step.renderer.source]
85
+ expect(text_representation).to eq(expectation)
86
+
87
+ first_substep_piece = subject.next
88
+ expectation = [:on_static_text, 3, substep_chunks[0].source]
89
+ expect(first_substep_piece).to eq(expectation)
90
+
91
+ second_substep_piece = subject.next
92
+ expect(second_substep_piece).to eq([:on_eol, 3, nil])
93
+
94
+ third_substep_piece = subject.next
95
+ expectation = [:on_static_text, 3, substep_chunks[2].source]
96
+ expect(third_substep_piece).to eq(expectation)
97
+
98
+ fourth_substep_piece = subject.next
99
+ expect(fourth_substep_piece).to eq([:on_eol, 3, nil])
100
+
101
+ fifth_substep_piece = subject.next
102
+ expectation = [:on_static_text, 3, substep_chunks[4].source]
103
+ expect(fifth_substep_piece).to eq(expectation)
104
+
105
+ sixth_substep_piece = subject.next
106
+ expectation = [:on_placeholder, 3, substep_chunks[5].name]
107
+ expect(sixth_substep_piece).to eq(expectation)
108
+
109
+ seventh_substep_piece = subject.next
110
+ expectation = [:on_static_text, 3, substep_chunks[6].source]
111
+ expect(seventh_substep_piece).to eq(expectation)
112
+
113
+ eighth_substep_piece = subject.next
114
+ expect(eighth_substep_piece).to eq([:on_eol, 3, nil])
115
+
116
+ ninth_substep_piece = subject.next
117
+ expectation = [:on_static_text, 3, substep_chunks[8].source]
118
+ expect(ninth_substep_piece).to eq(expectation)
119
+
120
+ tenth_substep_piece = subject.next
121
+ expectation = [:on_placeholder, 3, substep_chunks[9].name]
122
+ expect(tenth_substep_piece).to eq(expectation)
123
+
124
+ eleventh_substep_piece = subject.next
125
+ expectation = [:on_static_text, 3, substep_chunks[10].source]
126
+ expect(eleventh_substep_piece).to eq(expectation)
127
+
128
+ twelfth_substep_piece = subject.next
129
+ expect(twelfth_substep_piece).to eq([:on_eol, 3, nil])
130
+
131
+ thirtieth_substep_piece = subject.next
132
+ expectation = [:on_static_text, 3, substep_chunks[12].source]
133
+ expect(thirtieth_substep_piece).to eq(expectation)
134
+
135
+ fourteenth_substep_piece = subject.next
136
+ expect(fourteenth_substep_piece).to eq([:on_eol, 3, nil])
137
+
138
+ fifteenth_substep_piece = subject.next
139
+ expect(fifteenth_substep_piece).to eq([:on_renderer_end, 2, nil])
140
+ end
141
+
142
+ it 'should notify the visit of a following macro step' do
143
+ (4 + 15 + 1).times { subject.next }
144
+ end_step = subject.next
145
+ expect(end_step).to eq([:on_step_end, 1, nil])
146
+ a_step = subject.next
147
+ expect(a_step).to eq([:on_step, 1, macro_coll.macro_steps.values[1]])
148
+ end
149
+
150
+ it 'should notify the visit of the phrase of a following macro step' do
151
+ (4 + 16 + 2).times { subject.next }
152
+ phrase = subject.next
153
+ sample_phrase = UseSampleCollection::SamplePhrase2
154
+ expect(phrase).to eq([:on_phrase, 2, sample_phrase, true])
155
+ end
156
+
157
+ it 'should notify the visit of the substeps of a following step' do
158
+ (4 + 16 + 3).times { subject.next }
159
+ a_renderer = subject.next
160
+ second_step = macro_coll.macro_steps.values[1]
161
+ expect(a_renderer).to eq([:on_renderer, 2, second_step.renderer])
162
+ end
163
+
164
+ it 'should notify the visit in substeps of following step' do
165
+ second_step = macro_coll.macro_steps.values[1]
166
+ substep_chunks = second_step.renderer.representation.dup
167
+ substep_chunks.map! do |ck|
168
+ if ck.kind_of?(Templating::Section)
169
+ [ck, ck.children, ck].flatten
170
+ else
171
+ ck
172
+ end
173
+ end
174
+ substep_chunks.flatten!
175
+
176
+ (4 + 16 + 4).times { subject.next }
177
+ substeps_text = subject.next
178
+ expectation = [:on_source, 3, second_step.renderer.source]
179
+ expect(substeps_text).to eq(expectation)
180
+
181
+ first_substep_piece = subject.next
182
+ expectation = [:on_static_text, 3, substep_chunks[0].source]
183
+ expect(first_substep_piece).to eq(expectation)
184
+
185
+ [
186
+ [:on_placeholder, 3, :name], # 0
187
+ [:on_static_text, 3, :source], # 1
188
+ [:on_eol, 3, nil], # 2
189
+ [:on_static_text, 3, :source], # 3
190
+ [:on_placeholder, 3, :name], # 4
191
+ [:on_static_text, 3, :source], # 5
192
+ [:on_eol, 3, nil], # 6
193
+ [:on_static_text, 3, :source], # 7
194
+ [:on_placeholder, 3, :name], # 8
195
+ [:on_static_text, 3, :source], # 9
196
+ [:on_eol, 3, nil], # 10
197
+ [:on_static_text, 3, :source], # 11
198
+ [:on_placeholder, 3, :name], # 12
199
+ [:on_static_text, 3, :source], # 13
200
+ [:on_eol, 3, nil], # 14
201
+ [:on_static_text, 3, :source], # 15
202
+ [:on_placeholder, 3, :name], # 16
203
+ [:on_static_text, 3, :source], # 17
204
+ [:on_eol, 3, nil], # 18
205
+ [:on_static_text, 3, :source], # 19
206
+ [:on_placeholder, 3, :name], # 20
207
+ [:on_static_text, 3, :source], # 21
208
+ [:on_eol, 3, nil], # 22
209
+ [:on_eol, 3, nil], # 23
210
+ [:on_comment, 3, :source], # 24
211
+ [:on_eol, 3, nil], # 25
212
+ [:on_comment, 3, :source], # 26
213
+ [:on_eol, 3, nil], # 27
214
+ [:on_comment, 3, :source], # 28
215
+ [:on_eol, 3, nil], # 29
216
+ [:on_section, 3, :name], # 30
217
+ [:on_static_text, 4, :source], # 31
218
+ [:on_placeholder, 4, :name], # 32
219
+ [:on_static_text, 4, :source], # 33
220
+ [:on_eol, 4, nil], # 34
221
+ [:on_section_end, 3, nil], # 35
222
+ [:on_eol, 3, nil], # 36
223
+ [:on_comment, 3, :source], # 37
224
+ [:on_eol, 3, nil], # 38
225
+ [:on_comment, 3, :source], # 39
226
+ [:on_eol, 3, nil], # 40
227
+ [:on_comment, 3, :source], # 41
228
+ [:on_eol, 3, nil], # 42
229
+ [:on_section, 3, :name], # 43
230
+ [:on_static_text, 4, :source], # 44
231
+ [:on_placeholder, 4, :name], # 45
232
+ [:on_static_text, 4, :source], # 46
233
+ [:on_eol, 4, nil], # 47
234
+ [:on_section_end, 3, nil], # 48
235
+ [:on_static_text, 3, :source], # 49
236
+ [:on_eol, 3, nil], # 50
237
+ [:on_renderer_end, 2, nil], # 51
238
+ [:on_step_end, 1, nil], # 52
239
+ [:on_collection_end, 0, nil] # 53
240
+ ].each_with_index do |event, i|
241
+ actual = subject.next
242
+ expect(actual[0]).to eq(event[0])
243
+ expect(actual[1]).to eq(event[1])
244
+ unless event[2].nil?
245
+ expected_obj = substep_chunks[i + 1]
246
+ expect(actual[2]).to eq(expected_obj.send(event[2]))
247
+ end
248
+ end
249
+ expect { subject.next }.to raise_error(StopIteration)
250
+
251
+ end
252
+
253
+ # Must be last test script since it pollutes the macro-collection
254
+ it 'should complain when visiting an unsupported node' do
255
+ first_step = macro_coll.macro_steps.values[0]
256
+ first_step.renderer.representation.insert(2, :not_a_valid_element)
257
+ err_type = Macros4Cuke::InternalError
258
+ err_msg = "Don't know how to format a Symbol."
259
+ expect { subject.each { |x| } }.to raise_error(err_type, err_msg)
260
+ end
261
+
262
+ end # context
263
+
264
+ end # describe
265
+
266
+ end # module
267
+
268
+
269
+ # End of file