coryodaniel-stories 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Damian Janowski and Michel Martens for Citrusbyte
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,187 @@
1
+ Stories
2
+ =======
3
+
4
+ Stories and User Acceptance Tests for the minimalist test framework [Contest](http://github.com/citrusbyte/contest).
5
+
6
+ Description
7
+ -----------
8
+
9
+ Write user stories and user acceptace tests using Contest, the tiny add on to Test::Unit that provides nested contexts and declarative tests.
10
+
11
+ Usage
12
+ -----
13
+
14
+ Declare your stories as follows:
15
+
16
+ require 'stories'
17
+
18
+ class UserStoryTest < Test::Unit::TestCase
19
+ story "As a user I want to create stories so I can test if they pass" do
20
+ setup do
21
+ @user = "valid user"
22
+ end
23
+
24
+ scenario "A valid user" do
25
+ assert_equal "valid user", @user
26
+ end
27
+ end
28
+ end
29
+
30
+ If you are using Rails, you can use stories for your integration tests with Webrat:
31
+
32
+ class UserStoriesTest < ActionController::IntegrationTest
33
+ story "As a user I want to log in so that I can access restricted features" do
34
+ setup do
35
+ @user = User.spawn :name => "Albert", :email => "albert@example.com"
36
+ end
37
+
38
+ scenario "Using good information" do
39
+ visit "/"
40
+ click_link "Log in"
41
+ fill_in "Email", :with => user.email
42
+ fill_in "Password", :with => user.password
43
+ click_button "Sign in"
44
+
45
+ assert_contain "Logout"
46
+ assert_contain "Welcome Albert"
47
+ end
48
+ end
49
+ end
50
+
51
+ This will produce the following output:
52
+
53
+ - As a user I want to log in so that I can access restricted features
54
+ Using good information
55
+ Go to “/”
56
+ Click “Log in”
57
+ Fill in “Email” with “albert@example.com”
58
+ Fill in “Password” with “secret”
59
+ Click “Sign in”
60
+ I should see “Logout”
61
+ I should see “Welcome Albert”
62
+
63
+ Custom reports
64
+ --------------
65
+
66
+ Stories ships with reports for many Webrat helpers, but you can write your own version or add reports
67
+ for other helpers.
68
+
69
+ For instance, let's say you add this assertion:
70
+
71
+ module Test::Unit::Assertions
72
+ def assert_current_page(path)
73
+ assert_equal path, current_url.sub(%r{^http://www\.example\.com}, '')
74
+ end
75
+ end
76
+
77
+ When you run it, no output will be generated because Stories doesn't know what to say about this assertion.
78
+ Adding a custom report is easy:
79
+
80
+ module Stories::Webrat
81
+ report_for :assert_current_page do |page|
82
+ "I should be on #{quote(page)}"
83
+ end
84
+ end
85
+
86
+ All this should reside in your `stories_helper.rb`.
87
+
88
+ Step wrappers
89
+ ---------------
90
+
91
+ There are two other methods that are useful for reporting steps or assertions: `silent` and `report`.
92
+
93
+ The `silent` helper gives you the ability to suspend the steps and assertions' normal output:
94
+
95
+ ...
96
+ silent do
97
+ assert_contain "Some obscure hash"
98
+ end
99
+ ...
100
+
101
+ In a similar fashion, `report` lets you replace the normal output with your own message:
102
+
103
+ ...
104
+ report "I verify that the hash generated correctly" do
105
+ assert_contain "The same obscure hash"
106
+ end
107
+ ...
108
+
109
+ Running stories
110
+ ---------------
111
+
112
+ You can run it normally, it's Test::Unit after all. If you want to run a particular test, say "yet more tests", try this:
113
+
114
+ $ ruby my_test.rb -n test_yet_more_tests
115
+
116
+ Or with a regular expression:
117
+
118
+ $ ruby my_test.rb -n /yet_more_tests/
119
+
120
+ Pending stories
121
+ ---------------
122
+
123
+ Since Stories aims to improve your project's documentation, you can have pending stories:
124
+
125
+ class UserStoryTest < Test::Unit::TestCase
126
+ story "As a user I want to create stories so I can test if they pass"
127
+ end
128
+
129
+ This is useful if you want to write all your stories upfront, even before you write the acceptance tests.
130
+
131
+ Awesome output
132
+ --------------
133
+
134
+ You can get a nice output with your user stories with the `stories` runner:
135
+
136
+ $ ruby my_test.rb --runner=stories
137
+
138
+ Now, if you want to impress everyone around you, try this:
139
+
140
+ $ ruby my_test.rb --runner=stories-pdf
141
+
142
+ You will get a nicely formatted PDF with your user stories. It uses [Prawn](http://prawn.majesticseacreature.com/), so you will need to install it first.
143
+
144
+ If you're using Rails, you can run the whole build with the following Rake tasks:
145
+
146
+ $ rake stories
147
+ $ rake stories:pdf
148
+
149
+ Installation
150
+ ------------
151
+
152
+ $ sudo gem install stories
153
+
154
+ If you want to use it with Rails, add this to config/environment.rb:
155
+
156
+ config.gem "stories"
157
+
158
+ Then you can vendor the gem:
159
+
160
+ rake gems:install
161
+ rake gems:unpack
162
+
163
+ License
164
+ -------
165
+
166
+ Copyright (c) 2009 Damian Janowski and Michel Martens for Citrusbyte
167
+
168
+ Permission is hereby granted, free of charge, to any person
169
+ obtaining a copy of this software and associated documentation
170
+ files (the "Software"), to deal in the Software without
171
+ restriction, including without limitation the rights to use,
172
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
173
+ copies of the Software, and to permit persons to whom the
174
+ Software is furnished to do so, subject to the following
175
+ conditions:
176
+
177
+ The above copyright notice and this permission notice shall be
178
+ included in all copies or substantial portions of the Software.
179
+
180
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
181
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
182
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
183
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
184
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
185
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
186
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
187
+ OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ task :default => :test
4
+
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.pattern = 'test/**/*_test.rb'
7
+ t.verbose = false
8
+ end
data/lib/stories.rb ADDED
@@ -0,0 +1,21 @@
1
+ require "rubygems"
2
+ require "contest"
3
+
4
+ unless defined?(Test::Unit::AutoRunner)
5
+ gem "test-unit", "1.2.3"
6
+ end
7
+
8
+ class Test::Unit::TestCase
9
+ class << self
10
+ alias story context
11
+ alias scenario test
12
+ end
13
+ end
14
+
15
+ Test::Unit::AutoRunner::RUNNERS[:stories] = proc do |r|
16
+ puts "Story runner not found. You need to require 'stories/runner'."
17
+ end
18
+
19
+ Test::Unit::AutoRunner::RUNNERS[:"stories-pdf"] = proc do |r|
20
+ puts "Story runner not found. You need to require 'stories/runner'."
21
+ end
@@ -0,0 +1,168 @@
1
+ # encoding: utf-8
2
+ require 'test/unit/ui/console/testrunner'
3
+
4
+ module Stories
5
+ def self.all
6
+ @all ||= {}
7
+ end
8
+
9
+ module TestCase
10
+ def self.included(base)
11
+ class << base
12
+ def story(name, *args, &block)
13
+ context(name, *args) do
14
+ Stories.all[self] = Stories::Story.new(name)
15
+ class_eval(&block) if block_given?
16
+ end
17
+ end
18
+
19
+ def scenario(name, *args, &block)
20
+ scenario = Stories::Scenario.new(name)
21
+
22
+ Stories.all[self].scenarios << scenario
23
+
24
+ test(name) do
25
+ @scenario = scenario
26
+ instance_eval(&block)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ Test::Unit::TestCase.send(:include, Stories::TestCase)
35
+
36
+ module Test::Unit::Assertions
37
+ def report(text, &block)
38
+ @scenario.steps << text
39
+ silent(&block) if block_given?
40
+ end
41
+
42
+ def silent(&block)
43
+ scenario, @scenario = @scenario, Stories::Scenario.new("#{@scenario.name} (Silent)")
44
+
45
+ begin
46
+ block.call
47
+ ensure
48
+ @scenario = scenario
49
+ end
50
+ end
51
+ end
52
+
53
+ module Stories
54
+ class Story
55
+ attr_accessor :name, :scenarios
56
+
57
+ def initialize(name)
58
+ @name = name
59
+ @scenarios = []
60
+ end
61
+ end
62
+
63
+ class Scenario
64
+ attr_accessor :name, :steps, :assertions
65
+
66
+ def initialize(name)
67
+ @name = name
68
+ @steps = []
69
+ @assertions = []
70
+ end
71
+ end
72
+
73
+ class Runner < Test::Unit::UI::Console::TestRunner
74
+ def test_finished(name)
75
+ print "."
76
+ end
77
+
78
+ def add_fault(fault)
79
+ print "F"
80
+ @faults << fault
81
+ end
82
+
83
+ def finished(elapsed_time)
84
+ puts
85
+
86
+ Stories.all.values.to_a.each_with_index do |story,i|
87
+ puts "- #{story.name}"
88
+
89
+ story.scenarios.each do |scenario|
90
+ puts " #{scenario.name}"
91
+
92
+ unless scenario.steps.empty? && scenario.assertions.empty?
93
+ scenario.steps.each do |step|
94
+ puts " #{step}"
95
+ end
96
+
97
+ scenario.assertions.each do |assertion|
98
+ puts " #{assertion}"
99
+ end
100
+
101
+ puts
102
+ end
103
+ end
104
+
105
+ puts unless i + 1 == Stories.all.values.size
106
+ end
107
+
108
+ super
109
+ puts "%d stories, %d scenarios" % [Stories.all.values.size, Stories.all.values.inject(0) {|total,s| total + s.scenarios.size }]
110
+ end
111
+ end
112
+
113
+ module Webrat
114
+ def report_for(action, &block)
115
+ define_method(action) do |*args|
116
+ @scenario.steps << block.call(*args)
117
+ super(*args)
118
+ end
119
+ end
120
+
121
+ module_function :report_for
122
+ end
123
+ end
124
+
125
+ Test::Unit::AutoRunner::RUNNERS[:stories] = proc do |r|
126
+ Stories::Runner
127
+ end
128
+
129
+ Test::Unit::AutoRunner::RUNNERS[:"stories-pdf"] = proc do |r|
130
+ begin
131
+ Stories::Runner::PDF
132
+ rescue NameError
133
+ require File.expand_path(File.dirname(__FILE__) + "/runner/pdf")
134
+ Stories::Runner::PDF
135
+ end
136
+ end
137
+
138
+ # Common Webrat steps.
139
+ module Stories::Webrat
140
+ report_for :click_link do |name|
141
+ "Click #{quote(name)}"
142
+ end
143
+
144
+ report_for :click_button do |name|
145
+ "Click #{quote(name)}"
146
+ end
147
+
148
+ report_for :fill_in do |name, opts|
149
+ "Fill in #{quote(name)} with #{quote(opts[:with])}"
150
+ end
151
+
152
+ report_for :visit do |page|
153
+ "Go to #{quote(page)}"
154
+ end
155
+
156
+ report_for :check do |name|
157
+ "Check #{quote(name)}"
158
+ end
159
+
160
+ report_for :assert_contain do |text|
161
+ "I should see #{quote(text)}"
162
+ end
163
+
164
+ def quote(text)
165
+ "“#{text}”"
166
+ end
167
+ module_function :quote
168
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+
3
+ gem "prawn", "~> 0.4"
4
+ require "prawn"
5
+
6
+ module Stories
7
+ class Runner::PDF < Runner
8
+ def render_header(pdf)
9
+ end
10
+
11
+ def finished(elapsed_time)
12
+ super
13
+
14
+ Prawn::Document.generate("stories.pdf", :page_size => "A4") do |pdf|
15
+ render_header(pdf)
16
+
17
+ pdf.text "User Acceptance Tests", :size => 20, :style => :bold
18
+
19
+ pdf.move_down(15)
20
+
21
+ Stories.all.values.each do |story|
22
+ pdf.text story.name, :style => :bold
23
+
24
+ story.scenarios.each_with_index do |scenario,i|
25
+ scenario_leading = 15
26
+
27
+ pdf.span(pdf.bounds.width - scenario_leading, :position => scenario_leading) do
28
+ pdf.text "— #{scenario.name}"
29
+
30
+ pdf.fill_color "666666"
31
+
32
+ unless scenario.steps.empty? && scenario.assertions.empty?
33
+ pdf.span(pdf.bounds.width - 30, :position => 30) do
34
+ pdf.font_size(9) do
35
+ render_many(pdf, scenario.steps)
36
+ render_many(pdf, scenario.assertions)
37
+ end
38
+ end
39
+ end
40
+
41
+ pdf.move_down(5) unless i + 1 == story.scenarios.size
42
+
43
+ pdf.fill_color "000000"
44
+ end
45
+ end
46
+
47
+ pdf.move_down(10)
48
+ end
49
+ end
50
+ end
51
+
52
+ def render_many(pdf, elements)
53
+ elements.each do |el|
54
+ pdf.text el.to_s
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,17 @@
1
+ Rake::TestTask.new(:stories => "db:test:prepare") do |t|
2
+ t.libs << "test"
3
+ t.options = "--runner=stories"
4
+ t.pattern = 'test/integration/**/*_test.rb'
5
+ t.verbose = false
6
+ end
7
+ Rake::Task['stories'].comment = "Run and print the UATs"
8
+
9
+ namespace :stories do
10
+ Rake::TestTask.new(:pdf => "db:test:prepare") do |t|
11
+ t.libs << "test"
12
+ t.options = "--runner=stories-pdf"
13
+ t.pattern = 'test/integration/**/*_test.rb'
14
+ t.verbose = false
15
+ end
16
+ Rake::Task['stories:pdf'].comment = "Run UATs and produce a nice PDF"
17
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,13 @@
1
+ if RAILS_ENV == 'test'
2
+
3
+ require File.dirname(__FILE__) + "/../lib/stories/runner"
4
+
5
+ # Require webrat and configure it to work in Rails mode.
6
+ require "webrat"
7
+
8
+ Webrat.configure do |config|
9
+ config.mode = :rails
10
+ end
11
+
12
+ ActionController::IntegrationTest.send(:include, Stories::Webrat)
13
+ end
data/test/all_test.rb ADDED
@@ -0,0 +1,32 @@
1
+ require File.dirname(__FILE__) + "/../lib/stories"
2
+ require File.dirname(__FILE__) + "/../lib/stories/runner"
3
+
4
+ # Use the story runner by default.
5
+ Test::Unit::AutoRunner::RUNNERS[:console] = Proc.new {|r| Stories::Runner }
6
+
7
+ class UserStoryTest < Test::Unit::TestCase
8
+ story "As a user I want to create stories so I can test if they pass" do
9
+ setup do
10
+ @user = "valid user"
11
+ end
12
+
13
+ scenario "A valid user" do
14
+ assert_equal "valid user", @user
15
+ end
16
+ end
17
+
18
+ story "As a user I want helpers so that I can extract" do
19
+ def some_helper
20
+ 1
21
+ end
22
+
23
+ scenario "A call to a helper" do
24
+ report "I use some helper" do
25
+ some_helper
26
+ end
27
+ assert_equal 1, some_helper
28
+ end
29
+ end
30
+
31
+ story "Pending story"
32
+ end
data/test/pdf_test.rb ADDED
@@ -0,0 +1,25 @@
1
+ require File.dirname(__FILE__) + "/../lib/stories"
2
+ require File.dirname(__FILE__) + "/../lib/stories/runner"
3
+ require File.dirname(__FILE__) + "/../lib/stories/runner/pdf"
4
+
5
+ class Stories::Runner::PDF
6
+ def render_header(pdf)
7
+ pdf.text "My custom header", :size => 20, :style => :bold
8
+ pdf.move_down 20
9
+ end
10
+ end
11
+
12
+ # Use the story runner by default.
13
+ Test::Unit::AutoRunner::RUNNERS[:console] = Proc.new {|r| Stories::Runner::PDF }
14
+
15
+ class UserStoryTest < Test::Unit::TestCase
16
+ story "As a user I want to create stories with a custom PDF" do
17
+ setup do
18
+ @user = "valid user"
19
+ end
20
+
21
+ scenario "A valid user" do
22
+ assert_equal "valid user", @user
23
+ end
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: coryodaniel-stories
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 4
9
+ version: 0.1.4
10
+ platform: ruby
11
+ authors:
12
+ - Damian Janowski
13
+ - Michel Martens
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-04-30 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: contest
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ - 1
31
+ version: "0.1"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: test-unit
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ version: "0"
44
+ type: :runtime
45
+ version_requirements: *id002
46
+ description: Write Stories and User Acceptance Tests using Contest, the tiny add on to Test::Unit that provides nested contexts and declarative tests.
47
+ email:
48
+ - djanowski@dimaion.com
49
+ - michel@soveran.com
50
+ executables: []
51
+
52
+ extensions: []
53
+
54
+ extra_rdoc_files: []
55
+
56
+ files:
57
+ - lib/tasks/stories.rake
58
+ - lib/stories/runner/pdf.rb
59
+ - lib/stories/runner.rb
60
+ - lib/stories.rb
61
+ - README.markdown
62
+ - LICENSE
63
+ - Rakefile
64
+ - rails/init.rb
65
+ - test/all_test.rb
66
+ - test/pdf_test.rb
67
+ has_rdoc: true
68
+ homepage: http://github.com/citrusbyte/stories
69
+ licenses: []
70
+
71
+ post_install_message:
72
+ rdoc_options: []
73
+
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ requirements: []
91
+
92
+ rubyforge_project: stories
93
+ rubygems_version: 1.3.6
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Write Stories and User Acceptance Tests using the minimalist testing framework Contest.
97
+ test_files: []
98
+