cukable 0.1.1
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 +7 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +47 -0
- data/README.md +368 -0
- data/Rakefile +41 -0
- data/bin/cuke2fit +25 -0
- data/cukable.gemspec +26 -0
- data/features/conversion.feature +54 -0
- data/features/cuke_fixture.feature +147 -0
- data/features/slim_json_formatter.feature +153 -0
- data/features/step_definitions/cukable_steps.rb +115 -0
- data/features/support/env.rb +183 -0
- data/lib/cukable.rb +2 -0
- data/lib/cukable/conversion.rb +314 -0
- data/lib/cukable/cuke.rb +305 -0
- data/lib/cukable/helper.rb +221 -0
- data/lib/cukable/slim_json_formatter.rb +366 -0
- data/spec/conversion_spec.rb +275 -0
- data/spec/cuke_spec.rb +134 -0
- data/spec/helper_spec.rb +203 -0
- data/spec/spec_helper.rb +10 -0
- data/vendor/cache/rubyslim-0.1.1.gem +0 -0
- metadata +188 -0
@@ -0,0 +1,366 @@
|
|
1
|
+
# slim_json_formatter.rb
|
2
|
+
|
3
|
+
require 'cucumber/formatter/console'
|
4
|
+
require 'cucumber/formatter/io'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
|
9
|
+
module Cucumber
|
10
|
+
module Formatter
|
11
|
+
# FitNesse SliM JSON output formatter for Cucumber
|
12
|
+
class SlimJSON
|
13
|
+
include Console
|
14
|
+
include Io
|
15
|
+
include FileUtils
|
16
|
+
|
17
|
+
# Create a new SlimJSON formatter, with the provided `path_or_io` (as
|
18
|
+
# given by the `--out` option) and any additional options passed to
|
19
|
+
# cucumber.
|
20
|
+
def initialize(step_mother, path_or_io, options)
|
21
|
+
@step_mother = step_mother
|
22
|
+
|
23
|
+
# Output directory
|
24
|
+
@out_dir = path_or_io
|
25
|
+
ensure_dir(@out_dir, "FitNesse")
|
26
|
+
|
27
|
+
# There should be no IO until we get a feature, and
|
28
|
+
# create the output directory in before_feature
|
29
|
+
@io = nil
|
30
|
+
|
31
|
+
# Cache of data lines to write
|
32
|
+
@data = []
|
33
|
+
# Multi-line output (must be cached and printed after the step that
|
34
|
+
# precedes it)
|
35
|
+
@multiline = []
|
36
|
+
# Expected/actual, to support table diffs
|
37
|
+
@expected_row = []
|
38
|
+
@actual_row = []
|
39
|
+
|
40
|
+
# Not in background until we actually find one
|
41
|
+
@in_background = false
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
# Called before each `.feature` is run. Creates a new output file for the
|
46
|
+
# results in `@out_dir`, and empties `@data`.
|
47
|
+
def before_feature(feature)
|
48
|
+
file = File.join(@out_dir, "#{feature.file}.json")
|
49
|
+
dir = File.dirname(file)
|
50
|
+
mkdir_p(dir) unless File.directory?(dir)
|
51
|
+
@io = ensure_file(file, "FitNesse")
|
52
|
+
@data = []
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# Called after each `.feature` is run. Write all `@data` to the JSON
|
57
|
+
# file, then closes the output file.
|
58
|
+
def after_feature(feature)
|
59
|
+
@io.puts JSON.pretty_generate(@data)
|
60
|
+
@io.flush
|
61
|
+
@io.close
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
# Called when `Feature: <name>` is read. Generates a single row of output
|
66
|
+
# in `@data` with the feature name.
|
67
|
+
def feature_name(keyword, name)
|
68
|
+
@data << [section_message(keyword, name)]
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
# Called when `Scenario: <name>` is read. Generates a single row of
|
73
|
+
# output in `@data` with the scenario name.
|
74
|
+
def scenario_name(keyword, name, file_colon_line, source_indent)
|
75
|
+
@data << [section_message(keyword, name, file_colon_line)]
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
# Called before a `Background:` block.
|
80
|
+
def before_background(background)
|
81
|
+
@in_background = true
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
# Called when a `Background:` line is read. Generates a single row of
|
86
|
+
# output in `@data` with the `Background:` line.
|
87
|
+
def background_name(keyword, name, file_colon_line, source_indent)
|
88
|
+
@data << [section_message(keyword, name, file_colon_line)]
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# Called after a `Background:` block.
|
93
|
+
def after_background(background)
|
94
|
+
@in_background = false
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
# Start a new multiline arg (such as a table or Py-string). Initializes
|
99
|
+
# `@multiline` and related arrays.
|
100
|
+
def before_multiline_arg(multiline_arg)
|
101
|
+
@multiline = []
|
102
|
+
@expected_row = []
|
103
|
+
@actual_row = []
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
# Called before a table row is read. Starts a new `@table_row`.
|
108
|
+
def before_table_row(table_row)
|
109
|
+
@table_row = []
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
# Called when a table cell value is read. Appends to `@table_row`.
|
114
|
+
def table_cell_value(value, status)
|
115
|
+
return if @hide_this_step
|
116
|
+
stat = status_map(status)
|
117
|
+
@table_row << "#{stat}:#{value}"
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
# Called after a table row is done being read. Appends `@table_row`
|
122
|
+
# to `@multiline`, which will be output in `after_step_result`.
|
123
|
+
#
|
124
|
+
# There is some special handling here for handling table diffs;
|
125
|
+
# when doing a table diff, and a row doesn't match, two rows are
|
126
|
+
# generated. These need to be merged into a single row in the JSON
|
127
|
+
# output, to maintain the 1:1 mapping between FitNesse table and
|
128
|
+
# the returned results.
|
129
|
+
def after_table_row(table_row)
|
130
|
+
return if @hide_this_step
|
131
|
+
|
132
|
+
# If we have an @expected_row and @actual_row at this point,
|
133
|
+
# merge them into a single row and append to @multiline_arg
|
134
|
+
if !@expected_row.empty? && !@actual_row.empty?
|
135
|
+
cell_diff = []
|
136
|
+
@expected_row.zip(@actual_row) do |expect, actual|
|
137
|
+
expect.gsub!(/^ignore:/, '')
|
138
|
+
actual.gsub!(/^error:/, '')
|
139
|
+
# If we got what we wanted in this cell, consider it passed
|
140
|
+
if actual == expect
|
141
|
+
cell_diff << "pass:#{actual}"
|
142
|
+
# Otherwise, show expected vs. actual as a failure
|
143
|
+
else
|
144
|
+
cell_diff << "fail:Expected: '#{expect}'<br/>Actual: '#{actual}'"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
@multiline << ["report: "] + cell_diff
|
148
|
+
# Reset for upcoming rows
|
149
|
+
@expected_row = []
|
150
|
+
@actual_row = []
|
151
|
+
end
|
152
|
+
|
153
|
+
# Row with all cells having status == :comment (ignore)?
|
154
|
+
# This row was part of a table diff, and contains the values
|
155
|
+
# that were expected to be in the row.
|
156
|
+
if @table_row.all? { |cell| cell =~ /^ignore:/ }
|
157
|
+
@expected_row = @table_row
|
158
|
+
|
159
|
+
# Row with all cells having status == :undefined (error)?
|
160
|
+
# This row was part of a table diff, and contains the values
|
161
|
+
# that actually appeared in the row.
|
162
|
+
elsif @table_row.all? { |cell| cell =~ /^error:/ }
|
163
|
+
@actual_row = @table_row
|
164
|
+
|
165
|
+
# For any other row, append to multiline normally
|
166
|
+
else
|
167
|
+
# If an exception occurred in a table row, put the exception
|
168
|
+
# message in the first cell (which is normally empty). This
|
169
|
+
# allows us to show the exception without adding extra rows
|
170
|
+
# (which messes up the original table's formatting)
|
171
|
+
if table_row.exception
|
172
|
+
@multiline << ["fail:#{backtrace(table_row.exception)}"] + @table_row
|
173
|
+
# Otherwise, output an empty report: cell in the first column
|
174
|
+
else
|
175
|
+
@multiline << ["report: "] + @table_row
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
# Called before an `Examples:` section in a Scenario Outline. An
|
183
|
+
# `Examples:` table works similarly to a multiline argument, except there
|
184
|
+
# is no associated step to output them. The `after_table_row` method
|
185
|
+
# will still accumulate the table rows, but we need to rely on
|
186
|
+
# `after_examples` to output them. Thus, we will be accumulating these
|
187
|
+
# rows in the multi-purpose `@multiline` variable, initialized here.
|
188
|
+
def before_examples(examples)
|
189
|
+
@multiline = []
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
# Called when the `Examples:` line is read. Outputs the `Examples:` line
|
194
|
+
# to `@data`.
|
195
|
+
def examples_name(keyword, name)
|
196
|
+
@data << ["report:#{keyword}: #{name}"]
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
# Called after an `Examples:` section. Outputs anything accumulated in
|
201
|
+
# `@multiline`, and empties it.
|
202
|
+
def after_examples(examples)
|
203
|
+
# Output any multiline args that followed this step
|
204
|
+
@multiline.each do |row|
|
205
|
+
@data << row
|
206
|
+
end
|
207
|
+
@multiline = []
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
# Called *after* a step has been executed, but *before* any output from
|
212
|
+
# that step has been done.
|
213
|
+
def before_step(step)
|
214
|
+
@current_step = step
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
# Called when a multi-line string argument is read. Generates a row of
|
219
|
+
# output for each line in the multi-line string (including the `"""`
|
220
|
+
# opening and closing lines), colored based on the status of the current
|
221
|
+
# step. The output is accumulated in `@multiline`, for output in
|
222
|
+
# `after_step_result`.
|
223
|
+
def py_string(string)
|
224
|
+
return if @hide_this_step
|
225
|
+
status = status_map(@current_step.status)
|
226
|
+
@multiline << [status + ':"""']
|
227
|
+
string.split("\n").each do |line|
|
228
|
+
@multiline << ["#{status}:#{line}"]
|
229
|
+
end
|
230
|
+
@multiline << [status + ':"""']
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
# Called when a tag name is found. Generates a single row of output in
|
235
|
+
# `@data` with the tag name. (Note that this will only work properly if
|
236
|
+
# there is only one tag per line; otherwise, too many lines may be
|
237
|
+
# output.)
|
238
|
+
def tag_name(tag_name)
|
239
|
+
@data << ["ignore:#{tag_name}"]
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
# Called before any output from a step result. To avoid redundant output,
|
244
|
+
# we want to show the results of `Background` steps only once, within the
|
245
|
+
# `Background` section (unless a background step somehow failed when it
|
246
|
+
# was executed at the top of a Scenario). Here, `background` is true if
|
247
|
+
# the step was defined in the `Background` section, and `@in_background`
|
248
|
+
# is true if we are actually inside the `Background` section during
|
249
|
+
# execution. In short, if a step was defined in the `Background` section,
|
250
|
+
# but we are *not* within the `Background` section now, we want to hide
|
251
|
+
# the step's output.
|
252
|
+
def before_step_result(keyword, step_match, multiline_arg, status,
|
253
|
+
exception, source_indent, background)
|
254
|
+
if status != :failed && @in_background ^ background
|
255
|
+
@hide_this_step = true
|
256
|
+
else
|
257
|
+
@hide_this_step = false
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
|
262
|
+
# Called after a step has executed, and we have a result. Generates a
|
263
|
+
# single row of output in `@data`, including the status of the completed
|
264
|
+
# step, along with one row for each line accumulated in a multi-line
|
265
|
+
# argument (`@multiline`) if any were provided. Resets `@multiline` when
|
266
|
+
# done.
|
267
|
+
def after_step_result(keyword, step_match, multiline_arg, status,
|
268
|
+
exception, source_indent, background)
|
269
|
+
return if @hide_this_step
|
270
|
+
# One-line message to print
|
271
|
+
message = ''
|
272
|
+
# A bit of a hack here to support Scenario Outlines
|
273
|
+
# Apparently, at the time of calling after_step_result, a StepMatch in
|
274
|
+
# a Scenario Outline has a `name` attribute (and no arguments to
|
275
|
+
# format, because they don't get pattern-matched until the Examples:
|
276
|
+
# section that fills them in), whereas a StepMatch in a normal scenario
|
277
|
+
# has no value set for its `name` attribute, and *does* have arguments
|
278
|
+
# to format. This behavior is exploited here for the sake of replacing
|
279
|
+
# the `<...>` angle brackets that appear in Scenario Outline steps.
|
280
|
+
#
|
281
|
+
# In other words, if `step_match` has a `name`, assume it's in a
|
282
|
+
# Scenario Outline, and replace the angle brackets (so the bracketed
|
283
|
+
# parameter can be displayed in an HTML page)
|
284
|
+
if step_match.name
|
285
|
+
step_name = keyword + step_match.name.gsub('<', '<').gsub('>', '>')
|
286
|
+
# Otherwise, wrap the arguments in bold tags
|
287
|
+
else
|
288
|
+
step_name = keyword + step_match.format_args("<b>%s</b>")
|
289
|
+
end
|
290
|
+
|
291
|
+
# Output the step name with appropriate colorization
|
292
|
+
stat = status_map(status)
|
293
|
+
message = "#{stat}:#{step_name}"
|
294
|
+
|
295
|
+
# Add the source file and line number where this step was defined
|
296
|
+
message += source_message(step_match.file_colon_line)
|
297
|
+
|
298
|
+
# Include additional info for undefined and failed steps
|
299
|
+
if status == :undefined
|
300
|
+
message += "<br/>(Undefined Step)"
|
301
|
+
elsif status == :failed && exception
|
302
|
+
message += backtrace(exception)
|
303
|
+
end
|
304
|
+
|
305
|
+
# Output the final message for this step
|
306
|
+
@data << [message]
|
307
|
+
|
308
|
+
# Output any multiline args that followed this step
|
309
|
+
@multiline.each do |row|
|
310
|
+
@data << row
|
311
|
+
end
|
312
|
+
@multiline = []
|
313
|
+
end
|
314
|
+
|
315
|
+
|
316
|
+
# ------------------------
|
317
|
+
# Utility methods
|
318
|
+
# (not called by Cucumber)
|
319
|
+
# ------------------------
|
320
|
+
|
321
|
+
# Map Cucumber status strings to FitNesse status strings
|
322
|
+
def status_map(status)
|
323
|
+
case status
|
324
|
+
when nil then 'pass'
|
325
|
+
when :passed then 'pass'
|
326
|
+
when :failed then 'fail'
|
327
|
+
when :undefined then 'error'
|
328
|
+
when :skipped then 'ignore'
|
329
|
+
when :comment then 'ignore'
|
330
|
+
else 'pass'
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
|
335
|
+
# Return `text` with any HTML-specific characters sanitized
|
336
|
+
def sanitize(text)
|
337
|
+
text.gsub('<', '<').gsub('>', '>')
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
# Return a string for outputting the source filename and line number
|
342
|
+
def source_message(file_colon_line)
|
343
|
+
return " <span class=\"source_file\">" + file_colon_line + '</span>'
|
344
|
+
end
|
345
|
+
|
346
|
+
|
347
|
+
# Return a string suitable for use as a section heading for "Feature:",
|
348
|
+
# "Scenario:" or "Scenario Outline:" output rows.
|
349
|
+
def section_message(keyword, name, file_colon_line='')
|
350
|
+
"report:#{keyword}: #{name}" + source_message(file_colon_line)
|
351
|
+
end
|
352
|
+
|
353
|
+
|
354
|
+
# Return an exception message and backtrace
|
355
|
+
def backtrace(exception)
|
356
|
+
message = "<br/>" + sanitize(exception.message) + "<br/>"
|
357
|
+
message += exception.backtrace.collect { |line|
|
358
|
+
sanitize(line)
|
359
|
+
}.join("<br/>")
|
360
|
+
return message
|
361
|
+
end
|
362
|
+
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
@@ -0,0 +1,275 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Cukable::Conversion do
|
4
|
+
|
5
|
+
context "#feature_to_fitnesse" do
|
6
|
+
it "adds table markup" do
|
7
|
+
feature = [
|
8
|
+
'Feature: User account',
|
9
|
+
' Scenario: Login',
|
10
|
+
' When I am on the login page',
|
11
|
+
' And I fill in "Username" with "Eric"',
|
12
|
+
' And I fill in "Password" with "foobar"',
|
13
|
+
]
|
14
|
+
fitnesse = [
|
15
|
+
'!| Table: Cuke |',
|
16
|
+
'| Feature: User account |',
|
17
|
+
'| Scenario: Login |',
|
18
|
+
'| When I am on the login page |',
|
19
|
+
'| And I fill in "Username" with "Eric" |',
|
20
|
+
'| And I fill in "Password" with "foobar" |',
|
21
|
+
]
|
22
|
+
feature_to_fitnesse(feature).should == fitnesse
|
23
|
+
end
|
24
|
+
|
25
|
+
it "includes unparsed text" do
|
26
|
+
feature = [
|
27
|
+
'Feature: User account',
|
28
|
+
'',
|
29
|
+
' As a user with an account',
|
30
|
+
' I want to login to the website',
|
31
|
+
'',
|
32
|
+
' Scenario: Login',
|
33
|
+
' When I am on the login page',
|
34
|
+
]
|
35
|
+
fitnesse = [
|
36
|
+
'As a user with an account',
|
37
|
+
'I want to login to the website',
|
38
|
+
'',
|
39
|
+
'!| Table: Cuke |',
|
40
|
+
'| Feature: User account |',
|
41
|
+
'| Scenario: Login |',
|
42
|
+
'| When I am on the login page |',
|
43
|
+
]
|
44
|
+
feature_to_fitnesse(feature).should == fitnesse
|
45
|
+
end
|
46
|
+
|
47
|
+
it "correctly marks up table rows" do
|
48
|
+
feature = [
|
49
|
+
'Feature: User account',
|
50
|
+
'',
|
51
|
+
' Background:',
|
52
|
+
' Given a user exists:',
|
53
|
+
' | Username | Password |',
|
54
|
+
' | Eric | foobar |',
|
55
|
+
' | Ken | barfoo |',
|
56
|
+
'',
|
57
|
+
' Scenario: Login',
|
58
|
+
' When I am on the login page',
|
59
|
+
]
|
60
|
+
fitnesse = [
|
61
|
+
'!| Table: Cuke |',
|
62
|
+
'| Feature: User account |',
|
63
|
+
'| Background: |',
|
64
|
+
'| Given a user exists: |',
|
65
|
+
'| | Username | Password |',
|
66
|
+
'| | Eric | foobar |',
|
67
|
+
'| | Ken | barfoo |',
|
68
|
+
'| Scenario: Login |',
|
69
|
+
'| When I am on the login page |',
|
70
|
+
]
|
71
|
+
feature_to_fitnesse(feature).should == fitnesse
|
72
|
+
end
|
73
|
+
|
74
|
+
it "correctly marks up scenario outlines with examples" do
|
75
|
+
feature = [
|
76
|
+
'Feature: Scenario outlines',
|
77
|
+
'',
|
78
|
+
' Scenario: Different pages',
|
79
|
+
' When I am on the <page> page',
|
80
|
+
' Then I should see "<text>"',
|
81
|
+
'',
|
82
|
+
' Examples:',
|
83
|
+
' | page | text |',
|
84
|
+
' | home | Relax |',
|
85
|
+
' | office | Get to work |',
|
86
|
+
]
|
87
|
+
fitnesse = [
|
88
|
+
'!| Table: Cuke |',
|
89
|
+
'| Feature: Scenario outlines |',
|
90
|
+
'| Scenario: Different pages |',
|
91
|
+
'| When I am on the <page> page |',
|
92
|
+
'| Then I should see "<text>" |',
|
93
|
+
'| Examples: |',
|
94
|
+
'| | page | text |',
|
95
|
+
'| | home | Relax |',
|
96
|
+
'| | office | Get to work |',
|
97
|
+
]
|
98
|
+
feature_to_fitnesse(feature).should == fitnesse
|
99
|
+
end
|
100
|
+
|
101
|
+
it "correctly includes multi-line strings" do
|
102
|
+
feature = [
|
103
|
+
'Feature: Strings',
|
104
|
+
'',
|
105
|
+
' Scenario: Multi-line string',
|
106
|
+
' Given a multi-line string:',
|
107
|
+
' """',
|
108
|
+
' Hello world',
|
109
|
+
' Goodbye world',
|
110
|
+
' """',
|
111
|
+
]
|
112
|
+
fitnesse = [
|
113
|
+
'!| Table: Cuke |',
|
114
|
+
'| Feature: Strings |',
|
115
|
+
'| Scenario: Multi-line string |',
|
116
|
+
'| Given a multi-line string: |',
|
117
|
+
'| """ |',
|
118
|
+
'| Hello world |',
|
119
|
+
'| Goodbye world |',
|
120
|
+
'| """ |',
|
121
|
+
]
|
122
|
+
feature_to_fitnesse(feature).should == fitnesse
|
123
|
+
end
|
124
|
+
|
125
|
+
it "outputs @tags on separate lines" do
|
126
|
+
feature = [
|
127
|
+
'@tag_a @tag_b',
|
128
|
+
'Feature: Tags',
|
129
|
+
'',
|
130
|
+
' @tag_c @tag_d @tag_e',
|
131
|
+
' Scenario: Tags',
|
132
|
+
' Given a scenario',
|
133
|
+
]
|
134
|
+
fitnesse = [
|
135
|
+
'!| Table: Cuke |',
|
136
|
+
'| @tag_a |',
|
137
|
+
'| @tag_b |',
|
138
|
+
'| Feature: Tags |',
|
139
|
+
'| @tag_c |',
|
140
|
+
'| @tag_d |',
|
141
|
+
'| @tag_e |',
|
142
|
+
'| Scenario: Tags |',
|
143
|
+
'| Given a scenario |',
|
144
|
+
]
|
145
|
+
feature_to_fitnesse(feature).should == fitnesse
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
context "#fitnesse_to_features" do
|
151
|
+
it "returns one table for each feature" do
|
152
|
+
fitnesse = [
|
153
|
+
'!| Table: Cuke |',
|
154
|
+
'| Feature: First |',
|
155
|
+
'| Scenario: Scenario 1A |',
|
156
|
+
'| Given a scenario |',
|
157
|
+
'| Scenario: Scenario 1B |',
|
158
|
+
'| Given a scenario |',
|
159
|
+
'',
|
160
|
+
'!| Table: Cuke |',
|
161
|
+
'| Feature: Second |',
|
162
|
+
'| Scenario: Scenario 2A |',
|
163
|
+
'| Given a scenario |',
|
164
|
+
'| Scenario: Scenario 2B |',
|
165
|
+
'| Given a scenario |',
|
166
|
+
]
|
167
|
+
features = [
|
168
|
+
[
|
169
|
+
['Feature: First'],
|
170
|
+
['Scenario: Scenario 1A'],
|
171
|
+
['Given a scenario'],
|
172
|
+
['Scenario: Scenario 1B'],
|
173
|
+
['Given a scenario'],
|
174
|
+
],
|
175
|
+
[
|
176
|
+
['Feature: Second'],
|
177
|
+
['Scenario: Scenario 2A'],
|
178
|
+
['Given a scenario'],
|
179
|
+
['Scenario: Scenario 2B'],
|
180
|
+
['Given a scenario'],
|
181
|
+
],
|
182
|
+
]
|
183
|
+
fitnesse_to_features(fitnesse).should == features
|
184
|
+
end
|
185
|
+
|
186
|
+
it "correctly interprets tables" do
|
187
|
+
fitnesse = [
|
188
|
+
'!| Table: Cuke |',
|
189
|
+
'| Feature: Tables |',
|
190
|
+
'| Scenario: Table rows |',
|
191
|
+
'| Given a table: |',
|
192
|
+
'| | First | Last |',
|
193
|
+
'| | Eric | Pierce |',
|
194
|
+
'| | Ken | Brazier |',
|
195
|
+
]
|
196
|
+
features = [
|
197
|
+
[
|
198
|
+
['Feature: Tables'],
|
199
|
+
['Scenario: Table rows'],
|
200
|
+
['Given a table:'],
|
201
|
+
['', 'First', 'Last'],
|
202
|
+
['', 'Eric', 'Pierce'],
|
203
|
+
['', 'Ken', 'Brazier'],
|
204
|
+
],
|
205
|
+
]
|
206
|
+
fitnesse_to_features(fitnesse).should == features
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
it "correctly escapes brackets in Scenario Outlines" do
|
211
|
+
fitnesse = [
|
212
|
+
'!| Table: Cuke |',
|
213
|
+
'| Feature: Scenario Outlines |',
|
214
|
+
'| Scenario Outline: With tables |',
|
215
|
+
'| Given a user: |',
|
216
|
+
'| | First | Last |',
|
217
|
+
'| | <first> | <last> |',
|
218
|
+
'| Examples: |',
|
219
|
+
'| | first | last |',
|
220
|
+
'| | Eric | Pierce |',
|
221
|
+
'| | Ken | Brazier |',
|
222
|
+
]
|
223
|
+
features = [
|
224
|
+
[
|
225
|
+
['Feature: Scenario Outlines'],
|
226
|
+
['Scenario Outline: With tables'],
|
227
|
+
['Given a user:'],
|
228
|
+
['', 'First', 'Last'],
|
229
|
+
['', '<first>','<last>'],
|
230
|
+
['Examples:'],
|
231
|
+
['', 'first', 'last'],
|
232
|
+
['', 'Eric', 'Pierce'],
|
233
|
+
['', 'Ken', 'Brazier'],
|
234
|
+
],
|
235
|
+
]
|
236
|
+
fitnesse_to_features(fitnesse).should == features
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
it "ignores non-table lines" do
|
241
|
+
fitnesse = [
|
242
|
+
'This text should be ignored',
|
243
|
+
'!| Table: Cuke |',
|
244
|
+
'| Feature: Extra text |',
|
245
|
+
'| Scenario: Ignore extra text|',
|
246
|
+
'| Given a scenario |',
|
247
|
+
'This line should also be ignored',
|
248
|
+
'And this one too',
|
249
|
+
]
|
250
|
+
features = [
|
251
|
+
[
|
252
|
+
['Feature: Extra text'],
|
253
|
+
['Scenario: Ignore extra text'],
|
254
|
+
['Given a scenario'],
|
255
|
+
],
|
256
|
+
]
|
257
|
+
fitnesse_to_features(fitnesse).should == features
|
258
|
+
end
|
259
|
+
|
260
|
+
it "returns an empty list if no Cuke tables are present" do
|
261
|
+
fitnesse = [
|
262
|
+
'This wiki page has no Cuke tables',
|
263
|
+
'So there are no tables to parse',
|
264
|
+
'| there is | this table |',
|
265
|
+
'| but it is not | a Cuke table |',
|
266
|
+
'| so it will also | be ignored |',
|
267
|
+
]
|
268
|
+
features = []
|
269
|
+
fitnesse_to_features(fitnesse).should == features
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
end
|
274
|
+
|
275
|
+
|