cuke_writer 0.0.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/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ # Aruba artifacts
2
+ tmp
3
+
4
+ # Editors and tools and such
5
+ .idea
6
+ .redcar
7
+ .project
8
+ *~
9
+ *.swp
10
+
11
+ # Testing settings
12
+ cucumber.yml
13
+ .rspec
14
+
15
+ # Added by bundler for the gem
16
+ *.gem
17
+ .bundle
18
+ Gemfile.lock
19
+ pkg/*
20
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cuke_writer.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,222 @@
1
+ CukeWriter
2
+ ==========
3
+
4
+ [![Build Status](http://Travis-ci.org/joelhelbling/cuke_writer.png)](http://Travis-ci.org/joelhelbling/cuke_writer)
5
+
6
+
7
+ What is it?
8
+ -----------
9
+
10
+ CukeWriter is a custom Cucumber formatter which collects steps and generates
11
+ serialized sets of Cucumber features.
12
+
13
+ To use it:
14
+
15
+ cucumber --format CukeWriter::Formatter features
16
+
17
+ CukeWriter basically creates features and scenarios which correspond directly
18
+ to the features and scenarios that generated them. You'll need to instantiate
19
+ a step collector:
20
+
21
+ @step_collector = StepCollector.new
22
+
23
+ Now suppose we have a feature called features/awesome.feature:
24
+
25
+ Feature: Let's write some cukes
26
+
27
+ Scenario: Just one step, but boy, what a step
28
+ Given I put all my eggs in one basket
29
+
30
+ Then, in any of your step definitions, you do summa this:
31
+
32
+ Given /^I put all my eggs in one basket$/ do
33
+ @step_collector.add "Given I have hadoop report \"R#{SerialNumber}.html.gz\""
34
+ @step_collector.add "When I uncompress the hadoop report"
35
+ @step_collector.add "Then I should see \"All files processed\""
36
+ end
37
+
38
+ Each collected step is added to a scenario/feature which has the same name as the scenario
39
+ which had the step which collected that step. So in this example, CukeWriter would create
40
+ a feature called features/generated_features/20101122131415/awesome.cw.feature (where the
41
+ serial number for this run was 20101122131415):
42
+
43
+ Feature: Let's write some cukes
44
+ [generated from features/awesome.feature]
45
+
46
+ Scenario: Just one step, but boy, what a step
47
+ [from features/awesome.feature:3]
48
+ Given I have hadoop report "R20101122131415.html.gz"
49
+ When I uncompress the hadoop report
50
+ Then I should see "All files processed"
51
+
52
+ If a scenario did not have any collected steps, it will not be added to its corresponding
53
+ generated feature. If a feature did not have any generated scenarios, it will not generate
54
+ a CukeWriter feature.
55
+
56
+ _This just in! If your *generating* feature has a Background section which has steps which
57
+ generate steps, then the *generated* feature will also have a Background section which
58
+ contains those generated background steps._
59
+
60
+ Why Would You Want This?
61
+ ------------------------
62
+
63
+ Suppose you are using Cucumber to test an asynchronous system which generates artifacts which won't
64
+ be available for some time after your Cucumber tests run. CukeWriter allows you to generate a
65
+ serial-number-tagged set of Cucumber features which can be run at some later time.
66
+
67
+ For example, suppose you wish to test an email messaging system which uses a third-party email
68
+ service provider which takes its sweet ole time getting those messages into an actual email inbox.
69
+ Each time you run your main features, you send a new batch of emails out. You could tag each email
70
+ with the serial number of that particular run, so that when you run the generated features, they
71
+ will test the specific set of emails which were generated in the corresponding execution of the
72
+ main features.
73
+
74
+ Wait, What About Scenario Outlines?
75
+ -----------------------------
76
+
77
+ So glad you asked. When CukeWriter encounters a scenario outline/example table in your
78
+ feature, it generates a separate regular ole Scenario:™ for each row in your examples
79
+ table, and references the line number of the corresponding example table row.
80
+
81
+ Early on, I figured I'd have CukeWriter just output a corresponding Scenario Outline:™,
82
+ but as I thought about it, I realized that to assume the steps you generate from one example
83
+ row could very well be different from the steps generated from another example row. For
84
+ example:
85
+
86
+ Feature: As a music fan,
87
+ In order to make the most of a rock star spotting,
88
+ I want to adapt to each rock star differently
89
+ So that I can get an autograph
90
+
91
+ Scenario Outline: This is it
92
+ Given I see "<rock_star>"
93
+
94
+ Examples:
95
+ | rock_star |
96
+ | Ringo |
97
+ | Sting |
98
+ | Ted Nugent |
99
+
100
+ And suppose you wanted this to generate different steps for different stars? Here's an
101
+ example of what we _can_ do:
102
+
103
+ Feature: As a music fan,
104
+ In order to make the most of a rock star spotting,
105
+ I want to adapt to each rock star differently
106
+ So that I can get an autograph
107
+ [generated from features/rock_star_recognition.feature]
108
+
109
+ Scenario: This is it
110
+ [from features/rock_star_recognition.feature:8]
111
+ Given "Ringo" sees me
112
+ Then I say "Cheers!"
113
+
114
+ Scenario: This is it
115
+ [from features/rock_star_recognition.feature:9]
116
+ Given Sting is in an elevator
117
+ When I say "De do do do, de da da da!"
118
+ Then Sting says "I just remembered, I've got some business on 85."
119
+
120
+ Scenario: This is it
121
+ [from features/rock_star_recognition.feature:10]
122
+ Given Ted Nugent sees me
123
+ Then I run the other way
124
+
125
+ All this unique handling of each brush with greatness becomes impossible if we just generate
126
+ a single scenario outline.
127
+
128
+ Step Tables
129
+ -----------
130
+
131
+ Sometimes you just want to have CukeWriter generate a step which has a table attached to it. And
132
+ that's ok. `StepCollector#add` now accepts a hash as an optional second parameter, and in that
133
+ hash you can send over a `:table` (in the form of a `Cucumber::Ast::Table`). Behold:
134
+
135
+ Given I have a step with the following table:
136
+ | THAT | THOSE |
137
+ | thing | things |
138
+ | there | overthere |
139
+
140
+ ...and supposing your step defintion did this:
141
+
142
+ Given /^I have a step with the following table:$/ |table|
143
+ @step_collector.add "Then this table goes here:", {:table => table}
144
+ end
145
+
146
+ ...then your generated cuke would have this step (and attendant table):
147
+
148
+ Then this table goes here:
149
+ | THAT | THOSE |
150
+ | thing | things |
151
+ | there | overthere |
152
+
153
+ Of course, you don't have to just pass a table straight through. You can certainly just generate
154
+ your own table, e.g.
155
+
156
+ table = Cucumber::Ast::Table.new [['GOO', 'GAH'], ['boo', 'bah']]
157
+
158
+ Now that you can generate step tables, why, go forth, and multiply!
159
+
160
+ Keep 'Em Seperated
161
+ ------------------
162
+
163
+ If you're using CukeWriter to solve some kind of temporal distortion problem (a.k.a. an asynchronous
164
+ problem) then chances good that when you want to run the features that use CukeWriter to write
165
+ features, you _don't_ want to run the features that CukeWriter has generated...and vice versa.
166
+ Accordingly, CukeWriter puts a "@cuke_writer" tag on each feature that it generates. This makes it
167
+ easy to tell cucumber which kind of Cuke is in season now. When it's time to plant nice neat rows
168
+ of burpless seedlings, you can make Cucumber skip any CukeWriter-generated cukes with this:
169
+
170
+ cucumber --tags ~@cuke_writer
171
+
172
+ And when it's time to turn our attention to the nice new crop of CukeWriter-written cukes, you can
173
+ load 'em up for the county fair with this:
174
+
175
+ cucumber --tags @cuke_writer
176
+
177
+ Thus CukeWriter uses Cucumber tags to heal rifts in the time-space continuum, which is pretty
178
+ freakin' sweet.
179
+
180
+ Suggestion: you might be able to simplify your personal journey by putting all this `--tags ~@cuke_writer`
181
+ and `--tags @cuke_writer` stuff into your cucumber.yml. For instance:
182
+
183
+ default: --tags ~@cuke_writer # avoid running CW-generated cukes
184
+ cuke_writer: --tags @cuke_writer # run *only* CW-generated cukes
185
+
186
+ This seems like a very helpful suggestion if I do say so myself. Someday I will make CukeWriter
187
+ glance at your cucumber.yml and suggest this very same suggestion which I have suggested. Unless, of
188
+ course, you have already followed this suggestion, in which case CukeWriter will wink at you and
189
+ smile knowingly, like this: ;)
190
+
191
+ What CukeWriter Won't Do
192
+ ------------------------
193
+
194
+ _I would do anything for love, but I won't do that. --Meatloaf_
195
+
196
+ CukeWriter will not kick off the generated batch of cucumber tests automatically. This is because
197
+ the mechanism for doing that will likely be different depending on the requirements of your project
198
+ and operating environment in which CukeWriter is used: it could be triggered by cron, your CI server,
199
+ or a Windows scheduled task. It could be scheduled for five minutes from now, or fifty years from
200
+ now. Or it could be run manually whenever you darn well please. So let's leave the "when" and
201
+ "how" details in your hands.
202
+
203
+ It also won't generate the step definitions you'll need for running your generated features. You'll
204
+ want to write those yourself since they're so important!
205
+
206
+ What It _Will_ Do Soon
207
+ --------------------
208
+
209
+ Here's a nice, shallow backlog:
210
+
211
+ * _Output "Background" section (only if background steps generate steps)_ ...[DONE]
212
+ * _Handle scenario outlines_ ...[DONE]
213
+ * _Handle step tables_ ...[DONE]
214
+ * _Need a test which actually runs generated features to ensure they are kopasetic._ ...[DONE]
215
+ * _Need a special tag (e.g. @cuke_writer) which is added to each generated feature. This will allow
216
+ us to (re)run the main features while omitting the generated features and vice versa._ ...[DONE]
217
+ * Have CW glance at cucumber.yml and suggest a couple of changes for working with/around
218
+ the @cw tag (unluss cucumber.yml already does so).
219
+ * Generate a nice new rake task for each serialized batch of generated features.
220
+ * Create a nice rake task which cleans up old batches of generated features.
221
+ * Turn CukeWriter into a gem.
222
+
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'cucumber/rake/task'
2
+ require 'rspec/core/rake_task'
3
+ require 'bundler'
4
+
5
+ task :default => [:rspec_tests, :cucumber_tests]
6
+
7
+ Cucumber::Rake::Task.new(:cucumber_tests) do |t|
8
+ t.cucumber_opts = ['--no-source']
9
+ end
10
+
11
+ RSpec::Core::RakeTask.new(:rspec_tests) do |t|
12
+ t.rspec_opts = ['--format', 'nested']
13
+ end
14
+
15
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "cuke_writer/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "cuke_writer"
7
+ s.version = CukeWriter::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Joel Helbling"]
10
+ s.email = ["joel@joelhelbling.com"]
11
+ s.homepage = "http://github.com/joelhelbling/cuke_writer"
12
+ s.summary = %q{A Cucumber formatter which writes Cucumber features.}
13
+ s.description = %q{A custom Cucumber formatter which collects steps and generates serialized sets of Cucumber features. Meta FTW!}
14
+
15
+ s.rubyforge_project = "cuke_writer"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency 'cucumber', '>= 0.10.0'
23
+
24
+ s.add_development_dependency 'rake', '>= 0.9.2'
25
+ s.add_development_dependency 'rspec', '>= 2.6.0'
26
+ s.add_development_dependency 'rspec-core', '>= 2.6.4'
27
+ s.add_development_dependency 'aruba', '>= 0.3.0'
28
+ end
@@ -0,0 +1,61 @@
1
+ @announce
2
+ Feature: As a tester, I want to generate steps from steps which are part of a background.
3
+
4
+ Background:
5
+ Given a typical support/env.rb file
6
+ And a file named "features/step_definitions/steps.rb" with:
7
+ """
8
+ Given /^I don't do much$/ do
9
+ :do_relatively_nothing
10
+ end
11
+ Given /^I add a step "([^"]*)"$/ do |step_tag|
12
+ @step_collector ||= StepCollector.new
13
+ @step_collector.add "Given I do \"#{step_tag}\""
14
+ end
15
+ """
16
+
17
+ Scenario: A feature with background doesn't generate a feature with a background
18
+ if background doesn't add any steps.
19
+ Given a file named "features/generate_sans_background.feature" with:
20
+ """
21
+ Feature:
22
+
23
+ Background: This background doesn't generate steps
24
+ Given I don't do much
25
+
26
+ Scenario: But the scenario does generate steps
27
+ Given I add a step "foo"
28
+ """
29
+ When I run the "generate_sans_background" feature
30
+ Then the file "features/generated_features/P123456/generate_sans_background.cw.feature" should contain "Feature:"
31
+ And the file "features/generated_features/P123456/generate_sans_background.cw.feature" should contain "Scenario:"
32
+ But the file "features/generated_features/P123456/generate_sans_background.cw.feature" should not contain "Background:"
33
+
34
+ Scenario: A feature with background does generate a feature with background
35
+ if background does add steps
36
+ Given a file named "features/generate_sans_background.feature" with:
37
+ """
38
+ Feature:
39
+
40
+ Background: This background does generate steps
41
+ Given I add a step "bar"
42
+
43
+ Scenario: And the scenario also generates steps
44
+ Given I add a step "foo"
45
+ """
46
+ When I run the "generate_sans_background" feature
47
+ Then the file "features/generated_features/P123456/generate_sans_background.cw.feature" should contain exactly:
48
+ """
49
+ @cuke_writer
50
+ Feature:
51
+ [generated from features/generate_sans_background.feature]
52
+
53
+ Background: This background does generate steps
54
+ [from features/generate_sans_background.feature:3]
55
+ Given I do "bar"
56
+
57
+ Scenario: And the scenario also generates steps
58
+ [from features/generate_sans_background.feature:6]
59
+ Given I do "foo"
60
+
61
+ """
@@ -0,0 +1,98 @@
1
+ @announce
2
+ Feature: features with simple, straightforward scenarios
3
+
4
+ Background:
5
+ Given a typical support/env.rb file
6
+ And a file named "features/step_definitions/steps.rb" with:
7
+ """
8
+ Given /^I do this$/ do
9
+ @step_collector ||= StepCollector.new
10
+ @step_collector.add "Given I do that"
11
+ end
12
+ """
13
+
14
+ Scenario: A basic single-scenario feature with indentation
15
+ Given a file named "features/cuke_writer_test.feature" with:
16
+ """
17
+ Feature: Testing out CukeWriter
18
+
19
+ Scenario: A really basic scenario
20
+ Given I do this
21
+ """
22
+ When I run the "cuke_writer_test" feature
23
+ Then it should pass with:
24
+ """
25
+ 1 scenario (1 passed)
26
+ 1 step (1 passed)
27
+ """
28
+ And the following directories should exist:
29
+ | features/generated_features/P123456 |
30
+ And the following files should exist:
31
+ | features/generated_features/P123456/cuke_writer_test.cw.feature |
32
+ And the file "features/generated_features/P123456/cuke_writer_test.cw.feature" should contain exactly:
33
+ """
34
+ @cuke_writer
35
+ Feature: Testing out CukeWriter
36
+ [generated from features/cuke_writer_test.feature]
37
+
38
+ Scenario: A really basic scenario
39
+ [from features/cuke_writer_test.feature:3]
40
+ Given I do that
41
+
42
+ """
43
+
44
+ Scenario: When no steps are added, don't write a scenario
45
+ Given a file named "features/suppress_empty_scenarios.feature" with:
46
+ """
47
+ Feature: Testing out CukeWriter
48
+
49
+ Scenario: that doesn't write a step
50
+ Given I don't do much
51
+
52
+ Scenario: that does write a step
53
+ Given I do this
54
+ """
55
+ And I append to "features/step_definitions/steps.rb" with:
56
+ """
57
+
58
+ Given /^I don't do much$/ do
59
+ :do_relatively_nothing
60
+ end
61
+
62
+ """
63
+ When I run the "suppress_empty_scenarios" feature
64
+ Then the file "features/generated_features/P123456/suppress_empty_scenarios.cw.feature" should contain exactly:
65
+ """
66
+ @cuke_writer
67
+ Feature: Testing out CukeWriter
68
+ [generated from features/suppress_empty_scenarios.feature]
69
+
70
+ Scenario: that does write a step
71
+ [from features/suppress_empty_scenarios.feature:6]
72
+ Given I do that
73
+
74
+ """
75
+
76
+ Scenario: When no scenarios are written, don't write a feature
77
+ Given a file named "features/suppress_empty_features.feature" with:
78
+ """
79
+ Feature: Look Ma, no steps!
80
+
81
+ Scenario: this guy don't do nuthin'
82
+ Given I don't do much
83
+
84
+ Scenario: this guy doesn't do anything either
85
+ Given I don't do much
86
+ """
87
+ And I append to "features/step_definitions/steps.rb" with:
88
+ """
89
+
90
+ Given /^I don't do much$/ do
91
+ :do_relatively_nothing
92
+ end
93
+
94
+ """
95
+ When I run the "suppress_empty_scenarios" feature
96
+ Then the following files should not exist:
97
+ | features/supress_empty_features.feature |
98
+
@@ -0,0 +1,39 @@
1
+ @announce
2
+ Feature: Here is a feature (let's call him "Bob") who uses aruba
3
+ to write and then run a feature which uses CukeWriter to write
4
+ another feature which Bob then runs and verifies its output.
5
+ Great job, Bob!
6
+
7
+ Background:
8
+ Given a typical support/env.rb file
9
+ And a file named "features/step_definitions/steps.rb" with:
10
+ """
11
+ Given /^I wrote this with aruba$/ do
12
+ @step_collector ||= StepCollector.new
13
+ @step_collector.add "Then I wrote this with CukeWriter"
14
+ end
15
+ Then /^I wrote this with CukeWriter$/ do
16
+ puts "Hi, Bob!"
17
+ end
18
+ """
19
+
20
+ Scenario: Bob writes and runs a feature, and then runs the feature written by that feature (via CukeWriter)
21
+ Given a file named "features/bobs_end_to_end.feature" with:
22
+ """
23
+ Feature: Bob writes a feature that writes a feature
24
+
25
+ Scenario: here is Bob's scenario
26
+ Given I wrote this with aruba
27
+
28
+ """
29
+ When I run the "bobs_end_to_end" feature
30
+ And I run `cucumber features/generated_features/P123456/bobs_end_to_end.cw.feature --no-color --no-source -r features/`
31
+ Then it should pass with:
32
+ """
33
+ 1 scenario (1 passed)
34
+ 1 step (1 passed)
35
+ """
36
+ And the output should contain:
37
+ """
38
+ Hi, Bob!
39
+ """
@@ -0,0 +1,64 @@
1
+ @announce
2
+ Feature: Features which have scenario outlines should generate a separate scenario
3
+ for each pass through the scenario outlines steps (i.e. through each row in the
4
+ examples table).
5
+
6
+ Background:
7
+ Given a typical support/env.rb file
8
+ And a file named "features/step_definitions/steps.rb" with:
9
+ """
10
+ Given /^I don't do much$/ do
11
+ :do_relatively_nothing
12
+ end
13
+ Given /^I add a step "([^"]*)"$/ do |step_tag|
14
+ @step_collector ||= StepCollector.new
15
+ @step_collector.add "Given I do \"#{step_tag}\""
16
+ end
17
+ """
18
+
19
+ Scenario: a feature with a scenario outline generates a separate scenario for each example
20
+ Given a file named "features/has_a_scenario_outline.feature" with:
21
+ """
22
+ Feature: Messin' wit' scenario outlines
23
+
24
+ Scenario Outline: This is, well, you know
25
+ Given I don't do much
26
+ But I add a step "<step_name>"
27
+
28
+ Examples:
29
+ | step_name |
30
+ | foo |
31
+ | bar |
32
+ | baz |
33
+
34
+ Scenario: Another scenario just to prove there are no side effects
35
+ Given I add a step "have fun!"
36
+ """
37
+ When I run the "has_a_scenario_outline" feature
38
+ Then the file "features/generated_features/P123456/has_a_scenario_outline.cw.feature" should not contain "Scenario Outline:"
39
+ And the file "features/generated_features/P123456/has_a_scenario_outline.cw.feature" should not contain "Examples:"
40
+ And the file "features/generated_features/P123456/has_a_scenario_outline.cw.feature" should not contain "Scenarios:"
41
+ But the file "features/generated_features/P123456/has_a_scenario_outline.cw.feature" should contain exactly:
42
+ """
43
+ @cuke_writer
44
+ Feature: Messin' wit' scenario outlines
45
+ [generated from features/has_a_scenario_outline.feature]
46
+
47
+ Scenario: This is, well, you know
48
+ [from features/has_a_scenario_outline.feature:9]
49
+ Given I do "foo"
50
+
51
+ Scenario: This is, well, you know
52
+ [from features/has_a_scenario_outline.feature:10]
53
+ Given I do "bar"
54
+
55
+ Scenario: This is, well, you know
56
+ [from features/has_a_scenario_outline.feature:11]
57
+ Given I do "baz"
58
+
59
+ Scenario: Another scenario just to prove there are no side effects
60
+ [from features/has_a_scenario_outline.feature:13]
61
+ Given I do "have fun!"
62
+
63
+ """
64
+
@@ -0,0 +1,17 @@
1
+ Given /^a serial number "([^"]*)"$/ do |serial_number|
2
+ SerialNumber.number = serial_number
3
+ end
4
+
5
+ Given /^a typical support\/env.rb file$/ do
6
+ Given "a file named \"features/support/env.rb\" with:", <<-EOF
7
+ require File.dirname(__FILE__) + '/../../../../lib/cuke_writer'
8
+ require File.dirname(__FILE__) + '/../../../../lib/step_collector'
9
+ require File.dirname(__FILE__) + '/../../../../lib/serial_number'
10
+
11
+ SerialNumber.number = "P123456"
12
+ EOF
13
+ end
14
+
15
+ When /^I run the "([^"]*)" feature$/ do |feature_name|
16
+ When "I run \`cucumber features/#{feature_name}.feature -f CukeWriter::Formatter -o cuke_writer.txt -f progress\`"
17
+ end
@@ -0,0 +1,41 @@
1
+ @announce
2
+ Feature: a step table may be passed to the step_collector to be
3
+ appended after the corresponding step.
4
+
5
+ Background:
6
+ Given a typical support/env.rb file
7
+ And a file named "features/step_definitions/steps.rb" with:
8
+ """
9
+ Given /^I do "([^"]*)" with:$/ do |step_tag, table|
10
+ @step_collector ||= StepCollector.new
11
+ @step_collector.add "Then I put \"#{step_tag}\" on the table:", {:table => table}
12
+ end
13
+ """
14
+
15
+ Scenario:
16
+ Given a file named "features/has_a_step_table.feature" with:
17
+ """
18
+ Feature: Messin' wit step tables!
19
+
20
+ Scenario: Here we go
21
+ Given I do "this" with:
22
+ | that | those |
23
+ | thing | things |
24
+ | there | overthere |
25
+ """
26
+ When I run the "has_a_step_table" feature
27
+ Then the file "features/generated_features/P123456/has_a_step_table.cw.feature" should contain exactly:
28
+ """
29
+ @cuke_writer
30
+ Feature: Messin' wit step tables!
31
+ [generated from features/has_a_step_table.feature]
32
+
33
+ Scenario: Here we go
34
+ [from features/has_a_step_table.feature:3]
35
+ Then I put "this" on the table:
36
+ | that | those |
37
+ | thing | things |
38
+ | there | overthere |
39
+
40
+ """
41
+
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'aruba/cucumber'
4
+ require File.dirname(__FILE__) + '/../../lib/serial_number'
@@ -0,0 +1,10 @@
1
+ module Cucumber
2
+ module Ast
3
+ class Feature
4
+ def filename
5
+ @file.gsub(/^.*\//, '').gsub(/\.feature$/, '.cw.feature')
6
+ end
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,62 @@
1
+ require 'fileutils'
2
+ require File.dirname(__FILE__) + '/step_collector'
3
+ require File.dirname(__FILE__) + '/serial_number'
4
+ require File.dirname(__FILE__) + '/cucumber_ast_feature'
5
+
6
+ module CukeWriter
7
+ class Formatter
8
+
9
+ def initialize(step_mother, path_or_io, options)
10
+ @options = options
11
+ @meta_dir = 'generated_features'
12
+ @step_collector = StepCollector.new
13
+ @current_scenario_outline_heading = nil
14
+ @scenario_outline_name = nil
15
+ @currently_in_examples_table = false
16
+ end
17
+
18
+ def after_feature(feature)
19
+ if @step_collector.steps.size > 0
20
+ FileUtils.mkdir_p(output_directory) unless File.directory?(output_directory)
21
+ File.open("#{output_directory}/#{feature.filename}", 'w') do |fh|
22
+ fh.write "@cuke_writer\nFeature: #{feature.name}\n [generated from #{feature.file}]\n"
23
+ fh.write @step_collector.steps.join("\n") + "\n"
24
+ end
25
+ end
26
+ @step_collector.reset
27
+ end
28
+
29
+ def scenario_name(keyword, name, file_colon_line, source_indent)
30
+ if keyword == 'Scenario Outline'
31
+ @scenario_outline_info = {:name => name, :file => file_colon_line.gsub(/:\d+$/, '')}
32
+ else
33
+ @step_collector.add_scenario "#{keyword}: #{name}\n [from #{file_colon_line}]", {:indent => 2}
34
+ end
35
+ end
36
+
37
+ def background_name(keyword, name, file_colon_line, source_indent)
38
+ @step_collector.add_scenario "#{keyword}: #{name}\n [from #{file_colon_line}]", {:indent => 2}
39
+ end
40
+
41
+ def examples_name(*args)
42
+ @currently_in_examples_table = true
43
+ end
44
+
45
+ def after_examples(*args)
46
+ @scenario_outline_name = nil
47
+ @currently_in_examples_table = false
48
+ end
49
+
50
+ def before_table_row(table_row)
51
+ if @currently_in_examples_table
52
+ @step_collector.add_scenario("Scenario: #{@scenario_outline_info[:name]}\n [from #{@scenario_outline_info[:file]}:#{table_row.line}]", {:indent => 2})
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def output_directory
59
+ "features/#{@meta_dir}/#{SerialNumber}"
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,3 @@
1
+ module CukeWriter
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,24 @@
1
+ require 'date'
2
+
3
+ class SerialNumber
4
+ @@prefix = ''
5
+ @@number = nil
6
+
7
+ def self.number
8
+ @@number ||= generate_new_number
9
+ "#{@@prefix}#{@@number}"
10
+ end
11
+ def self.number=(new_number)
12
+ @@number = new_number
13
+ end
14
+ def self.prefix=(prefix)
15
+ @@prefix = prefix
16
+ end
17
+ def self.generate_new_number
18
+ DateTime.now.strftime('%Y%m%d%H%M%S')
19
+ end
20
+ def self.to_s
21
+ number
22
+ end
23
+ end
24
+
@@ -0,0 +1,29 @@
1
+ class StepCollector
2
+ @@scenarios = []
3
+
4
+ def add_scenario(scenario, p={})
5
+ indent = p[:indent] || 2
6
+ spaces = "\n" + " " * indent
7
+ @@scenarios << [ spaces + scenario ]
8
+ end
9
+
10
+ def add(step, p={})
11
+ table = p[:table]
12
+ indent = p[:indent] || 4
13
+ spaces = " " * indent
14
+ @@scenarios.last << spaces + step + prettified(table).to_s
15
+ end
16
+
17
+ def steps
18
+ @@scenarios.select { |scenario| scenario.size > 1 }.flatten
19
+ end
20
+
21
+ def reset
22
+ @@scenarios = []
23
+ end
24
+
25
+ def prettified(table, indent=4)
26
+ return nil unless table
27
+ table.to_s(:color => false, :indent => indent + 2).gsub(/\| +/, "| ").gsub(/\s+$/, '')
28
+ end
29
+ end
@@ -0,0 +1,36 @@
1
+ require 'date'
2
+ require File.dirname(__FILE__) + '/../../lib/serial_number.rb'
3
+
4
+ describe 'SerialNumber' do
5
+ before do
6
+ @time_piece = DateTime.now.strftime('%Y%m%d%H%M')
7
+ end
8
+ it "has a number (autogenerated!)" do
9
+ SerialNumber.number.should_not be_nil
10
+ end
11
+ specify "the autogenerated serial number should be date-time based" do
12
+ SerialNumber.number.should match(@time_piece)
13
+ end
14
+ it "accepts a serial number" do
15
+ lambda { SerialNumber.number = 123456 }.should_not raise_error
16
+ end
17
+ specify "the newly set number should be the serial number" do
18
+ SerialNumber.number = 987654
19
+ SerialNumber.number.should == "987654"
20
+ end
21
+ it "has a to_s method" do
22
+ SerialNumber.number = 112233445566
23
+ SerialNumber.to_s.should == '112233445566'
24
+ end
25
+ specify "this is cool: evaluate in string context, and class name returns the serial number!" do
26
+ SerialNumber.number = 333444555
27
+ "(#{SerialNumber})".should == '(333444555)'
28
+ end
29
+ it "accepts a prefix" do
30
+ lambda { SerialNumber.prefix = 'bar' }.should_not raise_error
31
+ end
32
+ specify "the number should begin with the prefix" do
33
+ SerialNumber.prefix = 'baz'
34
+ SerialNumber.number.should match(/^baz/)
35
+ end
36
+ end
@@ -0,0 +1,52 @@
1
+ require File.dirname(__FILE__) + '/../../lib/step_collector'
2
+
3
+ describe StepCollector do
4
+ before do
5
+ @step_collector = StepCollector.new
6
+ end
7
+
8
+ it "should return its steps" do
9
+ @step_collector.steps.should == []
10
+ end
11
+
12
+ it "adds scenarios" do
13
+ lambda { @step_collector.add_scenario "Scenario: We do stuff" }.should_not raise_error
14
+ end
15
+
16
+ it "should have an add method" do
17
+ lambda { @step_collector.add "Given my mama didn't raise no fool" }.should_not raise_error
18
+ end
19
+
20
+ specify "default indent for scenario headers is two spaces" do
21
+ @step_collector.steps.first.should == "\n Scenario: We do stuff"
22
+ end
23
+
24
+ specify "default indent should be 4 spaces" do
25
+ @step_collector.add "When I indent things look nicer"
26
+ @step_collector.steps.last.should == " When I indent things look nicer"
27
+ end
28
+
29
+ it "accumulates steps" do
30
+ @step_collector.add "When I spin around like this"
31
+ @step_collector.steps.should == [
32
+ "\n Scenario: We do stuff",
33
+ " Given my mama didn't raise no fool",
34
+ " When I indent things look nicer",
35
+ " When I spin around like this" ]
36
+ end
37
+
38
+ it "doesn't report scenarios which have no steps" do
39
+ @step_collector.add_scenario "Scenario: We do some more stuff (but not really)"
40
+ @step_collector.steps.should == [
41
+ "\n Scenario: We do stuff",
42
+ " Given my mama didn't raise no fool",
43
+ " When I indent things look nicer",
44
+ " When I spin around like this" ]
45
+ end
46
+
47
+ it "has no steps after resetting" do
48
+ @step_collector.reset
49
+ @step_collector.steps.should == []
50
+ end
51
+
52
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cuke_writer
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Joel Helbling
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-06-08 00:00:00 -04:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: cucumber
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 0.10.0
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 0.9.2
36
+ type: :development
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: rspec
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 2.6.0
47
+ type: :development
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: rspec-core
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 2.6.4
58
+ type: :development
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: aruba
62
+ prerelease: false
63
+ requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 0.3.0
69
+ type: :development
70
+ version_requirements: *id005
71
+ description: A custom Cucumber formatter which collects steps and generates serialized sets of Cucumber features. Meta FTW!
72
+ email:
73
+ - joel@joelhelbling.com
74
+ executables: []
75
+
76
+ extensions: []
77
+
78
+ extra_rdoc_files: []
79
+
80
+ files:
81
+ - .gitignore
82
+ - Gemfile
83
+ - README.md
84
+ - Rakefile
85
+ - cuke_writer.gemspec
86
+ - features/background.feature
87
+ - features/cuke_writer.feature
88
+ - features/good_job_bob.feature
89
+ - features/scenario_outline.feature
90
+ - features/step_definitions/cuke_writer_steps.rb
91
+ - features/step_tables.feature
92
+ - features/support/env.rb
93
+ - lib/cucumber_ast_feature.rb
94
+ - lib/cuke_writer.rb
95
+ - lib/cuke_writer/version.rb
96
+ - lib/serial_number.rb
97
+ - lib/step_collector.rb
98
+ - spec/lib/serial_number_spec.rb
99
+ - spec/lib/step_collector_spec.rb
100
+ has_rdoc: true
101
+ homepage: http://github.com/joelhelbling/cuke_writer
102
+ licenses: []
103
+
104
+ post_install_message:
105
+ rdoc_options: []
106
+
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: "0"
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: "0"
121
+ requirements: []
122
+
123
+ rubyforge_project: cuke_writer
124
+ rubygems_version: 1.6.2
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: A Cucumber formatter which writes Cucumber features.
128
+ test_files:
129
+ - features/background.feature
130
+ - features/cuke_writer.feature
131
+ - features/good_job_bob.feature
132
+ - features/scenario_outline.feature
133
+ - features/step_definitions/cuke_writer_steps.rb
134
+ - features/step_tables.feature
135
+ - features/support/env.rb
136
+ - spec/lib/serial_number_spec.rb
137
+ - spec/lib/step_collector_spec.rb