cuke_writer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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