stories 0.0.5

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,111 @@
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
36
+ end
37
+
38
+ scenario "Using good information" do
39
+ visit "/"
40
+ click_link "Log in"
41
+ fill_in :session_email, :with => user.email
42
+ fill_in :session_password, :with => user.password
43
+ click_button "Sign in"
44
+
45
+ assert_not_contain "Log in"
46
+ assert_not_contain "Sign up"
47
+ end
48
+ end
49
+ end
50
+
51
+ 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:
52
+
53
+ $ ruby my_test.rb -n test_yet_more_tests
54
+
55
+ Or with a regular expression:
56
+
57
+ $ ruby my_test.rb -n /yet_more_tests/
58
+
59
+ Awesome output
60
+ --------------
61
+
62
+ You can get a nice output with your user stories with the `stories` runner:
63
+
64
+ $ ruby my_test.rb --runner=stories
65
+
66
+ Now, if you want to impress everyone around you, try this:
67
+
68
+ $ ruby my_test.rb --runner=stories-pdf
69
+
70
+ 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.
71
+
72
+ Installation
73
+ ------------
74
+
75
+ $ gem sources -a http://gems.github.com (you only have to do this once)
76
+ $ sudo gem install citrusbyte-stories
77
+
78
+ If you want to use it with Rails, add this to config/environment.rb:
79
+
80
+ config.gem "citrusbyte-stories", :lib => 'stories', :source => 'http://gems.github.com'
81
+
82
+ Then you can vendor the gem:
83
+
84
+ rake gems:install
85
+ rake gems:unpack
86
+
87
+ License
88
+ -------
89
+
90
+ Copyright (c) 2009 Damian Janowski and Michel Martens for Citrusbyte
91
+
92
+ Permission is hereby granted, free of charge, to any person
93
+ obtaining a copy of this software and associated documentation
94
+ files (the "Software"), to deal in the Software without
95
+ restriction, including without limitation the rights to use,
96
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
97
+ copies of the Software, and to permit persons to whom the
98
+ Software is furnished to do so, subject to the following
99
+ conditions:
100
+
101
+ The above copyright notice and this permission notice shall be
102
+ included in all copies or substantial portions of the Software.
103
+
104
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
105
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
106
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
107
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
108
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
109
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
110
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
111
+ OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ require 'rake'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/testtask'
4
+ require 'rake/clean'
5
+
6
+ gem_spec_file = 'stories.gemspec'
7
+
8
+ gem_spec = eval(File.read(gem_spec_file)) rescue nil
9
+
10
+ task :default => :test
11
+
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.pattern = 'test/**/*_test.rb'
14
+ t.verbose = false
15
+ end
16
+
17
+ Rake::GemPackageTask.new(gem_spec) do |pkg|
18
+ pkg.need_zip = false
19
+ pkg.need_tar = false
20
+ rm_f FileList['pkg/**/*.*']
21
+ end if gem_spec
22
+
23
+ desc "Generate the gemspec file."
24
+ task :gemspec do
25
+ require 'erb'
26
+
27
+ File.open(gem_spec_file, 'w') do |f|
28
+ f.write ERB.new(File.read("#{gem_spec_file}.erb")).result(binding)
29
+ end
30
+ end
31
+
32
+ desc "Builds and installs the gem."
33
+ task :install => :repackage do
34
+ `sudo gem install pkg/#{gem_spec.name}-#{gem_spec.version}.gem`
35
+ end
data/lib/stories.rb ADDED
@@ -0,0 +1,17 @@
1
+ require "rubygems"
2
+ require "contest"
3
+
4
+ class Test::Unit::TestCase
5
+ class << self
6
+ alias story context
7
+ alias scenario test
8
+ end
9
+ end
10
+
11
+ Test::Unit::AutoRunner::RUNNERS[:stories] = proc do |r|
12
+ puts "Story runner not found. You need to require 'stories/runner'."
13
+ end
14
+
15
+ Test::Unit::AutoRunner::RUNNERS[:"stories-pdf"] = proc do |r|
16
+ puts "Story runner not found. You need to require 'stories/runner'."
17
+ end
@@ -0,0 +1,149 @@
1
+ require 'test/unit/ui/console/testrunner'
2
+
3
+ $stories = []
4
+
5
+ class Test::Unit::TestCase
6
+ class << self
7
+ alias original_story story
8
+ alias original_scenario scenario
9
+
10
+ def story(name, &block)
11
+ story = Stories::Story.new(name)
12
+
13
+ original_story(name) do
14
+ @@story = story
15
+ instance_eval(&block)
16
+ end
17
+
18
+ $stories << story
19
+ end
20
+
21
+ def scenario(name, &block)
22
+ scenario = Stories::Scenario.new(name)
23
+
24
+ original_scenario(name) do
25
+ @scenario = scenario
26
+ instance_eval(&block)
27
+ end
28
+
29
+ @@story.scenarios << scenario
30
+ end
31
+ end
32
+ end
33
+
34
+ module Stories
35
+ class Story
36
+ attr_accessor :name, :scenarios
37
+
38
+ def initialize(name)
39
+ @name = name
40
+ @scenarios = []
41
+ end
42
+ end
43
+
44
+ class Scenario
45
+ attr_accessor :name, :steps, :assertions
46
+
47
+ def initialize(name)
48
+ @name = name
49
+ @steps = []
50
+ @assertions = []
51
+ end
52
+ end
53
+
54
+ class Runner < Test::Unit::UI::Console::TestRunner
55
+ def test_finished(name)
56
+ print "."
57
+ end
58
+
59
+ def add_fault(fault)
60
+ print "F"
61
+ @faults << fault
62
+ end
63
+
64
+ def finished(elapsed_time)
65
+ puts
66
+
67
+ $stories.each_with_index do |story,i|
68
+ puts "- #{story.name}"
69
+
70
+ story.scenarios.each do |scenario|
71
+ puts " #{scenario.name}"
72
+
73
+ unless scenario.steps.empty? && scenario.assertions.empty?
74
+ scenario.steps.each do |step|
75
+ puts " #{step}"
76
+ end
77
+
78
+ scenario.assertions.each do |assertion|
79
+ puts " #{assertion}"
80
+ end
81
+
82
+ puts
83
+ end
84
+ end
85
+
86
+ puts unless i + 1 == $stories.size
87
+ end
88
+
89
+ super
90
+ puts "%d stories, %d scenarios" % [$stories.size, $stories.inject(0) {|total,s| total + s.scenarios.size }]
91
+ end
92
+ end
93
+
94
+ module Webrat
95
+ def report_for(action, &block)
96
+ define_method(action) do |*args|
97
+ @scenario.steps << block.call(*args)
98
+ super
99
+ end
100
+ end
101
+
102
+ module_function :report_for
103
+ end
104
+ end
105
+
106
+ Test::Unit::AutoRunner::RUNNERS[:stories] = proc do |r|
107
+ Stories::Runner
108
+ end
109
+
110
+ Test::Unit::AutoRunner::RUNNERS[:"stories-pdf"] = proc do |r|
111
+ begin
112
+ Stories::Runner::PDF
113
+ rescue NameError
114
+ require File.expand_path(File.dirname(__FILE__) + "/runner/pdf")
115
+ Stories::Runner::PDF
116
+ end
117
+ end
118
+
119
+ # Common Webrat steps.
120
+ module Stories::Webrat
121
+ report_for :click_link do |name|
122
+ "Click #{quote(name)}"
123
+ end
124
+
125
+ report_for :click_button do |name|
126
+ "Click #{quote(name)}"
127
+ end
128
+
129
+ report_for :fill_in do |name, opts|
130
+ "Fill in #{quote(name)} with #{quote(opts[:with])}"
131
+ end
132
+
133
+ report_for :visit do |page|
134
+ "Go to #{quote(page)}"
135
+ end
136
+
137
+ report_for :check do |name|
138
+ "Check #{quote(name)}"
139
+ end
140
+
141
+ report_for :assert_contain do |text|
142
+ "I should see #{quote(text)}"
143
+ end
144
+
145
+ def quote(text)
146
+ "“#{text}”"
147
+ end
148
+ module_function :quote
149
+ end
@@ -0,0 +1,56 @@
1
+ gem "prawn", "~> 0.4"
2
+ require "prawn"
3
+
4
+ module Stories
5
+ class Runner::PDF < Runner
6
+ def render_header(pdf)
7
+ end
8
+
9
+ def finished(elapsed_time)
10
+ super
11
+
12
+ Prawn::Document.generate("stories.pdf", :page_size => "A4") do |pdf|
13
+ render_header(pdf)
14
+
15
+ pdf.text "User Acceptance Tests", :size => 20, :style => :bold
16
+
17
+ pdf.move_down(15)
18
+
19
+ $stories.each do |story|
20
+ pdf.text story.name, :style => :bold
21
+
22
+ story.scenarios.each_with_index do |scenario,i|
23
+ scenario_leading = 15
24
+
25
+ pdf.span(pdf.bounds.width - scenario_leading, :position => scenario_leading) do
26
+ pdf.text "— #{scenario.name}"
27
+
28
+ pdf.fill_color "666666"
29
+
30
+ unless scenario.steps.empty? && scenario.assertions.empty?
31
+ pdf.span(pdf.bounds.width - 30, :position => 30) do
32
+ pdf.font_size(9) do
33
+ render_many(pdf, scenario.steps)
34
+ render_many(pdf, scenario.assertions)
35
+ end
36
+ end
37
+ end
38
+
39
+ pdf.move_down(5) unless i + 1 == story.scenarios.size
40
+
41
+ pdf.fill_color "000000"
42
+ end
43
+ end
44
+
45
+ pdf.move_down(10)
46
+ end
47
+ end
48
+ end
49
+
50
+ def render_many(pdf, elements)
51
+ elements.each do |el|
52
+ pdf.text el.to_s
53
+ end
54
+ end
55
+ end
56
+ 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,17 @@
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
+ 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 so I can test if they pass" 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,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stories
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Damian Janowski
8
+ - Michel Martens
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-04-30 00:00:00 -03:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: citrusbyte-contest
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 0.0.8
25
+ version:
26
+ description:
27
+ email: michel@soveran.com
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files: []
33
+
34
+ files:
35
+ - lib/stories/runner/pdf.rb
36
+ - lib/stories/runner.rb
37
+ - lib/stories.rb
38
+ - README.markdown
39
+ - LICENSE
40
+ - Rakefile
41
+ - rails/init.rb
42
+ - test/all_test.rb
43
+ - test/pdf_test.rb
44
+ has_rdoc: false
45
+ homepage: http://github.com/citrusbyte/stories
46
+ post_install_message:
47
+ rdoc_options: []
48
+
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.3.1
67
+ signing_key:
68
+ specification_version: 2
69
+ summary: Write stories and user acceptance tests using the minimalist testing framework Contest.
70
+ test_files: []
71
+