macros4cuke 0.3.42 → 0.4.00
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.
- checksums.yaml +8 -8
- data/.rubocop.yml +20 -3
- data/CHANGELOG.md +15 -1
- data/examples/i18n/fr/features/step_definitions/use_macro_steps.rb +1 -1
- data/features/demo06.feature +75 -75
- data/features/demo07.feature +15 -0
- data/features/support/env.rb +6 -0
- data/features/support/macro_support.rb +2 -2
- data/lib/macro_steps.rb +19 -2
- data/lib/macros4cuke/coll-walker-factory.rb +119 -0
- data/lib/macros4cuke/constants.rb +1 -1
- data/lib/macros4cuke/exceptions.rb +23 -1
- data/lib/macros4cuke/formatter/all-notifications.rb +30 -0
- data/lib/macros4cuke/formatter/to-gherkin.rb +80 -0
- data/lib/macros4cuke/formatter/to-null.rb +86 -0
- data/lib/macros4cuke/formatter/to-trace.rb +100 -0
- data/lib/macros4cuke/formatting-service.rb +74 -0
- data/lib/macros4cuke/macro-collection.rb +3 -3
- data/lib/macros4cuke/macro-step-support.rb +1 -1
- data/lib/macros4cuke/macro-step.rb +10 -26
- data/lib/macros4cuke/templating/engine.rb +74 -29
- data/spec/macros4cuke/coll-walker-factory_spec.rb +269 -0
- data/spec/macros4cuke/formatter/to-gherkin_spec.rb +129 -0
- data/spec/macros4cuke/formatter/to-null_spec.rb +62 -0
- data/spec/macros4cuke/formatter/to-trace_spec.rb +155 -0
- data/spec/macros4cuke/formatting-service_spec.rb +138 -0
- data/spec/macros4cuke/macro-collection_spec.rb +7 -4
- data/spec/macros4cuke/macro-step-support_spec.rb +41 -24
- data/spec/macros4cuke/macro-step_spec.rb +13 -6
- data/spec/macros4cuke/templating/engine_spec.rb +11 -7
- data/spec/macros4cuke/templating/placeholder_spec.rb +1 -1
- data/spec/macros4cuke/templating/section_spec.rb +3 -1
- data/spec/macros4cuke/use-sample-collection.rb +79 -0
- data/spec/spec_helper.rb +3 -0
- metadata +15 -2
|
@@ -84,7 +84,8 @@ end # class
|
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
|
|
87
|
-
# Raised when one invokes a macro-step without a required data table argument
|
|
87
|
+
# Raised when one invokes a macro-step without a required data table argument.
|
|
88
|
+
# The exception is raised only only via Cucumber (steps)
|
|
88
89
|
class DataTableNotFound < Macros4CukeError
|
|
89
90
|
def initialize(phrase)
|
|
90
91
|
msg = "The step with phrase [#{phrase}]: requires a data table."
|
|
@@ -93,6 +94,27 @@ class DataTableNotFound < Macros4CukeError
|
|
|
93
94
|
end # class
|
|
94
95
|
|
|
95
96
|
|
|
97
|
+
# Raised when the list of formatting events provided by a macro-step formatter
|
|
98
|
+
# is nil or empty.
|
|
99
|
+
class NoFormattingEventForFormatter < Macros4CukeError
|
|
100
|
+
def initialize(aFormatter)
|
|
101
|
+
msg = "Formatter #{aFormatter.class}"
|
|
102
|
+
msg << ' does not support any formatting event.'
|
|
103
|
+
super(msg)
|
|
104
|
+
end
|
|
105
|
+
end # class
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# Raised when a macro-step formatter uses an unknown formatting event.
|
|
109
|
+
class UnknownFormattingEvent < Macros4CukeError
|
|
110
|
+
def initialize(aFormatter, anEvent)
|
|
111
|
+
msg = "Formatter #{aFormatter.class}"
|
|
112
|
+
msg << " uses the unknown formatting event '#{anEvent}'."
|
|
113
|
+
super(msg)
|
|
114
|
+
end
|
|
115
|
+
end # class
|
|
116
|
+
|
|
117
|
+
|
|
96
118
|
# Raised when Macros4Cuke encountered an issue
|
|
97
119
|
# that it can't handle properly.
|
|
98
120
|
class InternalError < Macros4CukeError
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# File: all_notifications.rb
|
|
2
|
+
|
|
3
|
+
module Macros4Cuke # Module used as a namespace
|
|
4
|
+
|
|
5
|
+
# Namespace for all formatters of MacroCollection and MacroStep objects
|
|
6
|
+
module Formatter
|
|
7
|
+
|
|
8
|
+
# The list of all formatting notifications
|
|
9
|
+
AllNotifications = [
|
|
10
|
+
:on_collection,
|
|
11
|
+
:on_collection_end,
|
|
12
|
+
:on_step,
|
|
13
|
+
:on_step_end,
|
|
14
|
+
:on_phrase,
|
|
15
|
+
:on_renderer,
|
|
16
|
+
:on_renderer_end,
|
|
17
|
+
:on_source,
|
|
18
|
+
:on_static_text,
|
|
19
|
+
:on_eol,
|
|
20
|
+
:on_comment,
|
|
21
|
+
:on_placeholder,
|
|
22
|
+
:on_section,
|
|
23
|
+
:on_section_end
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
end # module
|
|
27
|
+
|
|
28
|
+
end # module
|
|
29
|
+
|
|
30
|
+
# End of file
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# File: to-gherkin.rb
|
|
2
|
+
|
|
3
|
+
require_relative '../constants'
|
|
4
|
+
|
|
5
|
+
module Macros4Cuke # Module used as a namespace
|
|
6
|
+
|
|
7
|
+
# Namespace for all formatters of MacroCollection and MacroStep objects
|
|
8
|
+
module Formatter
|
|
9
|
+
|
|
10
|
+
# A macro-step formatter that outputs to the given IO the macro-steps from a
|
|
11
|
+
# macro collection into a Gherkin feature file.
|
|
12
|
+
class ToGherkin
|
|
13
|
+
# The IO where the formatter's output will be written to.
|
|
14
|
+
attr_reader(:io)
|
|
15
|
+
|
|
16
|
+
# The number of macro-step encountered by the formatter.
|
|
17
|
+
attr_reader(:step_count)
|
|
18
|
+
|
|
19
|
+
def initialize(anIO)
|
|
20
|
+
@io = anIO
|
|
21
|
+
@step_count = 0
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
public
|
|
25
|
+
# Tell which notifications this formatter subscribes to.
|
|
26
|
+
def implements()
|
|
27
|
+
return [:on_collection, :on_step, :on_step_end, :on_phrase, :on_source]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def on_collection(aLevel, aMacroCollection)
|
|
31
|
+
io.print "# Feature file generated by Macro4Cuke #{Macros4Cuke::Version}"
|
|
32
|
+
io.puts " on #{Time.now.strftime('%d/%m/%Y %H:%M:%S')}"
|
|
33
|
+
io.puts ''
|
|
34
|
+
io.puts 'Feature: the set of macro-step definitions'
|
|
35
|
+
io.puts "#{indentation(1)}As a feature file writer"
|
|
36
|
+
io.puts "#{indentation(1)}So that I write higher-level steps"
|
|
37
|
+
io.puts ''
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def on_step(aLevel, aMacroStep)
|
|
41
|
+
@step_count += 1
|
|
42
|
+
io.puts "#{indentation(aLevel)}Scenario: Macro #{step_count}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def on_step_end(aLevel)
|
|
46
|
+
io.puts ''
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def on_phrase(aLevel, aPhraseText, useTable)
|
|
51
|
+
suffix = useTable ? ':' : ''
|
|
52
|
+
io.print "#{indentation(aLevel)}Given I define the step "
|
|
53
|
+
io.puts %Q|"* I [#{aPhraseText}]#{suffix}" to mean:|
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def on_source(aLevel, aSourceText)
|
|
57
|
+
ljust = indentation(aLevel)
|
|
58
|
+
triple_quotes = %Q|#{ljust}"""|
|
|
59
|
+
io.puts triple_quotes
|
|
60
|
+
|
|
61
|
+
# Indent source text
|
|
62
|
+
indented_text = aSourceText.gsub(/^/m, "#{ljust}")
|
|
63
|
+
|
|
64
|
+
io.puts indented_text
|
|
65
|
+
io.puts triple_quotes
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
def indentation(aLevel)
|
|
70
|
+
return ' ' * (aLevel)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
end # class
|
|
75
|
+
|
|
76
|
+
end # module
|
|
77
|
+
|
|
78
|
+
end # module
|
|
79
|
+
|
|
80
|
+
# End of file
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# File: to-null.rb
|
|
2
|
+
|
|
3
|
+
require_relative 'all-notifications'
|
|
4
|
+
|
|
5
|
+
module Macros4Cuke # Module used as a namespace
|
|
6
|
+
|
|
7
|
+
# Namespace for all formatters of MacroCollection and MacroStep objects
|
|
8
|
+
module Formatter
|
|
9
|
+
|
|
10
|
+
# A macro-step formatter that doesn't produce any output.
|
|
11
|
+
# It fully implements the expected interface of formatters but
|
|
12
|
+
# its methods are NOOP (i.e. they do nothing).
|
|
13
|
+
# This formatter can be useful when one wants to discard
|
|
14
|
+
# any formatted output.
|
|
15
|
+
class ToNull
|
|
16
|
+
|
|
17
|
+
public
|
|
18
|
+
# Tell which notifications the formatter subscribes to.
|
|
19
|
+
def implements()
|
|
20
|
+
return Formatter::AllNotifications
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def on_collection(aLevel, aMacroCollection)
|
|
24
|
+
; # Do nothing
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def on_collection_end(aLevel)
|
|
28
|
+
; # Do nothing
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def on_step(aLevel, aMacroStep)
|
|
32
|
+
; # Do nothing
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def on_step_end(aLevel)
|
|
36
|
+
; # Do nothing
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def on_phrase(aLevel, aPhraseText, useTable)
|
|
40
|
+
; # Do nothing
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def on_renderer(aLevel, aRenderer)
|
|
44
|
+
; # Do nothing
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def on_renderer_end(aLevel)
|
|
48
|
+
; # Do nothing
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def on_source(aLevel, aSourceText)
|
|
52
|
+
; # Do nothing
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def on_static_text(aLevel, aText)
|
|
56
|
+
; # Do nothing
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def on_comment(aLevel, aComment)
|
|
60
|
+
; # Do nothing
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def on_eol(aLevel)
|
|
64
|
+
; # Do nothing
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def on_placeholder(aLevel, aPlaceHolderName)
|
|
68
|
+
; # Do nothing
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def on_section(aLevel, aSectionName)
|
|
72
|
+
; # Do Nothing
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def on_section_end(aLevel)
|
|
76
|
+
; # Do Nothing
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
end # class
|
|
81
|
+
|
|
82
|
+
end # module
|
|
83
|
+
|
|
84
|
+
end # module
|
|
85
|
+
|
|
86
|
+
# End of file
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# File: to-trace.rb
|
|
2
|
+
|
|
3
|
+
require_relative 'all-notifications'
|
|
4
|
+
|
|
5
|
+
module Macros4Cuke # Module used as a namespace
|
|
6
|
+
|
|
7
|
+
# Namespace for all formatters of MacroCollection and MacroStep objects
|
|
8
|
+
module Formatter
|
|
9
|
+
|
|
10
|
+
# A macro-step formatter that outputs in the given IO the formatting events.
|
|
11
|
+
# Can be useful in tracing the visit sequence inside
|
|
12
|
+
# a given macro-step collection.
|
|
13
|
+
class ToTrace
|
|
14
|
+
# The IO where the formatter's output will be written to.
|
|
15
|
+
attr_reader(:io)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def initialize(anIO)
|
|
19
|
+
@io = anIO
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
public
|
|
23
|
+
# Tell which notifications the formatter subscribes to.
|
|
24
|
+
def implements()
|
|
25
|
+
return Formatter::AllNotifications
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def on_collection(aLevel, aMacroCollection)
|
|
29
|
+
trace_event(aLevel, __method__)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def on_collection_end(aLevel)
|
|
33
|
+
trace_event(aLevel, __method__)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def on_step(aLevel, aMacroStep)
|
|
37
|
+
trace_event(aLevel, __method__)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def on_step_end(aLevel)
|
|
41
|
+
trace_event(aLevel, __method__)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def on_phrase(aLevel, aPhraseText, useTable)
|
|
45
|
+
trace_event(aLevel, __method__)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def on_renderer(aLevel, aRenderer)
|
|
49
|
+
trace_event(aLevel, __method__)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def on_renderer_end(aLevel)
|
|
53
|
+
trace_event(aLevel, __method__)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def on_source(aLevel, aSourceText)
|
|
57
|
+
trace_event(aLevel, __method__)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def on_static_text(aLevel, aStaticText)
|
|
61
|
+
trace_event(aLevel, __method__)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def on_comment(aLevel, aComment)
|
|
65
|
+
trace_event(aLevel, __method__)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def on_eol(aLevel)
|
|
69
|
+
trace_event(aLevel, __method__)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def on_placeholder(aLevel, aPlaceHolderName)
|
|
73
|
+
trace_event(aLevel, __method__)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def on_section(aLevel, aSectionName)
|
|
77
|
+
trace_event(aLevel, __method__)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def on_section_end(aLevel)
|
|
81
|
+
trace_event(aLevel, __method__)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
def indentation(aLevel)
|
|
86
|
+
return ' ' * aLevel
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def trace_event(aLevel, anEvent)
|
|
90
|
+
io.puts "#{indentation(aLevel)}#{anEvent}"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
end # class
|
|
95
|
+
|
|
96
|
+
end # module
|
|
97
|
+
|
|
98
|
+
end # module
|
|
99
|
+
|
|
100
|
+
# End of file
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# File: formatting-service.rb
|
|
2
|
+
# Purpose: Implementation of the WalkEventPublisher class.
|
|
3
|
+
|
|
4
|
+
require_relative 'exceptions'
|
|
5
|
+
require_relative 'coll-walker-factory'
|
|
6
|
+
require_relative './formatter/all-notifications'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
module Macros4Cuke # Module used as a namespace
|
|
10
|
+
|
|
11
|
+
# A worker class that drives the rendering of macro-steps in
|
|
12
|
+
# any registered format.
|
|
13
|
+
class FormattingService
|
|
14
|
+
# The list of registered formatters
|
|
15
|
+
attr_reader(:formatters)
|
|
16
|
+
|
|
17
|
+
# Link to a factory of walker objects that visit macro collections
|
|
18
|
+
attr_reader(:walker_factory)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Constructor.
|
|
22
|
+
def initialize()
|
|
23
|
+
@formatters = []
|
|
24
|
+
@walker_factory = CollWalkerFactory.new
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Register a formatter.
|
|
29
|
+
# Raises an exception when the formatter implements
|
|
30
|
+
# an unknown formatting event.
|
|
31
|
+
def register(aFormatter)
|
|
32
|
+
# Get the list of formatted events supported by the formatter
|
|
33
|
+
supported_events = aFormatter.implements
|
|
34
|
+
|
|
35
|
+
# Complain if list is empty or nil
|
|
36
|
+
if supported_events.nil? || supported_events.empty?
|
|
37
|
+
fail(NoFormattingEventForFormatter.new(aFormatter))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Check that each event from the event list the formatter is known.
|
|
41
|
+
supported_events.each do |event|
|
|
42
|
+
unless Formatter::AllNotifications.include? event
|
|
43
|
+
fail(UnknownFormattingEvent.new(aFormatter, event))
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
formatters << aFormatter
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Launch the rendering(s) of the given macro collection.
|
|
51
|
+
def start!(aMacroCollection)
|
|
52
|
+
# Create a walker (that will visit the collection)
|
|
53
|
+
walker = walker_factory.build_walker(aMacroCollection)
|
|
54
|
+
|
|
55
|
+
walker.each do |visit_event|
|
|
56
|
+
(msg, nesting_level) = visit_event[0..1]
|
|
57
|
+
|
|
58
|
+
# Notify each formatter of the visit event.
|
|
59
|
+
formatters.each do |fmt|
|
|
60
|
+
accepted_notifications = fmt.implements
|
|
61
|
+
if accepted_notifications.include? msg
|
|
62
|
+
# Assumption: all nil argument(s) are at the end
|
|
63
|
+
arg_vector = visit_event[2..-1].reject { |an_arg| an_arg.nil? }
|
|
64
|
+
fmt.send(msg, nesting_level, *arg_vector)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
end # class
|
|
71
|
+
|
|
72
|
+
end # module
|
|
73
|
+
|
|
74
|
+
# End of file
|
|
@@ -32,7 +32,7 @@ public
|
|
|
32
32
|
# Prevent collision of macros (macros with same phrase).
|
|
33
33
|
# This can occur if a macro was defined in a background section.
|
|
34
34
|
# An exception is raised if the phrase syntax of both macros are the
|
|
35
|
-
|
|
35
|
+
fail(DuplicateMacroError, aPhrase) if find_macro(aPhrase, useTable)
|
|
36
36
|
|
|
37
37
|
macro_steps[new_macro.key] = new_macro
|
|
38
38
|
|
|
@@ -47,9 +47,9 @@ public
|
|
|
47
47
|
# Multiple rows with same argument name are acceptable.
|
|
48
48
|
# @return [String]
|
|
49
49
|
def render_steps(aPhrase, rawData = nil)
|
|
50
|
-
use_table = !
|
|
50
|
+
use_table = !rawData.nil?
|
|
51
51
|
macro = find_macro(aPhrase, use_table)
|
|
52
|
-
|
|
52
|
+
fail(UnknownMacroError, aPhrase) if macro.nil?
|
|
53
53
|
|
|
54
54
|
# Render the steps
|
|
55
55
|
return macro.expand(aPhrase, rawData)
|
|
@@ -32,7 +32,7 @@ public
|
|
|
32
32
|
# Invoke a macro with given phrase and (optionally) a table of values
|
|
33
33
|
# @param aPhraseInstance [String] an instance of the macro phrase.
|
|
34
34
|
# That is, the text between [...] and with zero or more actual values.
|
|
35
|
-
# @param rawData [Array or nil] An Array with
|
|
35
|
+
# @param rawData [Array or nil] An Array with couples of the form:
|
|
36
36
|
# [macro argument name, a value].
|
|
37
37
|
# Multiple rows with same argument name are acceptable.
|
|
38
38
|
def invoke_macro(aPhraseInstance, rawData = nil)
|