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