macros4cuke 0.2.13 → 0.2.14
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.
- data/.yardopts +6 -0
- data/CHANGELOG.md +7 -0
- data/lib/macro_steps.rb +5 -4
- data/lib/macros4cuke/constants.rb +5 -5
- data/lib/macros4cuke/exceptions.rb +82 -81
- data/lib/macros4cuke/macro-collection.rb +29 -25
- data/lib/macros4cuke/macro-step-support.rb +51 -51
- data/lib/macros4cuke/macro-step.rb +51 -46
- data/lib/macros4cuke/{template-engine.rb → templating/engine.rb} +69 -29
- data/spec/macros4cuke/macro-collection_spec.rb +6 -11
- data/spec/macros4cuke/macro-step-support_spec.rb +50 -0
- data/spec/macros4cuke/macro-step_spec.rb +24 -24
- data/spec/macros4cuke/{template-engine_spec.rb → templating/engine_spec.rb} +39 -38
- metadata +6 -4
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 0.2.14 / 2013-05-03
|
2
|
+
* [CHANGE] Code comments reformatted to YARD. The command line `yard stats`display a 100% documented score!
|
3
|
+
* [CHANGE] Moved all classes related to the template engine to the new module Templating.
|
4
|
+
* [CHANGE] Classes `StaticRep, EOLRep, VariableRep, TemplateEngine` renamed to `StaticText, EOLine, Placeholder, Engine` respectively.
|
5
|
+
* [CHANGE] Added spec file for `MacroStepSupport` module.
|
6
|
+
* [CHANGE] Initialization of `MacroStepSupport` singleton changed (no dependency on `extended` hook).
|
7
|
+
|
1
8
|
## 0.2.13 / 2013-05-02
|
2
9
|
* [NEW] File `macro-collection_spec.rb`: partial spec file for the `MacroCollection` class.
|
3
10
|
* [FIX] `DuplicateMacroError#initialize`: Removed superfluous [ in error message.
|
data/lib/macro_steps.rb
CHANGED
@@ -5,17 +5,17 @@
|
|
5
5
|
|
6
6
|
# This step is used to define a macro-step
|
7
7
|
# Example:
|
8
|
-
# Given I define the step "When I [log in as
|
8
|
+
# Given I define the step "When I [log in as <userid>]" to mean:
|
9
9
|
# """
|
10
10
|
# Given I landed in the homepage
|
11
11
|
# When I click "Sign in"
|
12
|
-
# And I fill in "Username" with "
|
12
|
+
# And I fill in "Username" with "<userid>"
|
13
13
|
# And I fill in "Password" with "unguessable"
|
14
14
|
# And I click "Submit"
|
15
15
|
# """
|
16
16
|
# The regexp has two capturing group: one for the phrase, a second for the terminating colon (:)
|
17
|
-
Given(/^I define the step "(?:Given|When|Then) I \[((?:[^\\\]]|\\.)+)\](:?)" to mean:$/) do |macro_phrase,
|
18
|
-
use_table = (
|
17
|
+
Given(/^I define the step "(?:Given|When|Then) I \[((?:[^\\\]]|\\.)+)\](:?)" to mean:$/) do |macro_phrase, colon_capture, template|
|
18
|
+
use_table = (colon_capture == ':')
|
19
19
|
add_macro(macro_phrase, template, use_table)
|
20
20
|
end
|
21
21
|
|
@@ -45,4 +45,5 @@ When(/^I \[([^\]]+)\]:$/) do |macro_phrase, table_argument|
|
|
45
45
|
end
|
46
46
|
|
47
47
|
|
48
|
+
|
48
49
|
# End of file
|
@@ -3,20 +3,20 @@
|
|
3
3
|
|
4
4
|
module Macros4Cuke # Module used as a namespace
|
5
5
|
# The version number of the gem.
|
6
|
-
Version = '0.2.
|
7
|
-
|
6
|
+
Version = '0.2.14'
|
7
|
+
|
8
8
|
# Brief description of the gem.
|
9
9
|
Description = "Macros for Cucumber"
|
10
|
-
|
10
|
+
|
11
11
|
# Constant Macros4Cuke::RootDir contains the absolute path of Rodent's root directory. Note: it also ends with a slash character.
|
12
12
|
unless defined?(RootDir)
|
13
13
|
# The initialisation of constant RootDir is guarded in order to avoid multiple initialisation (not allowed for constants)
|
14
|
-
|
14
|
+
|
15
15
|
# The root folder of Macros4Cuke.
|
16
16
|
RootDir = begin
|
17
17
|
require 'pathname' # Load Pathname class from standard library
|
18
18
|
rootdir = Pathname(__FILE__).dirname.parent.parent.expand_path()
|
19
|
-
rootdir.to_s() + '/' # Append trailing slash character to it
|
19
|
+
rootdir.to_s() + '/' # Append trailing slash character to it
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end # module
|
@@ -1,82 +1,83 @@
|
|
1
|
-
# encoding: utf-8 -- You should see a paragraph character: §
|
2
|
-
# File: exceptions.rb
|
3
|
-
|
4
|
-
module Macros4Cuke # Module used as a namespace
|
5
|
-
|
6
|
-
#
|
7
|
-
class
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
#
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
#
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
1
|
+
# encoding: utf-8 -- You should see a paragraph character: §
|
2
|
+
# File: exceptions.rb
|
3
|
+
|
4
|
+
module Macros4Cuke # Module used as a namespace
|
5
|
+
|
6
|
+
# @abstract
|
7
|
+
# Base class for any exception explicitly raised in the Macros4Cuke methods.
|
8
|
+
class Macros4CukeError < StandardError
|
9
|
+
end # class
|
10
|
+
|
11
|
+
# Raised when one attempts to define a new macro
|
12
|
+
# that has the same phrase as an existing macro.
|
13
|
+
class DuplicateMacroError < Macros4CukeError
|
14
|
+
def initialize(aPhrase)
|
15
|
+
super("A macro-step with phrase '#{aPhrase}' already exist.")
|
16
|
+
end
|
17
|
+
end # class
|
18
|
+
|
19
|
+
# Raised when one defines an argument name in a macro-step's phrase
|
20
|
+
# and that argument name does not appear in any sub-step.
|
21
|
+
class UselessPhraseArgument < Macros4CukeError
|
22
|
+
def initialize(anArgName)
|
23
|
+
super("The phrase argument '#{anArgName}' does not appear in a sub-step.")
|
24
|
+
end
|
25
|
+
end # class
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
# Raised when one defines an argument name in a macro-step's phrase
|
30
|
+
# and that argument name does not appear in any sub-step.
|
31
|
+
class UnreachableSubstepArgument < Macros4CukeError
|
32
|
+
def initialize(anArgName)
|
33
|
+
super("The sub-step argument '#{anArgName}' does not appear in the phrase.")
|
34
|
+
end
|
35
|
+
end # class
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
# Raised when a sub-step has an empty or blank argument name.
|
40
|
+
class EmptyArgumentError < Macros4CukeError
|
41
|
+
def initialize(aText)
|
42
|
+
super("An empty or blank argument occurred in '#{aText}'.")
|
43
|
+
end
|
44
|
+
end # class
|
45
|
+
|
46
|
+
|
47
|
+
# Raised when one invokes a macro-step with an unknown phrase.
|
48
|
+
class UnknownMacroError < Macros4CukeError
|
49
|
+
def initialize(aPhrase)
|
50
|
+
super("Unknown macro-step with phrase: '[#{aPhrase}'.")
|
51
|
+
end
|
52
|
+
end # class
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
# Raised when one invokes a macro-step with an argument
|
57
|
+
# that has an unknown name.
|
58
|
+
class UnknownArgumentError < Macros4CukeError
|
59
|
+
def initialize(argName)
|
60
|
+
super("Unknown macro-step argument '#{argName}'.")
|
61
|
+
end
|
62
|
+
end # class
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
# Raised when one invokes a macro-step without a required data table argument
|
67
|
+
class DataTableNotFound < Macros4CukeError
|
68
|
+
def initialize(argName)
|
69
|
+
super("The macro-step is missing a data table argument.")
|
70
|
+
end
|
71
|
+
end # class
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
# Raised when Macros4Cuke encountered an issue
|
76
|
+
# that it can't handle properly.
|
77
|
+
class InternalError < Macros4CukeError
|
78
|
+
end # class
|
79
|
+
|
80
|
+
|
81
|
+
end # module
|
82
|
+
|
82
83
|
# End of file
|
@@ -6,32 +6,22 @@ require_relative "macro-step"
|
|
6
6
|
|
7
7
|
module Macros4Cuke # Module used as a namespace
|
8
8
|
|
9
|
-
# Represents a container of macros.
|
10
|
-
# It
|
9
|
+
# Represents a container of macros.
|
10
|
+
# It gathers all the macros encountered by Cucumber while "executing" the feature files.
|
11
|
+
# @note This is a singleton class (i.e. there is only one macro collection object).
|
11
12
|
class MacroCollection
|
12
13
|
include Singleton # Use the Singleton design pattern.
|
13
14
|
|
14
|
-
#
|
15
|
-
|
15
|
+
# @!attribute [r] macro_steps.
|
16
|
+
# A Hash with pairs of the form: macro key => MacroStep object
|
16
17
|
|
17
|
-
# Init the pool if it was not done yet.
|
18
|
-
def init()
|
19
|
-
@macro_steps = {} if @macro_steps.nil?
|
20
|
-
end
|
21
18
|
|
22
19
|
public
|
23
|
-
# Return true iff the host has a macro with the given key.
|
24
|
-
def has_macro?(aPhrase, mode)
|
25
|
-
key = MacroStep::macro_key(aPhrase, mode)
|
26
|
-
return @macro_steps.include? key
|
27
|
-
end
|
28
|
-
|
29
|
-
|
30
20
|
# Add a new macro.
|
31
21
|
# Pre-condition: there is no existing macro with the same key.
|
32
|
-
# [
|
33
|
-
# [
|
34
|
-
# [
|
22
|
+
# @param aPhrase [String] The text that is enclosed between the square brackets.
|
23
|
+
# @param aTemplate [String] A text that consists of a sequence of sub-steps.
|
24
|
+
# @param useTable [boolean] A flag indicating whether a table should be used to pass actual values.
|
35
25
|
def add_macro(aPhrase, aTemplate, useTable)
|
36
26
|
new_macro = MacroStep.new(aPhrase, aTemplate, useTable)
|
37
27
|
|
@@ -40,17 +30,17 @@ public
|
|
40
30
|
# An exception is raised if the phrase syntax of both macros are the
|
41
31
|
raise DuplicateMacroError.new(aPhrase) if find_macro(aPhrase, useTable)
|
42
32
|
|
43
|
-
|
33
|
+
macro_steps[new_macro.key] = new_macro
|
44
34
|
|
45
35
|
end
|
46
|
-
|
36
|
+
|
47
37
|
# Render the steps associated to the macro with given phrase
|
48
38
|
# and (optionally) given a table of values.
|
49
39
|
# Return the rendered steps as a text.
|
50
|
-
# [
|
51
|
-
# [
|
52
|
-
#
|
53
|
-
#
|
40
|
+
# @param aPhrase [String] an instance of the macro phrase.
|
41
|
+
# @param rawData [Array] An array of couples of the form: [ argument name, a value].
|
42
|
+
# Multiple rows with same argument name are acceptable.
|
43
|
+
# @return [String]
|
54
44
|
def render_steps(aPhrase, rawData = nil)
|
55
45
|
useTable = ! rawData.nil?
|
56
46
|
macro = find_macro(aPhrase, useTable)
|
@@ -60,10 +50,24 @@ public
|
|
60
50
|
return macro.expand(aPhrase, rawData)
|
61
51
|
end
|
62
52
|
|
53
|
+
|
54
|
+
# Clear/remove all macro definitions from the collection.
|
55
|
+
# Post-condition: we are back to the same situation as no macro was ever defined.
|
56
|
+
def clear()
|
57
|
+
macro_steps.clear()
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# Read accessor for the @macro_steps attribute.
|
62
|
+
def macro_steps()
|
63
|
+
@macro_steps ||= {}
|
64
|
+
return @macro_steps
|
65
|
+
end
|
66
|
+
|
63
67
|
private
|
64
68
|
# Retrieve the macro, given a phrase.
|
65
69
|
def find_macro(aMacroPhrase, useTable)
|
66
|
-
return
|
70
|
+
return macro_steps[MacroStep::macro_key(aMacroPhrase, useTable, :invokation)]
|
67
71
|
end
|
68
72
|
|
69
73
|
end # class
|
@@ -1,52 +1,52 @@
|
|
1
|
-
# File: macro-step-support.rb
|
2
|
-
|
3
|
-
require_relative "exceptions"
|
4
|
-
require_relative "macro-collection"
|
5
|
-
|
6
|
-
module Macros4Cuke # Module used as a namespace
|
7
|
-
|
8
|
-
# Mix-in module that should be extending World objects in Cucumber.
|
9
|
-
# Synopsis (in env.rb):
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
#
|
29
|
-
# [
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
1
|
+
# File: macro-step-support.rb
|
2
|
+
|
3
|
+
require_relative "exceptions"
|
4
|
+
require_relative "macro-collection"
|
5
|
+
|
6
|
+
module Macros4Cuke # Module used as a namespace
|
7
|
+
|
8
|
+
# Mix-in module that should be extending World objects in Cucumber.
|
9
|
+
# Synopsis (in env.rb):
|
10
|
+
#
|
11
|
+
# require 'macros4cuke'
|
12
|
+
# ...
|
13
|
+
# World(Macros4Cuke::MacroStepSupport) # Extend the world object with this module.
|
14
|
+
#
|
15
|
+
module MacroStepSupport
|
16
|
+
public
|
17
|
+
|
18
|
+
# Add a new macro.
|
19
|
+
# Pre-condition: there is no existing macro with the same key.
|
20
|
+
# @param aPhrase [String] The text that is enclosed between the square brackets [...].
|
21
|
+
# @param aTemplate [String] The text template that consists of a sequence of sub-steps.
|
22
|
+
# @param useTable [boolean] A flag that indicates whether a table should be used to pass actual values.
|
23
|
+
def add_macro(aPhrase, aTemplate, useTable)
|
24
|
+
MacroCollection::instance.add_macro(aPhrase, aTemplate, useTable)
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
# Invoke a macro with given phrase and (optionally) a table of values
|
29
|
+
# @param aPhraseInstance [String] an instance of the macro phrase. That is, the text between [...] and with zero or more actual values.
|
30
|
+
# @param rawData [Array or nil] An array of couples. Each couple is of the form: [macro argument name, a value].
|
31
|
+
# Multiple rows with same argument name are acceptable.
|
32
|
+
def invoke_macro(aPhraseInstance, rawData = nil)
|
33
|
+
# Generate a text rendition of the step to be executed.
|
34
|
+
rendered_steps = MacroCollection::instance.render_steps(aPhraseInstance, rawData)
|
35
|
+
|
36
|
+
# Let Cucumber execute the sub-steps
|
37
|
+
steps(rendered_steps)
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# Clear (remove) all the macro-step definitions.
|
42
|
+
# After this, we are in the same situation when no macro-step was ever defined.
|
43
|
+
def clear_macros()
|
44
|
+
MacroCollection::instance.clear()
|
45
|
+
end
|
46
|
+
|
47
|
+
end # module
|
48
|
+
|
49
|
+
end # module
|
50
|
+
|
51
|
+
|
52
52
|
# End of file
|
@@ -3,14 +3,18 @@
|
|
3
3
|
|
4
4
|
|
5
5
|
require_relative 'exceptions'
|
6
|
-
require_relative '
|
6
|
+
require_relative 'templating/engine'
|
7
7
|
|
8
8
|
module Macros4Cuke # Module used as a namespace
|
9
9
|
|
10
|
-
#
|
11
|
-
# an aggregation of lower-level
|
10
|
+
# A macro-step object is a Cucumber step that is itself
|
11
|
+
# an aggregation of lower-level sub-steps.
|
12
|
+
# When a macro-step is used in a scenario, then its execution is equivalent
|
13
|
+
# to the execution of its sub-steps.
|
14
|
+
# A macro-step may have zero or more arguments. The actual values bound to these arguments
|
15
|
+
# are passed to the sub-steps at execution time.
|
12
16
|
class MacroStep
|
13
|
-
# A template engine that expands the
|
17
|
+
# A template engine that expands the sub-steps upon request.
|
14
18
|
attr_reader(:renderer)
|
15
19
|
|
16
20
|
# Unique key of the macro as derived from the macro phrase.
|
@@ -24,54 +28,56 @@ class MacroStep
|
|
24
28
|
|
25
29
|
|
26
30
|
# Constructor.
|
27
|
-
# [
|
28
|
-
# [
|
29
|
-
# [
|
31
|
+
# @param aMacroPhrase[String] The text from the macro step definition that is between the square brackets.
|
32
|
+
# @param theSubsteps [String] The source text of the steps to be expanded upon macro invokation.
|
33
|
+
# @param useTable [boolean] A flag indicating whether a data table must be used to pass actual values.
|
30
34
|
def initialize(aMacroPhrase, theSubsteps, useTable)
|
31
35
|
@key = self.class.macro_key(aMacroPhrase, useTable, :definition)
|
32
|
-
|
36
|
+
|
33
37
|
# Retrieve the macro arguments embedded in the phrase.
|
34
38
|
@phrase_args = scan_arguments(aMacroPhrase, :definition)
|
35
|
-
|
39
|
+
|
36
40
|
# Manipulate the substeps source text
|
37
41
|
substeps_processed = preprocess(theSubsteps)
|
38
42
|
|
39
|
-
@renderer =
|
43
|
+
@renderer = Templating::Engine.new(substeps_processed)
|
40
44
|
substeps_vars = renderer.variables
|
41
|
-
|
42
|
-
|
45
|
+
|
46
|
+
|
43
47
|
@args = validate_phrase_args(@phrase_args, substeps_vars)
|
44
48
|
@args.concat(substeps_vars)
|
45
49
|
@args.uniq!
|
46
50
|
end
|
47
|
-
|
48
|
-
|
49
|
-
# Compute the identifier of the macro from the given macro phrase.
|
50
|
-
# A macro phrase is a text that
|
51
|
-
# In definition mode, a placeholder is delimited by chevrons
|
52
|
-
# In invokation mode, a placeholder is delimited by double quotes.
|
53
|
-
# The rule for building the
|
54
|
-
# - Leading and trailing space(s) are removed.
|
55
|
-
# - Each underscore character is removed.
|
56
|
-
# - Every sequence of one or more space(s) is converted into an underscore
|
57
|
-
# - Each placeholder (i.e. = delimiters + enclosed text) is converted into a letter X.
|
58
|
-
# - when useTable is true, concatenate: _T
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
# [
|
64
|
-
# [
|
51
|
+
|
52
|
+
|
53
|
+
# Compute the identifier of the macro from the given macro phrase.
|
54
|
+
# A macro phrase is a text that may contain zero or more placeholders.
|
55
|
+
# In definition mode, a placeholder is delimited by chevrons <..>.
|
56
|
+
# In invokation mode, a value bound to a placeholder is delimited by double quotes.
|
57
|
+
# The rule for building the identifying key are:
|
58
|
+
# - Leading and trailing space(s) are removed.
|
59
|
+
# - Each underscore character is removed.
|
60
|
+
# - Every sequence of one or more space(s) is converted into an underscore
|
61
|
+
# - Each placeholder (i.e. = delimiters + enclosed text) is converted into a letter X.
|
62
|
+
# - when useTable is true, concatenate: _T
|
63
|
+
# @example:
|
64
|
+
# Consider the macro phrase: 'create the following "contactType" contact'
|
65
|
+
# The resulting macro_key is: 'create_the_following_X_contact_T'
|
66
|
+
#
|
67
|
+
# @param aMacroPhrase [String] The text from the macro step definition that is between the square brackets.
|
68
|
+
# @param useTable [boolean] A flag indicating whether a table should be used to pass actual values.
|
69
|
+
# @param mode [:definition, :invokation]
|
70
|
+
# @return [String] the key of the phrase/macro.
|
65
71
|
def self.macro_key(aMacroPhrase, useTable, mode)
|
66
72
|
stripped_phrase = aMacroPhrase.strip # Remove leading ... trailing space(s)
|
67
|
-
|
73
|
+
|
68
74
|
# Remove every underscore
|
69
75
|
stripped_phrase.gsub!(/_/, '')
|
70
|
-
|
76
|
+
|
71
77
|
# Replace all consecutive whitespaces by an underscore
|
72
78
|
stripped_phrase.gsub!(/\s+/, '_')
|
73
|
-
|
74
|
-
|
79
|
+
|
80
|
+
|
75
81
|
# Determine the pattern to isolate each argument/parameter with its delimiters
|
76
82
|
pattern = case mode
|
77
83
|
when :definition
|
@@ -80,28 +86,27 @@ class MacroStep
|
|
80
86
|
/"([^\\"]|\\.)*"/
|
81
87
|
|
82
88
|
end
|
83
|
-
|
89
|
+
|
84
90
|
# Each text between quotes or chevron is replaced by the letter X
|
85
91
|
normalized = stripped_phrase.gsub(pattern, 'X')
|
86
|
-
|
92
|
+
|
87
93
|
key = normalized + (useTable ? '_T' : '')
|
88
|
-
|
94
|
+
|
89
95
|
return key
|
90
|
-
end
|
91
|
-
|
92
|
-
|
96
|
+
end
|
97
|
+
|
98
|
+
|
93
99
|
# Render the steps from the template, given the values
|
94
100
|
# taken by the parameters
|
95
|
-
# [
|
96
|
-
# [
|
97
|
-
#
|
98
|
-
# Multiple rows with same argument name are acceptable.
|
101
|
+
# @param aPhrase [String] an instance of the macro phrase.
|
102
|
+
# @param rawData [Array] An array of couples of the form: [argument name, a value].
|
103
|
+
# Multiple rows with same argument name are acceptable.
|
99
104
|
def expand(aPhrase, rawData)
|
100
105
|
params = validate_params(aPhrase, rawData)
|
101
106
|
return renderer.render(nil, params)
|
102
107
|
end
|
103
108
|
|
104
|
-
private
|
109
|
+
private
|
105
110
|
# Build a Hash from the given raw data.
|
106
111
|
# [aPhrase] an instance of the macro phrase.
|
107
112
|
# [rawData] An Array of couples.
|
@@ -109,7 +114,7 @@ private
|
|
109
114
|
# Multiple rows with same argument name are acceptable.
|
110
115
|
def validate_params(aPhrase, rawData)
|
111
116
|
macro_parameters = {}
|
112
|
-
|
117
|
+
|
113
118
|
# Retrieve the value(s) per variable in the phrase.
|
114
119
|
quoted_values = scan_arguments(aPhrase, :invokation)
|
115
120
|
quoted_values.each_with_index do |val, index|
|
@@ -1,37 +1,59 @@
|
|
1
|
-
# File:
|
1
|
+
# File: engine.rb
|
2
2
|
# Purpose: Implementation of the MacroStep class.
|
3
3
|
|
4
4
|
require 'strscan' # Use the StringScanner for lexical analysis.
|
5
|
-
require_relative 'exceptions' # Load the
|
5
|
+
require_relative '../exceptions' # Load the custom exception classes.
|
6
|
+
|
6
7
|
|
7
8
|
module Macros4Cuke # Module used as a namespace
|
8
9
|
|
9
10
|
|
11
|
+
# Module containing all classes implementing the simple template engine
|
12
|
+
# used internally in Macros4Cuke.
|
13
|
+
module Templating
|
10
14
|
|
11
|
-
|
15
|
+
# Class used internally by the template engine.
|
16
|
+
# Represents a static piece of text from a template.
|
17
|
+
# A static text is a text that is reproduced verbatim when rendering a template.
|
18
|
+
class StaticText
|
19
|
+
# The static text extracted from the original template.
|
12
20
|
attr_reader(:source)
|
13
21
|
|
22
|
+
|
23
|
+
# @param aSourceText [String] A piece of text extracted from the template that must be rendered verbatim.
|
14
24
|
def initialize(aSourceText)
|
15
25
|
@source = aSourceText
|
16
26
|
end
|
17
|
-
|
27
|
+
|
18
28
|
public
|
19
|
-
|
29
|
+
|
30
|
+
# Render the static text.
|
31
|
+
# This method has the same signature as the {Engine#render} method.
|
32
|
+
# @return [String] Static text is returned verbatim ("as is")
|
33
|
+
def render(aContextObject, theLocals)
|
20
34
|
return source
|
21
35
|
end
|
22
36
|
end # class
|
23
37
|
|
24
38
|
|
25
|
-
|
39
|
+
# Class used internally by the template engine.
|
40
|
+
# Represents a named placeholder in a template, that is,
|
41
|
+
# a name placed between <..> in the template.
|
42
|
+
# At rendition, a placeholder is replaced by the text value that is associated with it.
|
43
|
+
class Placeholder
|
44
|
+
# The name of the placeholder/variable.
|
26
45
|
attr_reader(:name)
|
27
46
|
|
47
|
+
# @param aVarName [String] The name of the placeholder from a template.
|
28
48
|
def initialize(aVarName)
|
29
49
|
@name = aVarName
|
30
50
|
end
|
31
|
-
|
51
|
+
|
32
52
|
public
|
33
|
-
#
|
34
|
-
#
|
53
|
+
# Render the placeholder given the passed arguments.
|
54
|
+
# This method has the same signature as the {Engine#render} method.
|
55
|
+
# @return [String] The text value assigned to the placeholder.
|
56
|
+
# Returns an empty string when no value is assigned to the placeholder.
|
35
57
|
def render(aContextObject, theLocals)
|
36
58
|
actual_value = theLocals[name]
|
37
59
|
if actual_value.nil?
|
@@ -41,59 +63,74 @@ public
|
|
41
63
|
|
42
64
|
return actual_value.is_a?(String) ? actual_value : actual_value.to_s
|
43
65
|
end
|
44
|
-
|
66
|
+
|
45
67
|
end # class
|
46
68
|
|
47
69
|
|
48
|
-
|
70
|
+
# Class used internally by the template engine.
|
71
|
+
# Represents an end of line that must be rendered as such.
|
72
|
+
class EOLine
|
49
73
|
public
|
74
|
+
# Render an end of line.
|
75
|
+
# This method has the same signature as the {Engine#render} method.
|
76
|
+
# @return [String] An end of line marker. Its exact value is OS-dependent.
|
50
77
|
def render(aContextObject, theLocals)
|
51
78
|
return "\n"
|
52
79
|
end
|
53
80
|
end # class
|
54
81
|
|
55
82
|
|
56
|
-
# A very simple implementation of a templating engine.
|
57
|
-
|
83
|
+
# A very simple implementation of a templating engine.
|
84
|
+
# Earlier versions of Macros4Cuke relied on the logic-less Mustache template engine.
|
85
|
+
# But it was decided afterwards to replace it by a very simple template engine.
|
86
|
+
# The reasons were the following:
|
87
|
+
# - Be closer to the usual Gherkin syntax (parameters of scenario outlines use chevrons <...>,
|
88
|
+
# while Mustache use !{{...}} delimiters),
|
89
|
+
# - Feature files are meant to be simple, so should the template engine be.
|
90
|
+
class Engine
|
58
91
|
# The original text of the template is kept here.
|
59
92
|
attr_reader(:source)
|
60
93
|
|
61
|
-
#
|
62
|
-
# [
|
94
|
+
# Builds an Engine and compiles the given template text into an internal representation.
|
95
|
+
# @param aSourceTemplate [String] The template source text. It may contain zero or tags enclosed between chevrons <...>.
|
63
96
|
def initialize(aSourceTemplate)
|
64
97
|
@source = aSourceTemplate
|
65
98
|
@representation = compile(aSourceTemplate)
|
66
99
|
end
|
67
100
|
|
68
101
|
public
|
69
|
-
#
|
70
|
-
#
|
71
|
-
|
102
|
+
# Render the template within the given scope object and with the locals specified.
|
103
|
+
# The method mimicks the signature of the Tilt::Template#render method.
|
104
|
+
# @param aContextObject [anything] context object to get actual values (when not present in the locals Hash).
|
105
|
+
# @param theLocals [Hash] Contains one or more pairs of the form: tag/placeholder name => actual value.
|
106
|
+
# @return [String] The rendition of the template given the passed argument values.
|
107
|
+
def render(aContextObject = Object.new, theLocals)
|
72
108
|
return '' if @representation.empty?
|
73
|
-
context = aContextObject.nil? ? Object.new : aContextObject
|
74
109
|
|
75
110
|
result = @representation.each_with_object('') do |element, subResult|
|
76
|
-
subResult << element.render(
|
111
|
+
subResult << element.render(aContextObject, theLocals)
|
77
112
|
end
|
78
113
|
|
79
114
|
return result
|
80
115
|
end
|
116
|
+
|
81
117
|
|
82
|
-
#
|
118
|
+
# Retrieve all placeholder names that appear in the template.
|
119
|
+
# @return [Array] The list of placeholder names.
|
83
120
|
def variables()
|
84
121
|
# The result will be cached/memoized...
|
85
122
|
@variables ||= begin
|
86
|
-
vars = @representation.select { |element| element.is_a?(
|
123
|
+
vars = @representation.select { |element| element.is_a?(Placeholder) }
|
87
124
|
vars.map(&:name)
|
88
125
|
end
|
89
126
|
|
90
127
|
return @variables
|
91
128
|
end
|
92
129
|
|
93
|
-
|
94
|
-
#
|
95
|
-
# Couples
|
96
|
-
# [:static, text] or [:dynamic, text]
|
130
|
+
|
131
|
+
# Class method. Parse the given line text into a raw representation.
|
132
|
+
# @return [Array] Couples of the form:
|
133
|
+
# [:static, text] or [:dynamic, tag text]
|
97
134
|
def self.parse(aTextLine)
|
98
135
|
scanner = StringScanner.new(aTextLine)
|
99
136
|
result = []
|
@@ -157,7 +194,7 @@ private
|
|
157
194
|
# Convert the array of raw entries into full-fledged template elements.
|
158
195
|
def compile_line(aRawLine)
|
159
196
|
line_rep = aRawLine.map { |couple| compile_couple(couple) }
|
160
|
-
line_rep <<
|
197
|
+
line_rep << EOLine.new
|
161
198
|
end
|
162
199
|
|
163
200
|
|
@@ -168,9 +205,10 @@ private
|
|
168
205
|
|
169
206
|
result = case kind
|
170
207
|
when :static
|
171
|
-
|
208
|
+
StaticText.new(text)
|
209
|
+
|
172
210
|
when :dynamic
|
173
|
-
|
211
|
+
Placeholder.new(text)
|
174
212
|
else
|
175
213
|
raise StandardError, "Internal error: Don't know template element of kind #{kind}"
|
176
214
|
end
|
@@ -182,4 +220,6 @@ end # class
|
|
182
220
|
|
183
221
|
end # module
|
184
222
|
|
223
|
+
end # module
|
224
|
+
|
185
225
|
# End of file
|
@@ -8,20 +8,15 @@ module Macros4Cuke # Open this namespace to get rid of module qualifier prefixes
|
|
8
8
|
|
9
9
|
describe MacroCollection do
|
10
10
|
|
11
|
-
before(:all) do
|
12
|
-
# Initialize the sole instance
|
13
|
-
MacroCollection.instance.init()
|
14
|
-
end
|
15
|
-
|
16
11
|
let(:singleton) { MacroCollection.instance() }
|
17
|
-
|
12
|
+
|
18
13
|
context "Initialization:" do
|
19
14
|
it "should be empty" do
|
20
15
|
singleton.macro_steps.should be_empty
|
21
16
|
end
|
22
|
-
|
17
|
+
|
23
18
|
end
|
24
|
-
|
19
|
+
|
25
20
|
context "Provided services:" do
|
26
21
|
let(:sample_substeps) do
|
27
22
|
snippet = <<-SNIPPET
|
@@ -32,20 +27,20 @@ describe MacroCollection do
|
|
32
27
|
And I click "Submit"
|
33
28
|
SNIPPET
|
34
29
|
|
35
|
-
snippet
|
30
|
+
snippet
|
36
31
|
end
|
37
32
|
|
38
33
|
it "should accept the addition of a new macro-step" do
|
39
34
|
phrase = "[enter my credentials]"
|
40
35
|
lambda { singleton.add_macro(phrase, sample_substeps, true)}.should_not raise_error
|
41
36
|
singleton.should have(1).macro_steps
|
42
|
-
|
37
|
+
|
43
38
|
# Error case: inserting another macro with same phrase.
|
44
39
|
error_message = "A macro-step with phrase '[enter my credentials]' already exist."
|
45
40
|
lambda { singleton.add_macro(phrase, sample_substeps, true) }.should raise_error(Macros4Cuke::DuplicateMacroError, error_message)
|
46
41
|
end
|
47
42
|
end
|
48
|
-
|
43
|
+
|
49
44
|
end # describe
|
50
45
|
|
51
46
|
end # module
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# File: macro-step_spec.rb
|
2
|
+
|
3
|
+
require_relative '../spec_helper'
|
4
|
+
require_relative '../../lib/macros4cuke/macro-step-support' # The class under test
|
5
|
+
|
6
|
+
|
7
|
+
module Macros4Cuke # Open the module to avoid lengthy qualified names
|
8
|
+
|
9
|
+
# Class created just for testing purposes.
|
10
|
+
class MyWorld
|
11
|
+
include Macros4Cuke::MacroStepSupport
|
12
|
+
end # class
|
13
|
+
|
14
|
+
describe MacroStepSupport do
|
15
|
+
# Rule to build a bland world object
|
16
|
+
let(:world) do
|
17
|
+
w = Object.new
|
18
|
+
w.extend(Macros4Cuke::MacroStepSupport)
|
19
|
+
w
|
20
|
+
end
|
21
|
+
|
22
|
+
context "Defining macro(s):" do
|
23
|
+
let(:m1_phrase) { "enter the credentials" }
|
24
|
+
let(:m1_substeps) do
|
25
|
+
ssteps = <<-SNIPPET
|
26
|
+
Given I landed in the homepage
|
27
|
+
When I click "Sign in"
|
28
|
+
And I fill in "Username" with "<userid>"
|
29
|
+
And I fill in "Password" with "<password>"
|
30
|
+
And I click "Submit"
|
31
|
+
SNIPPET
|
32
|
+
ssteps
|
33
|
+
end
|
34
|
+
it "should add valid new macro" do
|
35
|
+
lambda { world.add_macro(m1_phrase, m1_substeps, true) }.should_not raise_error
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should complain when entering the same macro again" do
|
39
|
+
# Error case: trying to register another macro with same key/phrase.
|
40
|
+
error_message = "A macro-step with phrase 'enter the credentials' already exist."
|
41
|
+
lambda { world.add_macro(m1_phrase, m1_substeps, true) }.should raise_error(Macros4Cuke::DuplicateMacroError, error_message)
|
42
|
+
end
|
43
|
+
end # context
|
44
|
+
|
45
|
+
end # describe
|
46
|
+
|
47
|
+
end # module
|
48
|
+
|
49
|
+
|
50
|
+
# End of file
|
@@ -10,7 +10,7 @@ module Macros4Cuke # Open the module to avoid lengthy qualified names
|
|
10
10
|
|
11
11
|
describe MacroStep do
|
12
12
|
let(:sample_phrase) { "enter my credentials as <userid>" }
|
13
|
-
|
13
|
+
|
14
14
|
let(:sample_template) do
|
15
15
|
snippet = <<-SNIPPET
|
16
16
|
Given I landed in the homepage
|
@@ -29,42 +29,42 @@ end
|
|
29
29
|
|
30
30
|
context "Creation & initialization" do
|
31
31
|
it "should be created with a phrase, substeps and a table use indicator" do
|
32
|
-
lambda { MacroStep.new(sample_phrase, sample_template, true) }.should_not raise_error
|
32
|
+
lambda { MacroStep.new(sample_phrase, sample_template, true) }.should_not raise_error
|
33
33
|
end
|
34
|
-
|
35
|
-
|
34
|
+
|
35
|
+
|
36
36
|
it "should complain when a sub-step argument can never be assigned a value via the phrase" do
|
37
37
|
error_message = "The sub-step argument 'password' does not appear in the phrase."
|
38
|
-
lambda { MacroStep.new(sample_phrase, sample_template, false) }.should raise_error(Macros4Cuke::UnreachableSubstepArgument, error_message)
|
38
|
+
lambda { MacroStep.new(sample_phrase, sample_template, false) }.should raise_error(Macros4Cuke::UnreachableSubstepArgument, error_message)
|
39
39
|
end
|
40
|
-
|
41
|
-
|
40
|
+
|
41
|
+
|
42
42
|
it "should complain when an argument from the phrase never occurs in a substep" do
|
43
43
|
a_phrase = "enter my credentials as <foobar>"
|
44
44
|
error_message = "The phrase argument 'foobar' does not appear in a sub-step."
|
45
|
-
lambda { MacroStep.new(a_phrase, sample_template, true) }.should raise_error(Macros4Cuke::UselessPhraseArgument, error_message)
|
46
|
-
end
|
45
|
+
lambda { MacroStep.new(a_phrase, sample_template, true) }.should raise_error(Macros4Cuke::UselessPhraseArgument, error_message)
|
46
|
+
end
|
47
|
+
|
47
48
|
|
48
|
-
|
49
49
|
it "should know its key" do
|
50
50
|
subject.key.should == "enter_my_credentials_as_X_T"
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
it "should know the tags(placeholders) from its phrase" do
|
54
54
|
subject.phrase_args.should == %w[userid]
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
it "should know the tags(placeholders) from its phrase and template" do
|
58
|
-
subject.args.should == %w[userid password]
|
58
|
+
subject.args.should == %w[userid password]
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
end # context
|
62
|
-
|
63
|
-
|
62
|
+
|
63
|
+
|
64
64
|
context "Provided services" do
|
65
|
-
|
65
|
+
|
66
66
|
let(:phrase_instance) {%Q|enter my credentials as "nobody"|}
|
67
|
-
|
67
|
+
|
68
68
|
it "should render the substeps" do
|
69
69
|
text = subject.expand(phrase_instance, [ ['password', 'no-secret'] ])
|
70
70
|
expectation = <<-SNIPPET
|
@@ -74,10 +74,10 @@ end
|
|
74
74
|
And I fill in "Password" with "no-secret"
|
75
75
|
And I click "Submit"
|
76
76
|
SNIPPET
|
77
|
-
|
77
|
+
|
78
78
|
text.should == expectation
|
79
79
|
end
|
80
|
-
|
80
|
+
|
81
81
|
it "should render steps even when one argument has no actual value" do
|
82
82
|
# Special case: password has no value...
|
83
83
|
text = subject.expand(phrase_instance, [ ])
|
@@ -88,10 +88,10 @@ SNIPPET
|
|
88
88
|
And I fill in "Password" with ""
|
89
89
|
And I click "Submit"
|
90
90
|
SNIPPET
|
91
|
-
|
92
|
-
text.should == expectation
|
91
|
+
|
92
|
+
text.should == expectation
|
93
93
|
end
|
94
|
-
|
94
|
+
|
95
95
|
it "should complain when an unknown variable is used" do
|
96
96
|
# Error case: there is no macro argument called <unknown>
|
97
97
|
error_message = "Unknown macro-step argument 'unknown'."
|
@@ -99,7 +99,7 @@ SNIPPET
|
|
99
99
|
end
|
100
100
|
|
101
101
|
end # context
|
102
|
-
|
102
|
+
|
103
103
|
|
104
104
|
end # describe
|
105
105
|
|
@@ -1,12 +1,15 @@
|
|
1
1
|
# encoding: utf-8 -- You should see a paragraph character: §
|
2
|
-
# File:
|
2
|
+
# File: engine_spec.rb
|
3
3
|
|
4
|
-
require_relative '
|
5
|
-
require_relative '
|
4
|
+
require_relative '../../spec_helper'
|
5
|
+
require_relative '../../../lib/macros4cuke/templating/engine' # Load the class under test
|
6
6
|
|
7
|
-
module Macros4Cuke
|
7
|
+
module Macros4Cuke
|
8
8
|
|
9
|
-
|
9
|
+
module Templating # Open this namespace to get rid of module qualifier prefixes
|
10
|
+
|
11
|
+
|
12
|
+
describe Engine do
|
10
13
|
let(:sample_template) do
|
11
14
|
source = <<-SNIPPET
|
12
15
|
Given I landed in the homepage
|
@@ -17,29 +20,25 @@ describe TemplateEngine do
|
|
17
20
|
SNIPPET
|
18
21
|
end
|
19
22
|
|
20
|
-
# Rule for default instantiation
|
21
|
-
subject {
|
23
|
+
# Rule for default instantiation
|
24
|
+
subject { Engine.new sample_template }
|
22
25
|
|
23
26
|
|
24
27
|
context "Class services" do
|
25
|
-
#
|
26
|
-
|
27
|
-
return TemplateEngine::parse(aText)
|
28
|
-
end
|
29
|
-
|
30
|
-
# remove enclosing chevrons <..> (if any)
|
28
|
+
# Helper method.
|
29
|
+
# Remove enclosing chevrons <..> (if any)
|
31
30
|
def strip_chevrons(aText)
|
32
31
|
return aText.gsub(/^<|>$/, '')
|
33
32
|
end
|
34
33
|
|
35
34
|
it "should parse an empty text line" do
|
36
35
|
# Expectation: result should be an empty array.
|
37
|
-
|
36
|
+
Engine::parse('').should be_empty
|
38
37
|
end
|
39
38
|
|
40
39
|
it "should parse a text line without tag" do
|
41
40
|
sample_text = 'Mary has a little lamb'
|
42
|
-
result =
|
41
|
+
result = Engine::parse(sample_text)
|
43
42
|
|
44
43
|
# Expectation: an array with one couple: [:static, the source text]
|
45
44
|
result.should have(1).items
|
@@ -48,7 +47,7 @@ SNIPPET
|
|
48
47
|
|
49
48
|
it "should parse a text line that consists of just a tag" do
|
50
49
|
sample_text = '<some_tag>'
|
51
|
-
result =
|
50
|
+
result = Engine::parse(sample_text)
|
52
51
|
|
53
52
|
# Expectation: an array with one couple: [:static, the source text]
|
54
53
|
result.should have(1).items
|
@@ -57,7 +56,7 @@ SNIPPET
|
|
57
56
|
|
58
57
|
it "should parse a text line with a tag at the start" do
|
59
58
|
sample_text = '<some_tag>some text'
|
60
|
-
result =
|
59
|
+
result = Engine::parse(sample_text)
|
61
60
|
|
62
61
|
# Expectation: an array with two couples: [dynamic, 'some_tag'][:static, some text]
|
63
62
|
result.should have(2).items
|
@@ -67,7 +66,7 @@ SNIPPET
|
|
67
66
|
|
68
67
|
it "should parse a text line with a tag at the end" do
|
69
68
|
sample_text = 'some text<some_tag>'
|
70
|
-
result =
|
69
|
+
result = Engine::parse(sample_text)
|
71
70
|
|
72
71
|
# Expectation: an array with two couples: [:static, some text] [dynamic, 'some_tag']
|
73
72
|
result.should have(2).items
|
@@ -77,7 +76,7 @@ SNIPPET
|
|
77
76
|
|
78
77
|
it "should parse a text line with a tag in the middle" do
|
79
78
|
sample_text = 'begin <some_tag> end'
|
80
|
-
result =
|
79
|
+
result = Engine::parse(sample_text)
|
81
80
|
|
82
81
|
# Expectation: an array with three couples:
|
83
82
|
result.should have(3).items
|
@@ -88,7 +87,7 @@ SNIPPET
|
|
88
87
|
|
89
88
|
it "should parse a text line with two tags in the middle" do
|
90
89
|
sample_text = 'begin <some_tag>middle<another_tag> end'
|
91
|
-
result =
|
90
|
+
result = Engine::parse(sample_text)
|
92
91
|
|
93
92
|
# Expectation: an array with items couples:
|
94
93
|
result.should have(5).items
|
@@ -100,7 +99,7 @@ SNIPPET
|
|
100
99
|
|
101
100
|
# Case: two consecutive tags
|
102
101
|
sample_text = 'begin <some_tag><another_tag> end'
|
103
|
-
result =
|
102
|
+
result = Engine::parse(sample_text)
|
104
103
|
|
105
104
|
# Expectation: an array with four couples:
|
106
105
|
result.should have(4).items
|
@@ -112,7 +111,7 @@ SNIPPET
|
|
112
111
|
|
113
112
|
it "should parse a text line with escaped chevrons" do
|
114
113
|
sample_text = 'Mary has a \<little\> lamb'
|
115
|
-
result =
|
114
|
+
result = Engine::parse(sample_text)
|
116
115
|
|
117
116
|
# Expectation: an array with one couple: [:static, the source text]
|
118
117
|
result.should have(1).items
|
@@ -121,7 +120,7 @@ SNIPPET
|
|
121
120
|
|
122
121
|
it "should parse a text line with escaped chevrons in a tag" do
|
123
122
|
sample_text = 'begin <some_\<\\>weird\>_tag> end'
|
124
|
-
result =
|
123
|
+
result = Engine::parse(sample_text)
|
125
124
|
|
126
125
|
# Expectation: an array with three couples:
|
127
126
|
result.should have(3).items
|
@@ -133,7 +132,7 @@ SNIPPET
|
|
133
132
|
it "should complain if a tag misses an closing chevron" do
|
134
133
|
sample_text = 'begin <some_tag\> end'
|
135
134
|
error_message = "Missing closing chevron '>'."
|
136
|
-
lambda {
|
135
|
+
lambda { Engine::parse(sample_text) }.should raise_error(StandardError, error_message)
|
137
136
|
end
|
138
137
|
|
139
138
|
end # context
|
@@ -141,43 +140,43 @@ SNIPPET
|
|
141
140
|
context "Creation and initialization" do
|
142
141
|
|
143
142
|
it "should accept an empty template text" do
|
144
|
-
lambda {
|
143
|
+
lambda { Engine.new '' }.should_not raise_error
|
145
144
|
end
|
146
145
|
|
147
146
|
it "should be created with a template text" do
|
148
|
-
lambda {
|
147
|
+
lambda { Engine.new sample_template }.should_not raise_error
|
149
148
|
end
|
150
149
|
|
151
150
|
it "should know the source text" do
|
152
151
|
subject.source.should == sample_template
|
153
152
|
|
154
153
|
# Case of an empty template
|
155
|
-
instance =
|
154
|
+
instance = Engine.new ''
|
156
155
|
instance.source.should be_empty
|
157
156
|
end
|
158
|
-
|
157
|
+
|
159
158
|
it "should complain when a placeholder is empty or blank" do
|
160
159
|
text_w_empty_arg = sample_template.sub(/userid/, '')
|
161
160
|
error_message = %Q|An empty or blank argument occurred in 'And I fill in "Username" with "<>"'.|
|
162
|
-
lambda {
|
163
|
-
|
161
|
+
lambda { Engine.new text_w_empty_arg }.should raise_error(Macros4Cuke::EmptyArgumentError, error_message)
|
162
|
+
|
164
163
|
end
|
165
164
|
end
|
166
|
-
|
165
|
+
|
167
166
|
context "Provided services" do
|
168
|
-
|
167
|
+
|
169
168
|
it "should know the variable(s) it contains" do
|
170
169
|
subject.variables == [:userid, :password]
|
171
170
|
|
172
171
|
# Case of an empty source template text
|
173
|
-
instance =
|
172
|
+
instance = Engine.new ''
|
174
173
|
instance.variables.should be_empty
|
175
174
|
end
|
176
|
-
|
175
|
+
|
177
176
|
it "should render the text given the actuals" do
|
178
177
|
locals = {'userid' => "johndoe"}
|
179
|
-
|
180
|
-
rendered_text = subject.render(
|
178
|
+
|
179
|
+
rendered_text = subject.render(Object.new, locals)
|
181
180
|
expected = <<-SNIPPET
|
182
181
|
Given I landed in the homepage
|
183
182
|
# The credentials are entered here
|
@@ -197,10 +196,10 @@ SNIPPET
|
|
197
196
|
And I fill in "Password" with "holmes"
|
198
197
|
And I click "Sign in"
|
199
198
|
SNIPPET
|
200
|
-
|
199
|
+
|
201
200
|
|
202
201
|
# Case of an empty source template text
|
203
|
-
instance =
|
202
|
+
instance = Engine.new ''
|
204
203
|
instance.render(nil, {}).should be_empty
|
205
204
|
end
|
206
205
|
end
|
@@ -209,4 +208,6 @@ end # describe
|
|
209
208
|
|
210
209
|
end # module
|
211
210
|
|
211
|
+
end # module
|
212
|
+
|
212
213
|
# End of file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: macros4cuke
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.14
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-05-
|
12
|
+
date: 2013-05-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: cucumber
|
@@ -49,6 +49,7 @@ executables: []
|
|
49
49
|
extensions: []
|
50
50
|
extra_rdoc_files: []
|
51
51
|
files:
|
52
|
+
- .yardopts
|
52
53
|
- cucumber.yml
|
53
54
|
- CHANGELOG.md
|
54
55
|
- LICENSE.txt
|
@@ -60,7 +61,7 @@ files:
|
|
60
61
|
- lib/macros4cuke/macro-collection.rb
|
61
62
|
- lib/macros4cuke/macro-step-support.rb
|
62
63
|
- lib/macros4cuke/macro-step.rb
|
63
|
-
- lib/macros4cuke/
|
64
|
+
- lib/macros4cuke/templating/engine.rb
|
64
65
|
- features/demo01.feature
|
65
66
|
- features/demo02.feature
|
66
67
|
- features/demo03.feature
|
@@ -72,8 +73,9 @@ files:
|
|
72
73
|
- features/support/macro_support.rb
|
73
74
|
- spec/spec_helper.rb
|
74
75
|
- spec/macros4cuke/macro-collection_spec.rb
|
76
|
+
- spec/macros4cuke/macro-step-support_spec.rb
|
75
77
|
- spec/macros4cuke/macro-step_spec.rb
|
76
|
-
- spec/macros4cuke/
|
78
|
+
- spec/macros4cuke/templating/engine_spec.rb
|
77
79
|
homepage: https://github.com/famished-tiger/Macros4Cuke
|
78
80
|
licenses:
|
79
81
|
- MIT
|