yet-another-cucumber-tcl 0.0.9

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.
@@ -0,0 +1,61 @@
1
+ Feature: Reset state
2
+
3
+ In order to keep state from leaking between scenarios, by default,
4
+ we create a new Tcl interpreter for each test case.
5
+
6
+ In order to prevent long setup time before each scenario
7
+ we can optionally avoid starting a new TCL interpreter
8
+
9
+ Rules:
10
+ -- The state is maintained between scenarios if an environment variable is passed into Cucumber
11
+ -- Otherwise a new TCL interpreter is started and state is reset on each scenario
12
+
13
+ Background:
14
+ Given a file named "features/test.feature" with:
15
+ """
16
+ Feature:
17
+ Scenario:
18
+ Given I set a global variable
19
+ When I print the global variable
20
+
21
+ Scenario:
22
+ When I print the global variable
23
+ """
24
+ And a file named "features/step_definitions/steps.tcl" with:
25
+ """
26
+ global g
27
+
28
+ Given {^I set a global variable$} {
29
+ set ::g value
30
+ }
31
+
32
+ When {^I print the global variable$} {
33
+ puts $::g
34
+ }
35
+ """
36
+ And a file named "features/support/env.rb" with:
37
+ """
38
+ require 'cucumber/tcl'
39
+ """
40
+
41
+ Scenario: State reset when running 'cucumber' with no options
42
+ When I run `cucumber`
43
+ Then it should fail with:
44
+ """
45
+ can't read "::g": no such variable
46
+ """
47
+
48
+ Scenario: State reset when running 'cucumber' with a new framework object for each scenario
49
+ When I run `cucumber SHARE_FRAMEWORK=0`
50
+ Then it should fail with:
51
+ """
52
+ can't read "::g": no such variable
53
+ """
54
+
55
+ Scenario: State not reset when running 'cucumber' and sharing framework object
56
+ When I run `cucumber SHARE_FRAMEWORK=1`
57
+ Then it should pass with:
58
+ """
59
+ 2 scenarios (2 passed)
60
+ 3 steps (3 passed)
61
+ """
@@ -0,0 +1 @@
1
+ require 'aruba/cucumber'
@@ -0,0 +1,25 @@
1
+ require 'cucumber/core/filter'
2
+
3
+ module Cucumber
4
+ module Tcl
5
+ ActivateSteps = Cucumber::Core::Filter.new(:create_step_definitions) do
6
+ def test_case(test_case)
7
+ activated_steps = test_case.test_steps.map do |test_step|
8
+ step_definitions.attempt_to_activate(test_step)
9
+ end
10
+ test_case.with_steps(activated_steps).describe_to receiver
11
+ reset_step_definitons
12
+ end
13
+
14
+ private
15
+
16
+ def reset_step_definitons
17
+ @step_definitions = nil
18
+ end
19
+
20
+ def step_definitions
21
+ @step_definitions ||= create_step_definitions.call
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ module Cucumber
2
+ module Tcl
3
+ # Wraps the Cucumber DataTable so that when passed through to tcl, its
4
+ # string representation is easy to parse into a tcl list.
5
+ class DataTable
6
+ def initialize(original)
7
+ @raw = original.raw
8
+ end
9
+
10
+ def to_s
11
+ to_tcl_list(@raw.map { |row| to_tcl_list(row) })
12
+ end
13
+
14
+ private
15
+
16
+ def to_tcl_list(array)
17
+ array.map { |element| "{#{element}}" }.join(' ')
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ module Cucumber
2
+ module Tcl
3
+ class Framework
4
+ TCL_FRAMEWORK_PATH = "#{File.dirname(__FILE__)}/framework.tcl".freeze
5
+
6
+ def initialize(cucumber_config = nil, path = TCL_FRAMEWORK_PATH)
7
+ @tcl = ::Tcl::Interp.load_from_file(path)
8
+
9
+ all_files_to_load = cucumber_config.nil? ? [] : cucumber_config.all_files_to_load
10
+ all_files_to_load.collect! { |f| f.gsub(/([\\\s{}])/, '\\\\\1') }
11
+ @tcl.proc('source_files').call(all_files_to_load.join(' '))
12
+ end
13
+
14
+ def step_definition_exists?(step_name)
15
+ @tcl.proc('step_definition_exists').call(step_name) == '1'
16
+ end
17
+
18
+ def execute_step_definition(*args)
19
+ @tcl.proc('execute_step_definition').call(*args)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,157 @@
1
+ namespace eval ::cucumber:: {
2
+
3
+ variable STEPS [list]
4
+ variable TEST
5
+
6
+ # Set a variable to allow this to be more easily tested
7
+ if {[info exists env(TEST)] && $::env(TEST) eq 1} {
8
+ set TEST 1
9
+ } else {
10
+ set TEST 0
11
+ }
12
+
13
+ namespace export source_files
14
+ namespace export step_definition_exists
15
+ namespace export execute_step_definition
16
+ namespace export Given
17
+ namespace export When
18
+ namespace export Then
19
+ namespace export pending
20
+ }
21
+
22
+ #
23
+ # Define procs to match Gherkin keyworkds that put data in the STEPS array
24
+ #
25
+ proc ::cucumber::Given args {
26
+ _add_step {*}$args
27
+ }
28
+
29
+ proc ::cucumber::When args {
30
+ _add_step {*}$args
31
+ }
32
+
33
+ proc ::cucumber::Then args {
34
+ _add_step {*}$args
35
+ }
36
+
37
+ proc ::cucumber::_add_step args {
38
+
39
+ variable STEPS
40
+
41
+ if {[llength $args] == 2} {
42
+ set re [lindex $args 0]
43
+ set params {}
44
+ set body [lindex $args 1]
45
+ } elseif {[llength $args] == 3} {
46
+ set re [lindex $args 0]
47
+ set params [lindex $args 1]
48
+ set body [lindex $args 2]
49
+ } else {
50
+ error "The parameters for this procedure are regular_expression ?list_of_capture_variables? body"
51
+ return 0
52
+ }
53
+
54
+ lappend STEPS [list $re $params $body]
55
+ }
56
+
57
+ #
58
+ # Procs needed by cucumber for checking and executing steps
59
+ proc ::cucumber::step_definition_exists { step_name } {
60
+ set res [_search_steps $step_name 0]
61
+ return $res
62
+ }
63
+
64
+
65
+ proc ::cucumber::execute_step_definition { step_name {multiline_args {}} } {
66
+ set res [_search_steps $step_name 1 $multiline_args]
67
+ return $res
68
+
69
+ }
70
+
71
+
72
+ proc ::cucumber::_search_steps {step_name {execute 0} {multiline_args {}}} {
73
+ variable STEPS
74
+
75
+ foreach step $STEPS {
76
+ set existing_step_name [lindex $step 0]
77
+ set existing_step_params [lindex $step 1]
78
+ set existing_step_body [lindex $step 2]
79
+
80
+ if {[regexp $existing_step_name $step_name matchresult {*}[join $existing_step_params]]} {
81
+
82
+ # Now we've found a match, handle multiline args. The name of the var
83
+ # should be the last value of the $existing_step_params.
84
+ if {$multiline_args ne {}} {
85
+ set multiline_var_name [lindex $existing_step_params end]
86
+ set $multiline_var_name $multiline_args
87
+ }
88
+
89
+ if {$execute == 1} {
90
+ set retCode [catch {
91
+ eval $existing_step_body
92
+ } msg]
93
+ if {$retCode == 1} {
94
+ # we caught an TCL_ERROR and will check for error message
95
+ if {$msg eq "skipped"} {
96
+ return "skipped"
97
+ } elseif {$msg eq "pending"} {
98
+ return "pending"
99
+ } elseif {$msg eq "undefined"} {
100
+ return "undefined"
101
+ }
102
+ error $::errorInfo
103
+ }
104
+ }
105
+ return 1
106
+ }
107
+ }
108
+ return 0
109
+ }
110
+
111
+ # Sort a list of files such that: */support/env.{ext} < */support/{file} < */{file}
112
+ proc ::cucumber::_sort_by_source_priority {a b} {
113
+
114
+ if {[string equal [lindex [file split $a] end-1] "support"]} {
115
+ if {[string equal [file rootname [lindex [file split $a] end]] "env"]} {
116
+ set a_order 1
117
+ } else {
118
+ set a_order 2
119
+ }
120
+ } else {
121
+ set a_order 3
122
+ }
123
+
124
+ if {[string equal [lindex [file split $b] end-1] "support"]} {
125
+ if {[string equal [file rootname [lindex [file split $b] end]] "env"]} {
126
+ set b_order 1
127
+ } else {
128
+ set b_order 2
129
+ }
130
+ } else {
131
+ set b_order 3
132
+ }
133
+
134
+ return [expr {$a_order - $b_order}]
135
+ }
136
+
137
+ proc ::cucumber::source_files {files} {
138
+ variable TEST
139
+
140
+ if {$TEST ne 1} {
141
+ foreach file [lsort -command _sort_by_source_priority $files] {
142
+ if {[string equal [file extension $file] ".tcl"]} {
143
+ if {[catch {
144
+ uplevel #0 [list source $file]
145
+ }]} {
146
+ error $::errorInfo
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+
153
+ proc ::cucumber::pending args {
154
+ error "pending"
155
+ }
156
+
157
+ namespace import ::cucumber::*
@@ -0,0 +1,67 @@
1
+ require 'tcl'
2
+ require_relative 'data_table'
3
+
4
+ module Cucumber
5
+ module Tcl
6
+ class StepDefinitions
7
+ def initialize(tcl_framework)
8
+ @tcl_framework = tcl_framework
9
+ end
10
+
11
+ def attempt_to_activate(test_step)
12
+ return test_step unless match?(test_step)
13
+
14
+ test_step.with_action(&action_for(test_step))
15
+ end
16
+
17
+ private
18
+
19
+ def match?(test_step)
20
+ @tcl_framework.step_definition_exists?(test_step.text)
21
+ end
22
+
23
+ def action_for(test_step)
24
+ arguments = ArgumentList.new(test_step)
25
+ proc {
26
+ response = ExecuteResponse.new(@tcl_framework.execute_step_definition(*arguments.to_a))
27
+ response.raise_any_pending_error
28
+ }
29
+ end
30
+
31
+ class ExecuteResponse
32
+ def initialize(raw)
33
+ @raw = raw
34
+ end
35
+
36
+ PENDING_ERRORS = {
37
+ 'pending' => -> { raise Cucumber::Pending, 'TODO: Step not yet implemented' },
38
+ 'skipped' => -> { raise Cucumber::Core::Test::Result::Skipped },
39
+ 'undefined' => -> { raise Cucumber::Core::Test::Result::Undefined }
40
+ }.freeze
41
+
42
+ def raise_any_pending_error
43
+ PENDING_ERRORS[@raw]&.call
44
+ end
45
+ end
46
+
47
+ class ArgumentList
48
+ def initialize(test_step)
49
+ @arguments = [test_step.text]
50
+ test_step.multiline_arg.describe_to self
51
+ end
52
+
53
+ def doc_string(arg)
54
+ @arguments << arg.content
55
+ end
56
+
57
+ def data_table(arg)
58
+ @arguments << DataTable.new(arg)
59
+ end
60
+
61
+ def to_a
62
+ @arguments
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,322 @@
1
+ package require tcltest 2
2
+ namespace import tcltest::*
3
+
4
+ source "./lib/cucumber/tcl/framework.tcl"
5
+ namespace import cucumber::*
6
+
7
+ #
8
+ # Test Given
9
+ #
10
+ test Given-1 {calling Given with 2 parameters adds a new entry to the STEPS list with an empty parameters list} {
11
+ -setup {
12
+ set ::cucumber::STEPS [list]
13
+ }
14
+ -body {
15
+ Given {^Regular Expression$} { puts "Given RegExp" }
16
+ expr { [llength $::cucumber::STEPS] == 1 && [llength [lindex $::cucumber::STEPS 0]] == 3}
17
+ }
18
+ -result 1
19
+ }
20
+
21
+ test Given-2 {calling Given twice adds 2 entries to the STEPS list} {
22
+ -setup {
23
+ set ::cucumber::STEPS [list]
24
+ }
25
+ -body {
26
+ Given {^Regular Expression$} { puts "Given RegExp" }
27
+ Given {^Regular Expression 2$} { puts "Given RegExp 2" }
28
+ expr { [llength $::cucumber::STEPS] == 2 && [llength [lindex $::cucumber::STEPS 0]] == 3}
29
+ }
30
+ -result 1
31
+ }
32
+
33
+ test Given-3 {calling Given with 3 parameters adds a new entry to the STEPS list} {
34
+ -setup {
35
+ set ::cucumber::STEPS [list]
36
+ }
37
+ -body {
38
+ Given {^Regular Expression (\d+)$} {match} { puts "Given RegExp $match" }
39
+ expr { [llength $::cucumber::STEPS] == 1 && [llength [lindex $::cucumber::STEPS 0]] == 3}
40
+ }
41
+ -result 1
42
+ }
43
+
44
+ #
45
+ # Test _add_step
46
+ #
47
+ test _add_step-1 {calling _add_step with 2 parameters adds a new entry to the STEPS list with an empty parameters list} {
48
+ -setup {
49
+ set ::cucumber::STEPS [list]
50
+ }
51
+ -body {
52
+ ::cucumber::_add_step {^Regular Expression$} { puts "Given RegExp" }
53
+ expr { [llength $::cucumber::STEPS] == 1 && [llength [lindex $::cucumber::STEPS 0]] == 3}
54
+ }
55
+ -result 1
56
+ }
57
+
58
+ test _add_step-2 {calling _add_step twice adds 2 entries to the STEPS list} {
59
+ -setup {
60
+ set ::cucumber::STEPS [list]
61
+ }
62
+ -body {
63
+ ::cucumber::_add_step {^Regular Expression$} { puts "Given RegExp" }
64
+ ::cucumber::_add_step {^Regular Expression 2$} { puts "Given RegExp 2" }
65
+ expr { [llength $::cucumber::STEPS] == 2 && [llength [lindex $::cucumber::STEPS 0]] == 3}
66
+ }
67
+ -result 1
68
+ }
69
+
70
+ test _add_step-3 {calling _add_step with 3 parameters adds a new entry to the STEPS list} {
71
+ -setup {
72
+ set ::cucumber::STEPS [list]
73
+ }
74
+ -body {
75
+ ::cucumber::_add_step {^Regular Expression (\d+)$} {match} { puts "Given RegExp $match" }
76
+ expr { [llength $::cucumber::STEPS] == 1 && [llength [lindex $::cucumber::STEPS 0]] == 3}
77
+ }
78
+ -result 1
79
+ }
80
+
81
+ test _add_step-4 {calling _add_step with more than 3 parameters adds a new entry to the STEPS list} {
82
+ -body {
83
+ ::cucumber::_add_step {^Match1 (\w+) Match2 (\d+) Match3 (\d+)$} {match1} {match2} {match3} { puts "Given RegExp $match" }
84
+ }
85
+ -returnCodes error
86
+ -result {The parameters for this procedure are regular_expression ?list_of_capture_variables? body}
87
+ }
88
+
89
+
90
+
91
+ #
92
+ # Test When
93
+ #
94
+ test When-1 {calling When adds a new entry to the STEPS list} {
95
+ -setup {
96
+ set ::cucumber::STEPS [list]
97
+ }
98
+ -body {
99
+ When {^Regular Expression$} { puts "When RegExp" }
100
+ expr { [llength $::cucumber::STEPS] == 1 }
101
+ expr { [llength $::cucumber::STEPS] == 1 && [llength [lindex $::cucumber::STEPS 0]] == 3}
102
+ }
103
+ -result 1
104
+ }
105
+
106
+
107
+
108
+ #
109
+ # Test Then
110
+ #
111
+ test Then-1 {calling Then adds a new entry to the STEPS list} {
112
+ -setup {
113
+ set ::cucumber::STEPS [list]
114
+ }
115
+ -body {
116
+ Then {^Regular Expression$} { puts "Then RegExp" }
117
+ expr { [llength $::cucumber::STEPS] == 1 && [llength [lindex $::cucumber::STEPS 0]] == 3}
118
+ }
119
+ -result 1
120
+ }
121
+
122
+
123
+
124
+ #
125
+ # Test _sort_by_source_priority procedure
126
+ #
127
+ test _sort_by_source_priority-1 {_sort_by_source_priority prioritises support/env.{ext} over other support/ files} {
128
+ -body {
129
+ set sort [::cucumber::_sort_by_source_priority "test/support/abc.ext" "test/support/env.ext"]
130
+ # only interested if sort is +ve, 0 or -ve; convert to 1, 0 or -1
131
+ expr { $sort == 0 ? 0 : $sort / abs($sort) }
132
+ }
133
+ -result 1
134
+ }
135
+
136
+ test _sort_by_source_priority-2 {_sort_by_source_priority prioritises support/env.{ext} over other support/ files} {
137
+ -body {
138
+ set sort [::cucumber::_sort_by_source_priority "test/support/env.ext" "test/support/abc.ext"]
139
+ # only interested if sort is +ve, 0 or -ve; convert to 1, 0 or -1
140
+ expr { $sort == 0 ? 0 : $sort / abs($sort) }
141
+ }
142
+ -result -1
143
+ }
144
+
145
+ test _sort_by_source_priority-3 {_sort_by_source_priority prioritises support/ files over other files} {
146
+ -body {
147
+ set sort [::cucumber::_sort_by_source_priority "test/features/abc.ext" "test/support/abc.ext"]
148
+ # only interested if sort is +ve, 0 or -ve; convert to 1, 0 or -1
149
+ expr { $sort == 0 ? 0 : $sort / abs($sort) }
150
+ }
151
+ -result 1
152
+ }
153
+
154
+ test _sort_by_source_priority-4 {_sort_by_source_priority prioritises support/ files over other files} {
155
+ -body {
156
+ set sort [::cucumber::_sort_by_source_priority "test/support/abc.ext" "test/features/abc.ext"]
157
+ # only interested if sort is +ve, 0 or -ve; convert to 1, 0 or -1
158
+ expr { $sort == 0 ? 0 : $sort / abs($sort) }
159
+ }
160
+ -result -1
161
+ }
162
+
163
+ test _sort_by_source_priority-5 {_sort_by_source_priority prioritises non support/ files equally} {
164
+ -body {
165
+ set sort [::cucumber::_sort_by_source_priority "test/lib/abc.ext" "test/etc/abc.ext"]
166
+ # only interested if sort is +ve, 0 or -ve; convert to 1, 0 or -1
167
+ expr { $sort == 0 ? 0 : $sort / abs($sort) }
168
+ }
169
+ -result 0
170
+ }
171
+
172
+
173
+
174
+ #
175
+ # Test _search_steps procedure
176
+ #
177
+ test _search_steps-1 {_search_steps returns 0 if there are no existing steps} {
178
+ -setup {
179
+ set ::cucumber::STEPS [list]
180
+ }
181
+ -body {
182
+ ::cucumber::_search_steps {Unknown Regexp}
183
+ }
184
+ -result 0
185
+ }
186
+
187
+ test _search_steps-2 {_search_steps returns 0 if there are no matching steps} {
188
+ -setup {
189
+ set ::cucumber::STEPS [list [list {^First Step$} {} {puts "First Step"}]]
190
+ }
191
+ -body {
192
+ ::cucumber::_search_steps {Unknown Regexp}
193
+ }
194
+ -result 0
195
+ }
196
+
197
+ test _search_steps-3 {_search_steps returns 1 if there is a matching step} {
198
+ -setup {
199
+ set ::cucumber::STEPS [list [list {^First Step$} {} {puts "First Step"}]]
200
+ }
201
+ -body {
202
+ ::cucumber::_search_steps {First Step}
203
+ }
204
+ -result 1
205
+ }
206
+
207
+ test _search_steps-4 {_search_steps returns 1 if there is a matching step with multiple values in STEPS} {
208
+ -setup {
209
+ set ::cucumber::STEPS [list \
210
+ [list {^First Step$} {} {puts "First Step"}] \
211
+ [list {^Second Step$} {} {puts "Second Step"}] \
212
+ ]
213
+ }
214
+ -body {
215
+ ::cucumber::_search_steps {Second Step}
216
+ }
217
+ -result 1
218
+ }
219
+
220
+ test _search_steps-5 {_search_steps returns 1 and executes body of step if there is a matching step and execute is set to 1} {
221
+ -setup {
222
+ set ::cucumber::STEPS [list \
223
+ [list {^First Step$} {} {puts "First Step"}] \
224
+ [list {^Second Step$} {} {puts "Second Step"}] \
225
+ ]
226
+ }
227
+ -body {
228
+ ::cucumber::_search_steps {Second Step} {1}
229
+ }
230
+ -result 1
231
+ -match glob
232
+ -output {Second Step*}
233
+ }
234
+
235
+ test _search_steps-6 {_search_steps returns 1 and executes body of step if there is a matching step and execute is set to 1 with a parameter in the match} {
236
+ -setup {
237
+ set ::cucumber::STEPS [list \
238
+ [list {^First Step$} {} {puts "First Step"}] \
239
+ [list {^Second (\w+)$} {match} {puts "Second $match"}] \
240
+ ]
241
+ }
242
+ -body {
243
+ ::cucumber::_search_steps {Second Step} {1}
244
+ }
245
+ -result 1
246
+ -match glob
247
+ -output {Second Step*}
248
+ }
249
+
250
+ test _search_steps-7 {_search_steps returns 1 and executes body of step if there is a matching step and execute is set to 1 with 2 parameters in the match} {
251
+ -setup {
252
+ set ::cucumber::STEPS [list \
253
+ [list {^First Step$} {} {puts "First Step"}] \
254
+ [list {^(\w+) (\w+)$} {match1 match2} {puts "$match1 $match2"}] \
255
+ ]
256
+ }
257
+ -body {
258
+ ::cucumber::_search_steps {Second Step} {1}
259
+ }
260
+ -result 1
261
+ -match glob
262
+ -output {Second Step*}
263
+ }
264
+
265
+ # Testing multiline args
266
+ test _search_steps-8 {_search_steps returns 1 and executes body of step if there is a matching step, execute is set to 1 and there is a multiline_arg passed in} {
267
+ -setup {
268
+ set ::cucumber::STEPS [list \
269
+ [list {^First Step$} {} {puts "First Step"}] \
270
+ [list {^Second Step:$} {content} {puts "$content"}] \
271
+ ]
272
+ }
273
+ -body {
274
+ ::cucumber::_search_steps {Second Step:} {1} {Multiline Content}
275
+ }
276
+ -result 1
277
+ -match glob
278
+ -output {Multiline Content*}
279
+ }
280
+
281
+ test _search_steps-9 {_search_steps returns 1 and executes body of step if there is a matching step, execute is set to 1 and there is a multiline_arg passed in and there is a parameter match in the regexp} {
282
+ -setup {
283
+ set ::cucumber::STEPS [list \
284
+ [list {^First Step$} {} {puts "First Step"}] \
285
+ [list {^Second (\w+):$} {match1 content} {puts "content=$content, match=$match1"}] \
286
+ ]
287
+ }
288
+ -body {
289
+ ::cucumber::_search_steps {Second Step:} {1} {Multiline Content}
290
+ }
291
+ -result 1
292
+ -match glob
293
+ -output {content=Multiline Content, match=Step*}
294
+ }
295
+
296
+ test _search_steps-10 {_search_steps returns "pending" if there is a matching step, execute is set to 1 and the proc to be executed is pending} {
297
+ -setup {
298
+ set ::cucumber::STEPS [list \
299
+ [list {^Pending$} {} {pending}] \
300
+ ]
301
+ }
302
+ -body {
303
+ ::cucumber::_search_steps {Pending} {1} {}
304
+ }
305
+ -result "pending"
306
+ }
307
+
308
+ #
309
+ # Test Pending
310
+ #
311
+ test pending-1 {pending returns an error with the text "pending"} {
312
+ -setup {}
313
+ -body {pending}
314
+ -returnCodes error
315
+ -result {pending}
316
+ }
317
+
318
+ #
319
+ # Cleanup
320
+ #
321
+ cleanupTests
322
+
@@ -0,0 +1,5 @@
1
+ module Cucumber
2
+ module Tcl
3
+ VERSION = '0.0.9'.freeze
4
+ end
5
+ end