macros4cuke 0.0.01
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/HISTORY.md +4 -0
- data/LICENSE.txt +19 -0
- data/README.md +10 -0
- data/cucumber.yml +10 -0
- data/features/demo01.feature +35 -0
- data/features/demo02.feature +36 -0
- data/features/demo03.feature +56 -0
- data/features/demo04.feature +38 -0
- data/features/step_definitions/demo_steps.rb +38 -0
- data/features/step_definitions/use_macro_steps.rb +8 -0
- data/features/support/env.rb +47 -0
- data/lib/macro_steps.rb +72 -0
- data/lib/macros4cuke/constants.rb +21 -0
- data/lib/macros4cuke/macro-step-support.rb +50 -0
- data/lib/macros4cuke/macro-step.rb +221 -0
- data/lib/macros4cuke.rb +8 -0
- metadata +118 -0
data/HISTORY.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2013 Dimitri Geshef
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
Macros4Cuke
|
2
|
+
===========
|
3
|
+
|
4
|
+
## Add macros to your Cucumber feature files. ##
|
5
|
+
[Homepage](https://github.com/famished-tiger/Macros4Cuke)
|
6
|
+
|
7
|
+
|
8
|
+
Copyright
|
9
|
+
---------
|
10
|
+
Copyright (c) 2013, Dimitri Geshef. See [LICENSE.txt](https://github.com/famished-tiger/Macros4Cuke/blob/master/LICENSE.txt) for details.
|
data/cucumber.yml
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
default: --strict -v -b --format progress features
|
2
|
+
html: --strict -v -b --format html --out result.html features
|
3
|
+
pdf: --strict -v -b --format pdf --out result.pdf features
|
4
|
+
stepdefs: --strict -v -b --format stepdefs --out stepdefs.txt features
|
5
|
+
rerun: --strict -v -b --format rerun --out rerun.txt features
|
6
|
+
tag_cloud: --strict -v -b --format tag_cloud --out tag_cloud.txt features
|
7
|
+
usage: --strict -v -b --format usage --out usage.txt features
|
8
|
+
|
9
|
+
|
10
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# File: demo01.feature
|
2
|
+
|
3
|
+
Feature: Show the use of a basic macro
|
4
|
+
As a Cuke user
|
5
|
+
So that I enjoy writing scenario.
|
6
|
+
|
7
|
+
# The background section is THE good place to define your macros.
|
8
|
+
# Since the steps from this section are pre-pended to the steps of every scenario (outline),
|
9
|
+
# the macros will be available to every scenario in the feature file.
|
10
|
+
Background:
|
11
|
+
# The next step creates a macro(-step)
|
12
|
+
# The syntax of the new macro-step is specified between the < ... > delimiters.
|
13
|
+
# The steps to execute when the macro is used/invoked are listed in the multiline triple quotes arguments.
|
14
|
+
Given I define the step <When I [log in]> to mean:
|
15
|
+
"""
|
16
|
+
Given I landed in the homepage
|
17
|
+
When I click "Sign in"
|
18
|
+
And I fill in "Username" with "johndoe"
|
19
|
+
And I fill in "Password" with "unguessable"
|
20
|
+
And I click "Submit"
|
21
|
+
"""
|
22
|
+
|
23
|
+
Scenario: Let's use the macro we created above
|
24
|
+
# Here the macro is invoked
|
25
|
+
When I [log in]
|
26
|
+
|
27
|
+
# The next step verifies that the steps from the macro were effectively executed.
|
28
|
+
Then I expect the following step trace:
|
29
|
+
"""
|
30
|
+
Invoked step: ... I landed in the homepage
|
31
|
+
Invoked step: ... I click "Sign in"
|
32
|
+
Invoked step: ... I fill in "Username" with "johndoe"
|
33
|
+
Invoked step: ... I fill in "Password" with "unguessable"
|
34
|
+
Invoked step: ... I click "Submit"
|
35
|
+
"""
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# File: demo02.feature
|
2
|
+
|
3
|
+
Feature: Show the use of a basic macro with one argument
|
4
|
+
As a Cuke user
|
5
|
+
So that I enjoy writing scenario.
|
6
|
+
|
7
|
+
# The background section is THE good place to define your macros.
|
8
|
+
# Since the steps from this section are pre-pended to the steps of every scenario (outline),
|
9
|
+
# the macros will be available to every scenario in the feature file.
|
10
|
+
Background:
|
11
|
+
# The next step creates a macro(-step)
|
12
|
+
# The syntax of the new macro-step is specified between the < ... > delimiters.
|
13
|
+
# The steps to execute when the macro is used/invoked are listed in the multiline triple quotes arguments.
|
14
|
+
# The macro argument is put between double(triple) curly braces {{...}} as required by the Mustache template library.
|
15
|
+
Given I define the step <When I [log in as {{userid}}]> to mean:
|
16
|
+
"""
|
17
|
+
Given I landed in the homepage
|
18
|
+
When I click "Sign in"
|
19
|
+
And I fill in "Username" with "{{userid}}"
|
20
|
+
And I fill in "Password" with "unguessable"
|
21
|
+
And I click "Submit"
|
22
|
+
"""
|
23
|
+
|
24
|
+
Scenario: Let's use the macro we created above
|
25
|
+
# Here the macro is invoked. Actual value for the argument is put between double quotes.
|
26
|
+
When I [log in as "guest"]
|
27
|
+
|
28
|
+
# The next step verifies that the steps from the macro were effectively executed.
|
29
|
+
Then I expect the following step trace:
|
30
|
+
"""
|
31
|
+
Invoked step: ... I landed in the homepage
|
32
|
+
Invoked step: ... I click "Sign in"
|
33
|
+
Invoked step: ... I fill in "Username" with "guest"
|
34
|
+
Invoked step: ... I fill in "Password" with "unguessable"
|
35
|
+
Invoked step: ... I click "Submit"
|
36
|
+
"""
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# File: demo03.feature
|
2
|
+
|
3
|
+
Feature: Show the use of a basic macro with multiple arguments
|
4
|
+
As a Cuke user
|
5
|
+
So that I enjoy writing scenario.
|
6
|
+
|
7
|
+
# The background section is THE good place to define your macros.
|
8
|
+
# Since the steps from this section are pre-pended to the steps of every scenario (outline),
|
9
|
+
# the macros will be available to every scenario in the feature file.
|
10
|
+
Background:
|
11
|
+
# The next step creates a macro(-step)
|
12
|
+
# The syntax of the new macro-step is specified between the < ... > delimiters.
|
13
|
+
# The steps to execute when the macro is used/invoked are listed in the multiline triple quotes arguments.
|
14
|
+
# The macro argument is put between double(triple) curly braces {{...}} as required by the Mustache template library.
|
15
|
+
Given I define the step <When I [enter my userid {{userid}} and password {{password}}]> to mean:
|
16
|
+
"""
|
17
|
+
Given I landed in the homepage
|
18
|
+
When I click "Sign in"
|
19
|
+
And I fill in "Username" with "{{userid}}"
|
20
|
+
And I fill in "Password" with "{{password}}"
|
21
|
+
And I click "Submit"
|
22
|
+
"""
|
23
|
+
|
24
|
+
Scenario: Let's use the macro we created above
|
25
|
+
# Here the macro is invoked. Actual value for the argument is put between double quotes.
|
26
|
+
When I [enter my userid "guest" and password "unguessable"]
|
27
|
+
|
28
|
+
# The next step verifies that the steps from the macro were effectively executed.
|
29
|
+
Then I expect the following step trace:
|
30
|
+
"""
|
31
|
+
Invoked step: ... I landed in the homepage
|
32
|
+
Invoked step: ... I click "Sign in"
|
33
|
+
Invoked step: ... I fill in "Username" with "guest"
|
34
|
+
Invoked step: ... I fill in "Password" with "unguessable"
|
35
|
+
Invoked step: ... I click "Submit"
|
36
|
+
"""
|
37
|
+
|
38
|
+
Scenario: A macro invoking another macro (YES, it's possible!)
|
39
|
+
Given I define the step <When I [enter my credentials]> to mean:
|
40
|
+
"""
|
41
|
+
{{! Notice that the next step is invoking the first macro above}}
|
42
|
+
When I [enter my userid "guest" and password "unguessable"]
|
43
|
+
"""
|
44
|
+
|
45
|
+
# Invoking our lastly-created macro
|
46
|
+
When I [enter my credentials]
|
47
|
+
|
48
|
+
# Check that the nested macro still works
|
49
|
+
Then I expect the following step trace:
|
50
|
+
"""
|
51
|
+
Invoked step: ... I landed in the homepage
|
52
|
+
Invoked step: ... I click "Sign in"
|
53
|
+
Invoked step: ... I fill in "Username" with "guest"
|
54
|
+
Invoked step: ... I fill in "Password" with "unguessable"
|
55
|
+
Invoked step: ... I click "Submit"
|
56
|
+
"""
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# File: demo04.feature
|
2
|
+
|
3
|
+
Feature: Show the use of a macro with multiple arguments in a table
|
4
|
+
As a Cuke user
|
5
|
+
So that I enjoy writing scenario.
|
6
|
+
|
7
|
+
# The background section is THE good place to define your macros.
|
8
|
+
# Since the steps from this section are pre-pended to the steps of every scenario (outline),
|
9
|
+
# the macros will be available to every scenario in the feature file.
|
10
|
+
Background:
|
11
|
+
# The next step creates a macro(-step)
|
12
|
+
# The syntax of the new macro-step is specified between the < ... > delimiters.
|
13
|
+
# The steps to execute when the macro is used/invoked are listed in the multiline triple quotes arguments.
|
14
|
+
# The macro argument is put between double(triple) curly braces {{...}} as required by the Mustache template library.
|
15
|
+
Given I define the step <When I [enter my credentials as]:> to mean:
|
16
|
+
"""
|
17
|
+
Given I landed in the homepage
|
18
|
+
When I click "Sign in"
|
19
|
+
And I fill in "Username" with "{{userid}}"
|
20
|
+
And I fill in "Password" with "{{password}}"
|
21
|
+
And I click "Submit"
|
22
|
+
"""
|
23
|
+
|
24
|
+
Scenario: # Let's use the macro we created above
|
25
|
+
# Here the macro is invoked. Actual value for the argument are passed in a table argument.
|
26
|
+
When I [enter my credentials as]:
|
27
|
+
|userid|guest|
|
28
|
+
|password|unguessable|
|
29
|
+
|
30
|
+
# The next step verifies that the steps from the macro were effectively executed.
|
31
|
+
Then I expect the following step trace:
|
32
|
+
"""
|
33
|
+
Invoked step: ... I landed in the homepage
|
34
|
+
Invoked step: ... I click "Sign in"
|
35
|
+
Invoked step: ... I fill in "Username" with "guest"
|
36
|
+
Invoked step: ... I fill in "Password" with "unguessable"
|
37
|
+
Invoked step: ... I click "Submit"
|
38
|
+
"""
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# File: demo_steps.rb
|
2
|
+
# A few step definitions for demo and testing purpose.
|
3
|
+
|
4
|
+
When(/^I landed in the homepage$/) do
|
5
|
+
trace_steps << %Q|Invoked step: ... I landed in the homepage|
|
6
|
+
end
|
7
|
+
|
8
|
+
When(/^I click "([^"]*)"$/) do |element|
|
9
|
+
trace_steps << %Q|Invoked step: ... I click "#{element}"|
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
When(/^I fill in "(.*?)" with "(.*?)"$/) do |element, text|
|
14
|
+
trace_steps << %Q|Invoked step: ... I fill in "#{element}" with "#{text}"|
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
Then(/^I expect the following step trace:$/) do |step_text|
|
19
|
+
trace_steps.should == step_text.split(/\r?\n|\n/)
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
When(/^I leave (.+)$/) do |city|
|
24
|
+
trace_steps << %Q|Invoked step: ... I leave #{city}|
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
When(/^I visit (.+)$/) do |city|
|
29
|
+
trace_steps << %Q|Invoked step: ... I visit #{city}|
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
When(/^I arrive in (.+)$/) do |city|
|
34
|
+
trace_steps << %Q|Invoked step: ... I arrive in #{city}|
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# End of file
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# File: use_macro_steps.rb
|
2
|
+
# Place a copy of this file in the feature/step_definitions folder of your own Cucumber-based project.
|
3
|
+
|
4
|
+
# The following require will load the step definitions from Macros4Cuke.
|
5
|
+
# This allows feature file authors to use macro steps in their Cucumber scenarios.
|
6
|
+
require 'macros4cuke/../macro_steps'
|
7
|
+
|
8
|
+
# End of file
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8 You should see a paragraph character: §
|
2
|
+
# File: env.rb
|
3
|
+
# Purpose: Allow Cucumber to load the sample application configuration and hooks.
|
4
|
+
# It also demonstrate what to do in your env.rb file to use the Macros4Cuke gem.
|
5
|
+
|
6
|
+
|
7
|
+
# Macros4Cuke step one: Load modules and classes from the gem.
|
8
|
+
require 'macros4cuke'
|
9
|
+
|
10
|
+
|
11
|
+
module Macros4Cuke # Use the module as a namespace
|
12
|
+
|
13
|
+
=begin
|
14
|
+
Class created just for testing and demonstration purposes.
|
15
|
+
Its instance, will record the output emitted by the steps.
|
16
|
+
=end
|
17
|
+
class TracingWorld
|
18
|
+
# Will contain the text emitted by the steps
|
19
|
+
attr_reader(:trace_steps)
|
20
|
+
|
21
|
+
|
22
|
+
def initialize()
|
23
|
+
# Constructor
|
24
|
+
@trace_steps = []
|
25
|
+
end
|
26
|
+
|
27
|
+
public
|
28
|
+
|
29
|
+
|
30
|
+
end # class
|
31
|
+
|
32
|
+
end # module
|
33
|
+
|
34
|
+
# For testing purpose we override the default Cucumber behaviour
|
35
|
+
# making our world object an instance of the TracingWorld class
|
36
|
+
World { Macros4Cuke::TracingWorld.new }
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
# Macros4Cuke step two: extend the world object with the mix-in module
|
41
|
+
# that adds the support for macros in Cucumber.
|
42
|
+
World(Macros4Cuke::MacroStepSupport)
|
43
|
+
|
44
|
+
# That's all folks!...
|
45
|
+
|
46
|
+
|
47
|
+
# End of file
|
data/lib/macro_steps.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# File: macro_steps.rb
|
2
|
+
# Purpose: step definitions that help to build macro-steps (i.e. a step that is equivalent to a sequence of steps)
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
=begin This step is used to define a macro-step
|
7
|
+
Example:
|
8
|
+
Given I define the step <When I [create the following {{contactType}} contact]:> to mean:
|
9
|
+
"""
|
10
|
+
# In the next step we use triple curly brace in order to get un-escaped text
|
11
|
+
When I select "<type>" from "type"
|
12
|
+
When I fill in "name" with "{{{name}}}"
|
13
|
+
And I fill in "companyName" with "{{companyName}}"
|
14
|
+
And I fill in "companyName2" with "{{companyName2}}"
|
15
|
+
"""
|
16
|
+
=end
|
17
|
+
Given(/^I define the step <When I \[([^\]]+\]:?)> to mean:$/) do |macro_phrase, template|
|
18
|
+
add_macro(macro_phrase, template)
|
19
|
+
end
|
20
|
+
|
21
|
+
=begin This step is used to invoke a simple macro-step
|
22
|
+
# Example:
|
23
|
+
# Here we define a simple macro-step
|
24
|
+
Given I define the step <When I [fly from {{origin}} to {{destination}} via {{waypoint}}]> to mean:
|
25
|
+
"""
|
26
|
+
When I display the text "Departure: {{origin}}."
|
27
|
+
When I display the text "Stop at: {{waypoint}}."
|
28
|
+
When I display the text "Destination {{destination}}."
|
29
|
+
"""
|
30
|
+
=end
|
31
|
+
When(/^I \[([^\]]+\])$/) do |macro_phrase|
|
32
|
+
macro = find_macro(macro_phrase)
|
33
|
+
raise StandardError, "Undefined macro step for '[#{macro_phrase}'." if macro.nil?
|
34
|
+
|
35
|
+
# Retrieve macro argument names and their associated value from the table
|
36
|
+
params = macro.validate_params(macro_phrase, nil)
|
37
|
+
|
38
|
+
# Render the steps
|
39
|
+
rendered_steps = macro.expand(params)
|
40
|
+
|
41
|
+
# Execute the steps
|
42
|
+
steps(rendered_steps)
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
# This step is used to invoke a macro-step with a table argument.
|
47
|
+
# Example:
|
48
|
+
# When I [create the following "Registrant" contact]:
|
49
|
+
# |name|John Doe|
|
50
|
+
# |city|Gotham City|
|
51
|
+
# |street| Main street|
|
52
|
+
# |street3| Small street|
|
53
|
+
When(/^I \[([^\]]+\]:)$/) do |macro_phrase, table_argument|
|
54
|
+
macro = find_macro(macro_phrase)
|
55
|
+
raise StandardError, "Undefined macro step for '#{macro_phrase}'." if macro.nil?
|
56
|
+
|
57
|
+
unless table_argument.kind_of?(Cucumber::Ast::Table)
|
58
|
+
raise StandardError, "This step must have a table as an argument."
|
59
|
+
end
|
60
|
+
|
61
|
+
# Retrieve macro argument names and their associated value from the table
|
62
|
+
params = macro.validate_params(macro_phrase, table_argument.rows_hash())
|
63
|
+
|
64
|
+
# Render the steps
|
65
|
+
rendered_steps = macro.expand(params)
|
66
|
+
|
67
|
+
# Execute the steps
|
68
|
+
steps(rendered_steps)
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
# End of file
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# File: constants.rb
|
2
|
+
# Purpose: definition of Macros4Cuke constants.
|
3
|
+
|
4
|
+
module Macros4Cuke # Module used as a namespace
|
5
|
+
# This constant keeps the current version of the gem.
|
6
|
+
Version = '0.0.01'
|
7
|
+
|
8
|
+
Description = "Macros for Cucumber"
|
9
|
+
|
10
|
+
# Constant Macros4Cuke::RootDir contains the absolute path of Rodent's root directory. Note: it also ends with a slash character.
|
11
|
+
unless defined?(RootDir)
|
12
|
+
# The initialisation of constant RootDir is guarded in order to avoid multiple initialisation (not allowed for constants)
|
13
|
+
RootDir = begin
|
14
|
+
require 'pathname' # Load Pathname class from standard library
|
15
|
+
rootdir = Pathname(__FILE__).dirname.parent.parent.expand_path()
|
16
|
+
rootdir.to_s() + '/' # Append trailing slash character to it
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end # module
|
20
|
+
|
21
|
+
# End of file
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# File: macro-step-support.rb
|
2
|
+
|
3
|
+
require_relative "macro-step"
|
4
|
+
|
5
|
+
module Macros4Cuke # Module used as a namespace
|
6
|
+
|
7
|
+
# Mix-in module that should be extending World objects of Cucumber.
|
8
|
+
# Synopsis (in env.rb):
|
9
|
+
module MacroStepSupport
|
10
|
+
|
11
|
+
# Callback invoked when a World object is extend(ed) with this module.
|
12
|
+
def self.extended(world)
|
13
|
+
# Add & initialize an instance variable for macro support.
|
14
|
+
world.clear_macro_steps()
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
public
|
19
|
+
# Remove all macro steps
|
20
|
+
def clear_macro_steps()
|
21
|
+
@macro_steps = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
# Return true iff the host has a macro with the given key.
|
25
|
+
def has_macro?(aMacroPhrase, mode)
|
26
|
+
key = MacroStep::macro_key(aMacroPhrase, mode)
|
27
|
+
return @macro_steps.include? key
|
28
|
+
end
|
29
|
+
|
30
|
+
# Add a new macro.
|
31
|
+
# Pre-condition: there is no existing macro with the same key.
|
32
|
+
def add_macro(aPhrase, aTemplate)
|
33
|
+
if has_macro?(aPhrase, :definition)
|
34
|
+
raise StandardError, "Macro step for '[#{aPhrase}' already exist."
|
35
|
+
else
|
36
|
+
new_macro = MacroStep.new(aPhrase, aTemplate)
|
37
|
+
@macro_steps[new_macro.name] = new_macro
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Retrieve the macro, given a phrase.
|
42
|
+
def find_macro(aMacroPhrase)
|
43
|
+
return @macro_steps[MacroStep::macro_key(aMacroPhrase, :invokation)]
|
44
|
+
end
|
45
|
+
|
46
|
+
end # module
|
47
|
+
|
48
|
+
end # module
|
49
|
+
|
50
|
+
# End of file
|
@@ -0,0 +1,221 @@
|
|
1
|
+
# File: macro-step.rb
|
2
|
+
# Purpose: Implementation of the MacroStep class.
|
3
|
+
|
4
|
+
require 'mustache' # Load the Mustache template library
|
5
|
+
|
6
|
+
module Macros4Cuke # Module used as a namespace
|
7
|
+
|
8
|
+
# In essence, a macro step object represents a Cucumber step that stands for a sequence of steps.
|
9
|
+
class MacroStep
|
10
|
+
# A Mustache instance that expands the steps upon request.
|
11
|
+
attr_reader(:renderer)
|
12
|
+
|
13
|
+
# Name of the macro as derived from the macro phrase.
|
14
|
+
attr_reader(:name)
|
15
|
+
|
16
|
+
# The list of macro arguments that appears in the macro phrase.
|
17
|
+
attr_reader(:phrase_args)
|
18
|
+
|
19
|
+
# The list of macro argument names (as appearing in the Mustache template and in the macro phrase).
|
20
|
+
attr_reader(:args)
|
21
|
+
|
22
|
+
# Constructor.
|
23
|
+
# [aMacroPhrase]
|
24
|
+
# [aTemplate] The source text of the steps to be expanded upon macro invokation.
|
25
|
+
def initialize(aMacroPhrase, aTemplate)
|
26
|
+
@name = self.class.macro_key(aMacroPhrase, :definition)
|
27
|
+
|
28
|
+
# Retrieve the macro arguments embedded in the phrase.
|
29
|
+
@phrase_args = scan_arguments(aMacroPhrase, :definition)
|
30
|
+
@args = @phrase_args.dup()
|
31
|
+
|
32
|
+
# The expansion text is a Mustache template
|
33
|
+
@renderer = Mustache.new
|
34
|
+
renderer.template = aTemplate
|
35
|
+
|
36
|
+
# Retrieve the Mustache tag names from the template and add them as macro arguments
|
37
|
+
add_tags_multi(renderer.template.tokens())
|
38
|
+
@args = @args.flatten.uniq.sort
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# Compute the identifier of the macro from the given macro phrase.
|
43
|
+
# A macro phrase is a text that must start with a recognised verb and may contain zero or more placeholders.
|
44
|
+
# In definition mode, a placeholder is delimited by double or triple mustaches (accolades)
|
45
|
+
# In invokation mode, a placeholder is delimited by double quotes.
|
46
|
+
# The rule for building the identifier are:
|
47
|
+
# - Leading and trailing space(s) are removed.
|
48
|
+
# - Every sequence of one or more space(s) is converted into an underscore
|
49
|
+
# - Each placeholder (i.e. = delimiters + enclosed text) is converted into a letter X.
|
50
|
+
# - The endings are transformed as follows: ] => '', ]: => _T
|
51
|
+
# Example:
|
52
|
+
# Consider the macro phrase: 'create the following "contactType" contact]:'
|
53
|
+
# The resulting macro_key is: 'create_the_following_X_contact_T'
|
54
|
+
def self.macro_key(aMacroPhrase, mode)
|
55
|
+
stripped_phrase = aMacroPhrase.strip # Remove leading ... trailing space(s)
|
56
|
+
|
57
|
+
|
58
|
+
# Replace all consecutive whitespaces by an underscore
|
59
|
+
stripped_phrase.gsub!(/\s+/, '_')
|
60
|
+
|
61
|
+
|
62
|
+
# Determine the pattern to isolate each argument/paramter with its delimiters
|
63
|
+
pattern = case mode
|
64
|
+
when :definition
|
65
|
+
/\{{2,3}[^}]*\}{2,3}/
|
66
|
+
when :invokation
|
67
|
+
/"[^"]*"/
|
68
|
+
end
|
69
|
+
|
70
|
+
# Each text between quotes or mustaches is replaced by the letter X
|
71
|
+
normalized = stripped_phrase.gsub(pattern, 'X')
|
72
|
+
|
73
|
+
# Drop the "]" ending or replace the "]:# ending by "_T"
|
74
|
+
key = if normalized.end_with?("]")
|
75
|
+
normalized.chop()
|
76
|
+
else
|
77
|
+
normalized.sub(/\]:/, '_T')
|
78
|
+
end
|
79
|
+
|
80
|
+
return key
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# Render the steps from the template, given the values
|
85
|
+
# taken by the parameters
|
86
|
+
# [macro_parameters] a Hash with pairs of the kind: macro argument name => value
|
87
|
+
def expand(macro_parameters)
|
88
|
+
return renderer.render(macro_parameters)
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# Build a Hash from the given raw data.
|
93
|
+
# [aPhrase] an instance of the macro phrase.
|
94
|
+
# [rawData] An Array of couples.
|
95
|
+
# Each couple is of the form: argument name, a value.
|
96
|
+
# Multiple rows with same argument name are acceptable.
|
97
|
+
def validate_params(aPhrase, rawData)
|
98
|
+
macro_parameters = {}
|
99
|
+
|
100
|
+
# Retrieve the value(s) per variable in the phrase.
|
101
|
+
quoted_values = scan_arguments(aPhrase, :invokation)
|
102
|
+
quoted_values.each_with_index do |val, index|
|
103
|
+
macro_parameters[phrase_args[index]] = val
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
unless rawData.nil?
|
108
|
+
rawData.each do |(key, value)|
|
109
|
+
raise StandardError,"Unknown macro argument #{key}." unless @args.include? key
|
110
|
+
if macro_parameters.include? key
|
111
|
+
if macro_parameters[key].kind_of?(Array)
|
112
|
+
macro_parameters[key] << value
|
113
|
+
else
|
114
|
+
macro_parameters[key] = [macro_parameters[key], value]
|
115
|
+
end
|
116
|
+
else
|
117
|
+
macro_parameters[key] = value
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
return macro_parameters
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
private
|
127
|
+
# Retrieve from the macro phrase, all the text between "mustaches" or double quotes.
|
128
|
+
# Returns an array. Each of its elements corresponds to quoted text.
|
129
|
+
# Example:
|
130
|
+
# aMacroPhrase = 'a "qualifier" text with "quantity" placeholders.'
|
131
|
+
# Results in : ["qualifier", "quantity"]
|
132
|
+
# [aMacroPhrase] A phrase
|
133
|
+
# [mode] one of the following: :definition, :invokation
|
134
|
+
def scan_arguments(aMacroPhrase, mode)
|
135
|
+
# determine the syntax of the arguments/parameters
|
136
|
+
# as a regular expression with one capturing group
|
137
|
+
pattern = case mode
|
138
|
+
when :definition
|
139
|
+
/{{{([^}]*)}}}|{{([^}]*)}}/ # Two capturing groups!...
|
140
|
+
when :invokation
|
141
|
+
/"([^"]*)"/
|
142
|
+
else
|
143
|
+
raise Standard, "Internal error: Unknown mode argument #{mode}"
|
144
|
+
end
|
145
|
+
raw_result = aMacroPhrase.scan(pattern)
|
146
|
+
return raw_result.flatten.compact
|
147
|
+
end
|
148
|
+
|
149
|
+
# Visit an array of tokens of which the first element is the :multi symbol.
|
150
|
+
# Every found template variable is added to the 'args' attribute.
|
151
|
+
# [tokens] An array that begins with the :multi symbol
|
152
|
+
def add_tags_multi(tokens)
|
153
|
+
first_token = tokens.shift
|
154
|
+
unless first_token == :multi
|
155
|
+
raise StandardError, "Expecting a :multi token instead of a #{first_token}"
|
156
|
+
end
|
157
|
+
|
158
|
+
tokens.each do |an_opcode|
|
159
|
+
case an_opcode[0]
|
160
|
+
when :static
|
161
|
+
# Do nothing...
|
162
|
+
|
163
|
+
when :mustache
|
164
|
+
add_tags_mustache(an_opcode)
|
165
|
+
|
166
|
+
when String
|
167
|
+
#Do nothing...
|
168
|
+
else
|
169
|
+
raise StandardError, "Unknown Mustache token type #{an_opcode.first}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# [mustache_opcode] An array with the first element being :mustache
|
175
|
+
def add_tags_mustache(mustache_opcode)
|
176
|
+
mustache_opcode.shift() # Drop the :mustache symbol
|
177
|
+
|
178
|
+
case mustache_opcode[0]
|
179
|
+
when :etag
|
180
|
+
triplet = mustache_opcode[1]
|
181
|
+
raise StandardError, "expected 'mustache' token instead of '#{triplet[0]}'" unless triplet[0] == :mustache
|
182
|
+
raise StandardError, "expected 'fetch' token instead of '#{triplet[1]}'" unless triplet[1] == :fetch
|
183
|
+
@args << triplet.last
|
184
|
+
|
185
|
+
when :fetch
|
186
|
+
@args << mustache_opcode.last
|
187
|
+
|
188
|
+
when :section
|
189
|
+
add_tags_section(mustache_opcode)
|
190
|
+
|
191
|
+
else
|
192
|
+
raise StandardError, "Unknown Mustache token type #{mustache_opcode.first}"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
def add_tags_section(opcodes)
|
198
|
+
opcodes.shift() # Drop the :section symbol
|
199
|
+
|
200
|
+
opcodes.each do |op|
|
201
|
+
case op[0]
|
202
|
+
when :mustache
|
203
|
+
add_tags_mustache(op)
|
204
|
+
|
205
|
+
when :multi
|
206
|
+
add_tags_multi(op)
|
207
|
+
|
208
|
+
when String
|
209
|
+
return
|
210
|
+
else
|
211
|
+
raise StandardError, "Unknown Mustache token type #{op.first}"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
end # class
|
217
|
+
|
218
|
+
end # module
|
219
|
+
|
220
|
+
|
221
|
+
# End of file
|
data/lib/macros4cuke.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# encoding: utf-8 -- You should see a paragraph character: §
|
2
|
+
# File: macros4cuke.rb
|
3
|
+
# This file acts as a jumping-off point for loading dependencies expected for a Macros4Cuke user.
|
4
|
+
|
5
|
+
require_relative './macros4cuke/constants'
|
6
|
+
require_relative './macros4cuke/macro-step-support'
|
7
|
+
|
8
|
+
# End of file
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: macros4cuke
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.01
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dimitri Geshef
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: cucumber
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: mustache
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: ! "\tMacros4Cuke is a lightweight library that adds a macro facility
|
63
|
+
your Cucumber scenarios.\n In short, you will be able to create new steps that
|
64
|
+
will replace a sequence of lower-level steps. \n"
|
65
|
+
email: famished.tiger@yahoo.com
|
66
|
+
executables: []
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- cucumber.yml
|
71
|
+
- HISTORY.md
|
72
|
+
- LICENSE.txt
|
73
|
+
- README.md
|
74
|
+
- lib/macros4cuke.rb
|
75
|
+
- lib/macro_steps.rb
|
76
|
+
- lib/macros4cuke/constants.rb
|
77
|
+
- lib/macros4cuke/macro-step-support.rb
|
78
|
+
- lib/macros4cuke/macro-step.rb
|
79
|
+
- features/demo01.feature
|
80
|
+
- features/demo02.feature
|
81
|
+
- features/demo03.feature
|
82
|
+
- features/demo04.feature
|
83
|
+
- features/step_definitions/demo_steps.rb
|
84
|
+
- features/step_definitions/use_macro_steps.rb
|
85
|
+
- features/support/env.rb
|
86
|
+
homepage: https://github.com/famished-tiger/Macros4Cuke
|
87
|
+
licenses: []
|
88
|
+
post_install_message: ! '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
89
|
+
|
90
|
+
Thank you for installing Macros4Cuke...
|
91
|
+
|
92
|
+
Enjoy using Cucumber with macros...
|
93
|
+
|
94
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
95
|
+
|
96
|
+
'
|
97
|
+
rdoc_options: []
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ! '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 1.8.24
|
115
|
+
signing_key:
|
116
|
+
specification_version: 3
|
117
|
+
summary: Macros for Cucumber
|
118
|
+
test_files: []
|