macros4cuke 0.3.00 → 0.3.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/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 0.3.01 / 2013-05-10
2
+ * [NEW] features/ folder: added `demo06.feature` file showing the new conditional section.
3
+ * [NEW] File `placeholder_spec.rb`: Added a RSpec file to test the Placeholder class.
4
+ * [CHANGE] File `macro_steps.rb` Macro-step definition accepts the '*' Gherkin keyword.
5
+ * [CHANGE] Method `Engine#compile_sction` completed and tested to support section elements.
6
+ * [CHANGE] Method `Section#variables` expanded to support section elements.
7
+ * [CHANGE] Method `Engine#variables` expanded to support section elements.
8
+ * [CHANGE] Method `Engine#compile_line` added two formatting rules.
9
+ * [CHANGE] Method `MacroStep#scan_arguments` now un-escape the \" sequence into plain quote.
10
+ * [CHANGE] examples/ folder expanded and reorganized
11
+ * [FIX] Method `Section#render` fixed typo in call to __method__
12
+
1
13
  ## 0.3.00 / 2013-05-09 Version number bumped
2
14
  * [CHANGE] Class `Templating::Engine` can handle conditional tags (TODO: document).
3
15
  * [CHANGE] File `engine_spec.rb`: Added a RSpec examples to test the conditional tags.
@@ -0,0 +1,6 @@
1
+ default: --strict -v -b --format progress features
2
+ html: --strict -v -b --format html --out result.html features
3
+ usage: --strict -v -b --format usage --out usage.txt features
4
+
5
+
6
+
@@ -0,0 +1,54 @@
1
+ # File: basic.feature
2
+
3
+ Feature: Show -visually- the several ways to use macros
4
+ As a Cuke user
5
+ So that I enjoy writing scenario.
6
+
7
+
8
+ Scenario: Definition of a simple macro-step with two arguments
9
+ Given I define the step "* I [travel from <origin> to <destination>]" to mean:
10
+ """
11
+ When I leave '<origin>'
12
+ And I arrive in <destination>
13
+ """
14
+
15
+ Scenario: Do a simple travel
16
+ # Call a macro-step defined earlier
17
+ When I [travel from "Brussels" to "Rome"]
18
+ # You should see the output:
19
+ # I leave 'Brussels'
20
+ # I arrive in Rome
21
+
22
+
23
+ # Actual values can have embedded double quotes provided they are escaped.
24
+ When I [travel from "Tampa" to "\"Little Italy\""]
25
+ # You should see the output:
26
+ # I leave 'Tampa'
27
+ # I arrive in "Little Italy"
28
+
29
+ # Actual values MUST be present in the phrase (but they can be empty)
30
+ When I [travel from "" to "North Pole"]
31
+ # You should see the output:
32
+ # I leave ''
33
+ # I arrive in North Pole
34
+
35
+
36
+
37
+ Scenario: Defining a macro that's calling other macro-steps
38
+ Given I define the step "* I [travel from <origin> to <destination> and back]" to mean:
39
+ """
40
+ # The next two steps are, in fact, macro-step invokations
41
+ When I [travel from "<origin>" to "<destination>"]
42
+ When I [travel from "<destination>" to "<origin>"]
43
+ """
44
+
45
+ Scenario: Do a travel back and forth
46
+ When I [travel from "Paris" to "London" and back]
47
+
48
+ # You should see the output:
49
+ # I leave 'Paris'
50
+ # I arrive in London
51
+ # I leave 'London'
52
+ # I arrive in Paris
53
+
54
+
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+ # File: demo_steps.rb
3
+ # A few step definitions for demo and testing purpose.
4
+
5
+ When(/^I leave '(.*)'$/) do |city|
6
+ show "I leave #{city}"
7
+ end
8
+
9
+
10
+ When(/^I visit (.+)$/) do |city|
11
+ show "I visit #{city}"
12
+ end
13
+
14
+
15
+ When(/^I arrive in (.+)$/) do |city|
16
+ show "I arrive in #{city}"
17
+ end
18
+
19
+ When(/^I type \"([^"]*)\"$/) do |text|
20
+ show text
21
+ end
22
+
23
+ # 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,38 @@
1
+ # encoding: utf-8 You should see a paragraph character: §
2
+ # File: env.rb
3
+
4
+
5
+ module DemoMacros4Cuke # Use the module as a namespace
6
+
7
+
8
+ # Class created just for testing and demonstration purposes.
9
+ # Its instance, will record the output emitted by the steps.
10
+ class TracingWorld
11
+ # Will contain the text emitted by the steps
12
+ attr_reader(:trace_steps)
13
+
14
+
15
+ def initialize()
16
+ # Constructor
17
+ @trace_steps = []
18
+ end
19
+
20
+ public
21
+ # Write the given text to the error console
22
+ def show(someText)
23
+ # Replace every \" sequence by genuine "
24
+ unescaped = someText.gsub(/\\"/, '"')
25
+ $stderr.puts(unescaped)
26
+ end
27
+
28
+
29
+ end # class
30
+
31
+ end # module
32
+
33
+ # For testing purpose we override the default Cucumber behaviour
34
+ # making our world object an instance of the TracingWorld class
35
+ World { DemoMacros4Cuke::TracingWorld.new }
36
+
37
+
38
+ # End of file
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8 You should see a paragraph character: §
2
+ # File: macro_support.rb
3
+ # Purpose: Add the support for macros in Cucumber.
4
+ # This file is meant to be put next to the 'env.rb' file of your Cucumeber project.
5
+
6
+
7
+ # Macros4Cuke step one: Load modules and classes from the gem.
8
+ require 'macros4cuke'
9
+
10
+
11
+ # Macros4Cuke step two: extend the world object with the mix-in module
12
+ # that adds the support for macros in Cucumber.
13
+ World(Macros4Cuke::MacroStepSupport)
14
+
15
+
16
+ # End of file
@@ -1,51 +1,14 @@
1
1
  # File: travelling-demo.feature
2
2
 
3
- Feature: Show -visually- the several ways to use macros
3
+
4
+ Feature: Show how to define & use macro-steps with data table
4
5
  As a Cuke user
5
6
  So that I enjoy writing scenario.
6
7
 
7
8
 
8
- Scenario: Definition of a simple macro-step with two arguments
9
- Given I define the step "When I [travel from <origin> to <destination>]" to mean:
10
- """
11
- When I leave <origin>
12
- And I arrive in <destination>
13
- """
14
-
15
- Scenario: Do a simple travel
16
- # Call a macro-step defined earlier
17
- When I [travel from "Brussels" to "Rome"]
18
-
19
- # You should see the output:
20
- # I leave Brussels
21
- # I arrive in Rome
22
-
23
- # Actual values can have embedded double quotes provided they are escaped.
24
- When I [travel from "Tampa" to "\"Little Italy\""]
25
-
26
-
27
-
28
-
29
- Scenario: Defining a macro calling other macro(s)
30
- Given I define the step "When I [travel from <origin> to <destination> and back]" to mean:
31
- """
32
- # The next two steps are, in fact, macro-step invokations
33
- When I [travel from "<origin>" to "<destination>"]
34
- When I [travel from "<destination>" to "<origin>"]
35
- """
36
-
37
- Scenario: Do a travel back and forth
38
- When I [travel from "Paris" to "London" and back]
39
-
40
- # You should see the output:
41
- # I leave Paris
42
- # I arrive in London
43
- # I leave London
44
- # I arrive in Paris
45
-
46
-
47
9
  Scenario: Defining a macro that requires a data table
48
- Given I define the step "When I [fill in the form with]:" to mean:
10
+ # There is a colon : just after the phrase closing ']'. A data table must be used.
11
+ Given I define the step "* I [fill in the form with]:" to mean:
49
12
  """
50
13
  When I type "<firstname>"
51
14
  And I type "<lastname>"
@@ -92,7 +55,7 @@ Scenario: Using a macro-step with a data table
92
55
 
93
56
 
94
57
  Scenario: Demonstrate that it is possible to use a sub-step with a data table
95
- Given I define the step "When I [fill in, as a Londonian, the form with]:" to mean:
58
+ Given I define the step "* I [fill in, as a Londonian, the form with]:" to mean:
96
59
  """
97
60
  When I [fill in the form with]:
98
61
  |firstname| <firstname>|
@@ -9,7 +9,7 @@ Scenario: Definition of a simple macro-step
9
9
  # The next step creates a macro(-step)
10
10
  # The syntax of the new macro-step is specified between double quotes.
11
11
  # The steps to execute when the macro is used/invoked are listed in the multiline triple quotes arguments.
12
- Given I define the step "When I [log in]" to mean:
12
+ Given I define the step "* I [log in]" to mean:
13
13
  """
14
14
  Given I landed in the homepage
15
15
  When I click "Sign in"
@@ -30,4 +30,4 @@ Invoked step: ... I click "Sign in"
30
30
  Invoked step: ... I fill in "Username" with "johndoe"
31
31
  Invoked step: ... I fill in "Password" with "unguessable"
32
32
  Invoked step: ... I click "Submit"
33
- """
33
+ """
@@ -10,7 +10,7 @@ Scenario: Creating a basic scenario with one argument
10
10
  # The syntax of the new macro-step is specified between the double quotes.
11
11
  # The steps to execute when the macro is used/invoked are listed in the multiline triple quotes arguments.
12
12
  # The macro argument is put between chevrons <...>.
13
- Given I define the step "When I [log in\[\] as <userid>]" to mean:
13
+ Given I define the step "* I [log in\[\] as <userid>]" to mean:
14
14
  """
15
15
  Given I landed in the homepage
16
16
  When I click "Sign in"
@@ -31,4 +31,4 @@ Invoked step: ... I click "Sign in"
31
31
  Invoked step: ... I fill in "Username" with "guest"
32
32
  Invoked step: ... I fill in "Password" with "unguessable"
33
33
  Invoked step: ... I click "Submit"
34
- """
34
+ """
@@ -9,7 +9,7 @@ Scenario: defining basic macro with multiple arguments
9
9
  # The next step creates a macro(-step)double quotes.
10
10
  # The steps to execute when the macro is used/invoked are listed in the multiline triple quotes arguments.
11
11
  # The macro-step arguments are put between chevrons <...>.
12
- Given I define the step "When I [enter my userid <userid> and password <password>]" to mean:
12
+ Given I define the step "* I [enter my userid <userid> and password <password>]" to mean:
13
13
  """
14
14
  Given I landed in the homepage
15
15
  When I click "Sign in"
@@ -33,7 +33,7 @@ Invoked step: ... I click "Submit"
33
33
  """
34
34
 
35
35
  Scenario: A macro invoking another macro (YES, it's possible!)
36
- Given I define the step "When I [enter my credentials]" to mean:
36
+ Given I define the step "* I [enter my credentials]" to mean:
37
37
  """
38
38
  # Notice that the next step is invoking the first macro above
39
39
  When I [enter my userid "guest" and password "unguessable"]
@@ -50,4 +50,4 @@ Invoked step: ... I click "Sign in"
50
50
  Invoked step: ... I fill in "Username" with "guest"
51
51
  Invoked step: ... I fill in "Password" with "unguessable"
52
52
  Invoked step: ... I click "Submit"
53
- """
53
+ """
@@ -10,7 +10,7 @@ Scenario: Defining a macro to be used with multiple arguments in a table
10
10
  # The syntax of the new macro-step is specified between double quotes.
11
11
  # The steps to execute when the macro is used/invoked are listed in the multiline triple quotes arguments.
12
12
  # The macro arguments are put between chevrons <...>.
13
- Given I define the step "When I [enter my credentials as]:" to mean:
13
+ Given I define the step "* I [enter my credentials as]:" to mean:
14
14
  """
15
15
  Given I landed in the homepage
16
16
  When I click "Sign in"
@@ -33,4 +33,4 @@ Invoked step: ... I click "Sign in"
33
33
  Invoked step: ... I fill in "Username" with "guest"
34
34
  Invoked step: ... I fill in "Password" with "unguessable"
35
35
  Invoked step: ... I click "Submit"
36
- """
36
+ """
@@ -10,7 +10,7 @@ Scenario: Defining a macro to be used with multiple arguments in a table
10
10
  # The syntax of the new macro-step is specified between double quotes.
11
11
  # The steps to execute when the macro is used/invoked are listed in the multiline triple quotes arguments.
12
12
  # The macro arguments are put between chevrons <...>.
13
- Given I define the step "When I [enter my profile as]:" to mean:
13
+ Given I define the step "* I [enter my profile as]:" to mean:
14
14
  """
15
15
  And I fill in "location" with "<location>"
16
16
  And I fill in "email" with "<email>"
@@ -34,4 +34,4 @@ Scenario: # Let's use the macro we created above
34
34
  Invoked step: ... I fill in "email" with "nobody@example.com"
35
35
  Invoked step: ... I fill in "comment" with "First comment line<br/>Second comment line<br/>Third comment line"
36
36
  Invoked step: ... I click "Save"
37
- """
37
+ """
@@ -0,0 +1,73 @@
1
+ # File: demo06.feature
2
+
3
+ Feature: Show how to define conditional substeps in a macro-step.
4
+ As a Cuke user
5
+ So that I enjoy writing scenario.
6
+
7
+
8
+ Scenario: Defining a macro with conditional substeps
9
+ Given I define the step "* I [fill in the form with]:" to mean:
10
+ """
11
+ When I fill in "first_name" with "<firstname>"
12
+ And I fill in "last_name" with "<lastname>"
13
+ And I fill in "street_address" with "<street_address>"
14
+ And I fill in "zip" with "<postcode>"
15
+ And I fill in "city" with "<city>"
16
+ And I fill in "country" with "<country>"
17
+ # Let's assume that e-mail is optional
18
+ <?email>
19
+ And I fill in "email" with "<email>"
20
+ </email>
21
+ # Let's also assume that comment is optional
22
+ <?comment> And I fill in "comment" with "<comment>"</comment>
23
+ And I click "Save"
24
+ """
25
+
26
+ Scenario: # Let's use the macro-step WITHOUT the optional argument values.
27
+ When I [fill in the form with]:
28
+ |firstname|Alice|
29
+ |lastname| Inn |
30
+ |street_address| 11, No Street|
31
+ |city| Nowhere-City|
32
+ |country|Wonderland|
33
+ # No e-mail
34
+ # No comment
35
+
36
+ # The next step verifies that the optional steps from the macro were ignored.
37
+ Then I expect the following step trace:
38
+ """
39
+ Invoked step: ... I fill in "first_name" with "Alice"
40
+ Invoked step: ... I fill in "last_name" with "Inn"
41
+ Invoked step: ... I fill in "street_address" with "11, No Street"
42
+ Invoked step: ... I fill in "zip" with ""
43
+ Invoked step: ... I fill in "city" with "Nowhere-City"
44
+ Invoked step: ... I fill in "country" with "Wonderland"
45
+ Invoked step: ... I click "Save"
46
+ """
47
+
48
+
49
+ Scenario: # Let's use the macro-step WITH the optional argument values.
50
+ # Redo, now with e-mail and comment
51
+ When I [fill in the form with]:
52
+ |firstname|Alice|
53
+ |lastname| Inn |
54
+ |street_address| 11, No Street|
55
+ |city| Nowhere-City|
56
+ |country|Wonderland|
57
+ # Here come the optional values
58
+ |email|alice.inn@wonder.land|
59
+ |comment|No comment!|
60
+
61
+ # The next step verifies that the optional steps from the macro were ignored.
62
+ Then I expect the following step trace:
63
+ """
64
+ Invoked step: ... I fill in "first_name" with "Alice"
65
+ Invoked step: ... I fill in "last_name" with "Inn"
66
+ Invoked step: ... I fill in "street_address" with "11, No Street"
67
+ Invoked step: ... I fill in "zip" with ""
68
+ Invoked step: ... I fill in "city" with "Nowhere-City"
69
+ Invoked step: ... I fill in "country" with "Wonderland"
70
+ Invoked step: ... I fill in "email" with "alice.inn@wonder.land"
71
+ Invoked step: ... I fill in "comment" with "No comment!"
72
+ Invoked step: ... I click "Save"
73
+ """
@@ -20,23 +20,4 @@ Then(/^I expect the following step trace:$/) do |step_text|
20
20
  end
21
21
 
22
22
 
23
- When(/^I leave (.+)$/) do |city|
24
- show "I leave #{city}"
25
- end
26
-
27
-
28
- When(/^I visit (.+)$/) do |city|
29
- show "I visit #{city}"
30
- end
31
-
32
-
33
- When(/^I arrive in (.+)$/) do |city|
34
- show "I arrive in #{city}"
35
- end
36
-
37
- When(/^I type \"([^"]*)\"$/) do |text|
38
- show text
39
- end
40
-
41
-
42
23
  # End of file
@@ -3,7 +3,7 @@
3
3
 
4
4
  module Macros4Cuke # Module used as a namespace
5
5
  # The version number of the gem.
6
- Version = '0.3.00'
6
+ Version = '0.3.01'
7
7
 
8
8
  # Brief description of the gem.
9
9
  Description = "Macros for Cucumber"
@@ -158,11 +158,14 @@ private
158
158
  # /{{{([^}]*)}}}|{{([^}]*)}}/ # Two capturing groups!...
159
159
  when :invokation
160
160
  /"((?:[^\\"]|\\.)*)"/
161
- else
162
- raise InternalError, "Internal error: Unknown mode argument #{mode}"
163
161
  end
164
162
  raw_result = aMacroPhrase.scan(pattern)
165
- return raw_result.flatten.compact
163
+ args = raw_result.flatten.compact
164
+
165
+ # Replace escaped quotes by quote character.
166
+ args.map! { |a| a.sub(/\\"/, '"') } if mode == :invokation
167
+
168
+ return args
166
169
  end
167
170
 
168
171
  # Return the substeps text after some transformation
@@ -204,5 +207,4 @@ end # class
204
207
 
205
208
  end # module
206
209
 
207
-
208
210
  # End of file
@@ -130,14 +130,31 @@ public
130
130
  def add_child(aChild)
131
131
  children << aChild
132
132
  end
133
+
134
+ # Retrieve all placeholder names that appear in the template.
135
+ # @return [Array] The list of placeholder names.
136
+ def variables()
137
+ all_vars = children.each_with_object([]) do |a_child, subResult|
138
+ case a_child
139
+ when Placeholder
140
+ subResult << a_child.name
141
+ when Section
142
+ subResult.concat(a_child.variables)
143
+ else
144
+ # Do nothing
145
+ end
146
+ end
147
+
148
+ return all_vars.flatten.uniq
149
+ end
150
+
133
151
 
134
- protected
135
152
  # Render the placeholder given the passed arguments.
136
153
  # This method has the same signature as the {Engine#render} method.
137
154
  # @return [String] The text value assigned to the placeholder.
138
155
  # Returns an empty string when no value is assigned to the placeholder.
139
156
  def render(aContextObject, theLocals)
140
- raise NotImplementedError, "Method Section::#{_method_} must be implemented in subclass(es)."
157
+ raise NotImplementedError, "Method Section::#{__method__} must be implemented in subclass(es)."
141
158
  end
142
159
 
143
160
  end # class
@@ -175,7 +192,13 @@ public
175
192
  end
176
193
 
177
194
  return result
178
- end
195
+ end
196
+
197
+
198
+ # @return [String] The original text representation of the tag.
199
+ def to_s()
200
+ return "<?#{name}>"
201
+ end
179
202
 
180
203
  end # class
181
204
 
@@ -232,8 +255,20 @@ public
232
255
  def variables()
233
256
  # The result will be cached/memoized...
234
257
  @variables ||= begin
235
- vars = @representation.select { |element| element.is_a?(Placeholder) }
236
- vars.map(&:name)
258
+ vars = @representation.each_with_object([]) do |element, subResult|
259
+ case element
260
+ when Placeholder
261
+ subResult << element.name
262
+
263
+ when Section
264
+ subResult.concat(element.variables)
265
+
266
+ else
267
+ # Do nothing
268
+ end
269
+ end
270
+
271
+ vars.flatten.uniq
237
272
  end
238
273
 
239
274
  return @variables
@@ -302,10 +337,44 @@ private
302
337
  return compile_sections(compiled_lines.flatten())
303
338
  end
304
339
 
305
- # Convert the array of raw entries into full-fledged template elements.
340
+ # Convert the array of raw entries (per line) into full-fledged template elements.
306
341
  def compile_line(aRawLine)
307
342
  line_rep = aRawLine.map { |couple| compile_couple(couple) }
308
- line_rep << EOLine.new
343
+
344
+ # Apply the rule: when a line just consist of spaces and a section element,
345
+ # then remove all the spaces from that line.
346
+ section_item = nil
347
+ line_to_squeeze = line_rep.all? do |item|
348
+ case item
349
+ when StaticText
350
+ item.source =~ /\s+/
351
+
352
+ when Section, SectionEndMarker
353
+ if section_item.nil?
354
+ section_item = item
355
+ true
356
+ else
357
+ false
358
+ end
359
+ else
360
+ false
361
+ end
362
+ end
363
+ if line_to_squeeze && ! section_item.nil?
364
+ line_rep = [section_item]
365
+ else
366
+ # Apply another rule: if last item in line is an end of section marker,
367
+ # then place eoline before that item. Otherwise, end the line with a eoline marker.
368
+ if line_rep.last.is_a?(SectionEndMarker)
369
+ section_end = line_rep.pop()
370
+ line_rep << EOLine.new
371
+ line_rep << section_end
372
+ else
373
+ line_rep << EOLine.new
374
+ end
375
+ end
376
+
377
+ return line_rep
309
378
  end
310
379
 
311
380
 
@@ -353,10 +422,11 @@ private
353
422
  return result
354
423
  end
355
424
 
356
- # Group the elements by sections.
425
+ # Transform a flat sequence of elements into a hierarchy of sections.
357
426
  # @param flat_sequence [Array] a linear list of elements (including sections)
358
427
  def compile_sections(flat_sequence)
359
428
  open_sections = [] # The list of nested open sections
429
+
360
430
  compiled = flat_sequence.each_with_object([]) do |element, subResult|
361
431
  case element
362
432
  when Section
@@ -364,7 +434,7 @@ private
364
434
 
365
435
  when SectionEndMarker
366
436
  if open_sections.empty?
367
- raise StandardError, "End of section</#{element.name}> found while no corresponding section must be closed."
437
+ raise StandardError, "End of section</#{element.name}> found while no corresponding section is open."
368
438
  end
369
439
  if element.name != open_sections.last.name
370
440
  raise StandardError, "End of section</#{element.name}> doesn't match current section '#{open_sections.last.name}'."
@@ -381,6 +451,8 @@ private
381
451
 
382
452
  end
383
453
 
454
+ raise StandardError, "Unterminated section #{open_sections.last}." unless open_sections.empty?
455
+
384
456
  return compiled
385
457
  end
386
458
 
@@ -91,7 +91,22 @@ SNIPPET
91
91
 
92
92
  text.should == expectation
93
93
  end
94
+
95
+ it "should un-escape the double-quotes for phrase arguments" do
96
+ specific_phrase = %q|enter my credentials as "quotable\""|
97
+ text = subject.expand(specific_phrase, [ ['password', 'no-secret'] ])
98
+ expectation = <<-SNIPPET
99
+ Given I landed in the homepage
100
+ When I click "Sign in"
101
+ And I fill in "Username" with "quotable""
102
+ And I fill in "Password" with "no-secret"
103
+ And I click "Submit"
104
+ SNIPPET
105
+
106
+ text.should == expectation
107
+ end
94
108
 
109
+
95
110
  it "should complain when an unknown variable is used" do
96
111
  # Error case: there is no macro argument called <unknown>
97
112
  error_message = "Unknown macro-step argument 'unknown'."
@@ -27,10 +27,11 @@ SNIPPET
27
27
  source = <<-SNIPPET
28
28
  When I fill in "firstname" with "<firstname>"
29
29
  And I fill in "lastname" with "<lastname>"
30
- <?address>And I fill in "address" with "<address>"</address>
30
+ <?address> And I fill in "address" with "<address>"</address>
31
31
  <?birthdate>
32
32
  And I fill in "birthdate" with "<birthdate>"
33
33
  </birthdate>
34
+ <?dummy></dummy>
34
35
  And I click "Register"
35
36
  SNIPPET
36
37
  end
@@ -201,6 +202,29 @@ SNIPPET
201
202
  lambda { Engine.new text_w_empty_arg }.should raise_error(Macros4Cuke::InvalidCharError, error_message)
202
203
  end
203
204
 
205
+ it "should complain when a section has no closing tag" do
206
+ # Removing an end of section tag...
207
+ text_w_open_section = sophisticated_template.sub(/<\/address>/, '')
208
+
209
+ error_message = "Unterminated section <?address>."
210
+ lambda { Engine.new text_w_open_section}.should raise_error(StandardError, error_message)
211
+ end
212
+
213
+ it "should complain when a closing tag does not correspond to currently open section" do
214
+ # Replacing an end of section tag by another...
215
+ text_w_wrong_end = sophisticated_template.sub(/<\/address>/, '</foobar>')
216
+
217
+ error_message = "End of section</foobar> doesn't match current section 'address'."
218
+ lambda { Engine.new text_w_wrong_end}.should raise_error(StandardError, error_message)
219
+ end
220
+
221
+ it "should complain when a closing tag is found while no section is open" do
222
+ # Replacing an end of section tag by another...
223
+ text_w_wrong_end = sophisticated_template.sub(/<\?birthdate>/, '</foobar>')
224
+ error_message = "End of section</foobar> found while no corresponding section is open."
225
+ lambda { Engine.new text_w_wrong_end}.should raise_error(StandardError, error_message)
226
+ end
227
+
204
228
  end # context
205
229
 
206
230
  context "Provided services" do
@@ -283,15 +307,25 @@ SNIPPET
283
307
  expected = <<-SNIPPET
284
308
  When I fill in "firstname" with "Anon"
285
309
  And I fill in "lastname" with "Eemoos"
286
-
287
-
288
310
  And I fill in "birthdate" with "1976-04-21"
289
-
290
311
  And I click "Register"
291
312
  SNIPPET
292
313
 
293
314
  rendered_text.should == expected
294
315
 
316
+ # Redo with another context
317
+ locals["birthdate"] = nil
318
+ locals["address"] = "122, Mercer Street"
319
+
320
+ rendered_text = instance.render(Object.new, locals)
321
+ expected = <<-SNIPPET
322
+ When I fill in "firstname" with "Anon"
323
+ And I fill in "lastname" with "Eemoos"
324
+ And I fill in "address" with "122, Mercer Street"
325
+ And I click "Register"
326
+ SNIPPET
327
+
328
+ rendered_text.should == expected
295
329
  end
296
330
 
297
331
 
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8 -- You should see a paragraph character: §
2
+ # File: section_spec.rb
3
+
4
+ require_relative '../../spec_helper'
5
+ require_relative '../../../lib/macros4cuke/templating/engine' # Load the classes under test
6
+
7
+ module Macros4Cuke
8
+
9
+ module Templating # Open this namespace to get rid of module qualifier prefixes
10
+
11
+
12
+ describe Placeholder do
13
+ # Default instantiation rule
14
+ subject { Placeholder.new('foobar') }
15
+
16
+ context "Creation and initialization:" do
17
+
18
+ it "should be created with a variable name" do
19
+ lambda { Placeholder.new('foobar') }.should_not raise_error
20
+ end
21
+
22
+ it "should know the name of its variable" do
23
+ subject.name.should == 'foobar'
24
+ end
25
+
26
+ end # context
27
+
28
+ context "Provided services:" do
29
+ it "should render an empty string when no actual value is bound to the placeholder" do
30
+ # Case: context has no value associated to 'foobar'
31
+ rendered_text = subject.render(Object.new, {})
32
+ rendered_text.should be_empty
33
+
34
+ # Case: locals Hash has a nil value associated to 'foobar'
35
+ rendered_text = subject.render(Object.new, {'foobar' => nil})
36
+ rendered_text.should be_empty
37
+
38
+ # Case: context object has a nil value associated to 'foobar'
39
+ context = Object.new
40
+ def context.foobar; nil; end # Add singleton method foobar that returns nil
41
+ rendered_text = subject.render(context, {})
42
+ rendered_text.should be_empty
43
+ end
44
+
45
+ it "should render the actual value bound to the placeholder" do
46
+ # Case: locals Hash has a value associated to 'foobar'
47
+ rendered_text = subject.render(Object.new, {'foobar' => 'hello'})
48
+ rendered_text.should == 'hello'
49
+
50
+ # Case: context object has a value associated to 'foobar'
51
+ context = Object.new
52
+ def context.foobar; 'world'; end # Add singleton method foobar that returns 'world'
53
+ rendered_text = subject.render(context, {})
54
+ rendered_text.should == 'world'
55
+ end
56
+
57
+ end # context
58
+
59
+ end # describe
60
+
61
+ end # module
62
+
63
+ end # module
64
+
65
+ # End of file
@@ -8,23 +8,85 @@ module Macros4Cuke
8
8
 
9
9
  module Templating # Open this namespace to get rid of module qualifier prefixes
10
10
 
11
+ # Spec for abstract class Section.
12
+ describe Section do
13
+ # Default instantiation rule
14
+ subject { Section.new('foobar') }
15
+
16
+ # Return a list of possible child elements
17
+ let(:sample_children) do
18
+ [ StaticText.new("Hello "),
19
+ Placeholder.new("user"),
20
+ EOLine.new
21
+ ]
22
+ end
23
+
24
+ context "Creation and initialization" do
25
+
26
+ it "should be created with a variable name" do
27
+ lambda { Section.new('foobar') }.should_not raise_error
28
+ end
29
+
30
+ it "should know the name of its variable" do
31
+ subject.name.should == 'foobar'
32
+ end
33
+
34
+ it "should have no child at start" do
35
+ subject.should have(0).children
36
+ end
37
+
38
+ end # context
39
+
40
+ context "Provided services:" do
41
+ it "should add child element(s)" do
42
+ sample_children.each do |a_child|
43
+ subject.add_child(a_child)
44
+ end
45
+
46
+ # Control that the addition work as expected
47
+ subject.children.should == sample_children
48
+ end
49
+
50
+ it "should know the name all child placeholders" do
51
+ # Case: simple flat list of children
52
+ sample_children.each { |a_child| subject.add_child(a_child) }
53
+ subject.variables.should == [ 'user' ]
54
+
55
+ # Case: at least one child is a group
56
+ parent = Section.new('son')
57
+ [
58
+ subject,
59
+ StaticText.new("Bye "),
60
+ Placeholder.new("firstname"),
61
+ EOLine.new
62
+ ].each { |a_child| parent.add_child(a_child) }
63
+
64
+ parent.variables.should == [ 'user', 'firstname']
65
+
66
+ end
67
+
68
+
69
+ it "should expect that its subclasses render the children" do
70
+ error_message = "Method Section::render must be implemented in subclass(es)."
71
+ lambda {subject.send(:render, Object.new, {}) }.should raise_error(NotImplementedError, error_message)
72
+ end
73
+
74
+ end # context
75
+
76
+ end # describe
77
+
78
+
79
+
11
80
 
12
81
  describe ConditionalSection do
13
82
 
14
- context "Creation and initialization" do
83
+ context "Creation and initialization:" do
15
84
 
16
85
  it "should be created with a variable name and a boolean" do
17
86
  lambda { ConditionalSection.new('foobar', false) }.should_not raise_error
18
87
  lambda { ConditionalSection.new('foobar', true) }.should_not raise_error
19
88
  end
20
89
 
21
- it "should know the name of its variable" do
22
- [false, true].each do |existence|
23
- instance = ConditionalSection.new('foobar', existence)
24
- instance.name.should == 'foobar'
25
- end
26
- end
27
-
28
90
  it "should know whether the rendition on existence of actual value or not" do
29
91
  [false, true].each do |existence|
30
92
  instance = ConditionalSection.new('foobar', existence)
@@ -32,16 +94,9 @@ describe ConditionalSection do
32
94
  end
33
95
  end
34
96
 
35
- it "should have no child at start" do
36
- [false, true].each do |existence|
37
- instance = ConditionalSection.new('foobar', existence)
38
- instance.should have(0).children
39
- end
40
- end
41
-
42
97
  end # context
43
98
 
44
- context "Provided services" do
99
+ context "Provided services:" do
45
100
  # Return a list of possible child elements
46
101
  let(:sample_children) do
47
102
  [ StaticText.new("Hello "),
@@ -52,15 +107,9 @@ describe ConditionalSection do
52
107
 
53
108
  # Default instantiation rule
54
109
  subject { ConditionalSection.new('foobar', true) }
55
-
56
- it "should add child element(s)" do
57
- sample_children.each do |a_child|
58
- subject.add_child(a_child)
59
- end
60
-
61
- # Control that the addition worek as expected
62
- subject.children.should == sample_children
63
-
110
+
111
+ it "should know its original source text" do
112
+ subject.to_s.should == '<?foobar>'
64
113
  end
65
114
 
66
115
  it "should render its children when conditions are met" do
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.3.00
4
+ version: 0.3.01
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-09 00:00:00.000000000 Z
12
+ date: 2013-05-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: cucumber
@@ -72,14 +72,20 @@ files:
72
72
  - LICENSE.txt
73
73
  - README.md
74
74
  - lib/macros4cuke.rb
75
- - lib/macro_steps.rb
76
75
  - lib/macros4cuke/constants.rb
77
76
  - lib/macros4cuke/exceptions.rb
78
77
  - lib/macros4cuke/macro-collection.rb
79
78
  - lib/macros4cuke/macro-step-support.rb
80
79
  - lib/macros4cuke/macro-step.rb
81
80
  - lib/macros4cuke/templating/engine.rb
81
+ - examples/demo/cucumber.yml
82
+ - examples/demo/features/basic.feature
83
+ - examples/demo/features/table.feature
82
84
  - examples/i18n/fr/cucumber.yml
85
+ - examples/demo/features/step_definitions/step_defs.rb
86
+ - examples/demo/features/step_definitions/use_macro_steps.rb
87
+ - examples/demo/features/support/env.rb
88
+ - examples/demo/features/support/macro_support.rb
83
89
  - examples/i18n/fr/features/demo01-fr.feature
84
90
  - examples/i18n/fr/features/step_definitions/demo_steps.rb
85
91
  - examples/i18n/fr/features/step_definitions/use_macro_steps.rb
@@ -89,7 +95,7 @@ files:
89
95
  - features/demo03.feature
90
96
  - features/demo04.feature
91
97
  - features/demo05.feature
92
- - features/travelling-demo.feature
98
+ - features/demo06.feature
93
99
  - features/step_definitions/demo_steps.rb
94
100
  - features/step_definitions/use_macro_steps.rb
95
101
  - features/support/env.rb
@@ -99,6 +105,7 @@ files:
99
105
  - spec/macros4cuke/macro-step-support_spec.rb
100
106
  - spec/macros4cuke/macro-step_spec.rb
101
107
  - spec/macros4cuke/templating/engine_spec.rb
108
+ - spec/macros4cuke/templating/placeholder_spec.rb
102
109
  - spec/macros4cuke/templating/section_spec.rb
103
110
  homepage: https://github.com/famished-tiger/Macros4Cuke
104
111
  licenses:
data/lib/macro_steps.rb DELETED
@@ -1,49 +0,0 @@
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
- # This step is used to define a macro-step
7
- # Example:
8
- # Given I define the step "When I [log in as <userid>]" to mean:
9
- # """
10
- # Given I landed in the homepage
11
- # When I click "Sign in"
12
- # And I fill in "Username" with "<userid>"
13
- # And I fill in "Password" with "unguessable"
14
- # And I click "Submit"
15
- # """
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, colon_capture, template|
18
- use_table = (colon_capture == ':')
19
- add_macro(macro_phrase, template, use_table)
20
- end
21
-
22
- # This step is used to invoke a simple macro-step
23
- # Example:
24
- # When I [log in as "guest"]
25
- #
26
- When(/^I \[((?:[^\\\]]|\\.)+)\]$/) do |macro_phrase|
27
- invoke_macro(macro_phrase) # This will call the macro with the given phrase
28
- end
29
-
30
-
31
- # This step is used to invoke a macro-step with a data table argument.
32
- # Example:
33
- # When I [enter my credentials as]:
34
- # |userid |guest |
35
- # |password|unguessable|
36
- When(/^I \[([^\]]+)\]:$/) do |macro_phrase, table_argument|
37
- # Ensure that the second argument is of the correct type
38
- unless table_argument.kind_of?(Cucumber::Ast::Table)
39
- raise Macros4Cuke::DataTableNotFound, "This step must have a data table as an argument."
40
- end
41
-
42
- # This will call the macro with the given phrase.
43
- # The second argument consists of an array with couples of the kind: [argument name, actual value]
44
- invoke_macro(macro_phrase, table_argument.raw)
45
- end
46
-
47
-
48
-
49
- # End of file