rspec-approvals 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,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rspec-approvals.gemspec
4
+ gemspec
data/License.txt ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2011 Katrina Owen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # RSpec Approvals
2
+
3
+ Approvals are based on the idea of the *_golden master_*.
4
+
5
+ You take a snapshot of an object, and then compare all future
6
+ versions of the object to the snapshot.
7
+
8
+ See [ApprovalTests](http://www.approvaltests.com) for videos and additional documentation about the general concept.
9
+
10
+ The original Approvals libraries for Java and C# were developed by Llewellyn Falco.
11
+
12
+
13
+ ## Configuration
14
+
15
+ The default location for the output files is
16
+
17
+ spec/approvals
18
+
19
+ You can change this using the configuration option
20
+
21
+ RSpec.configure do |c|
22
+ c.approvals_path = 'some/other/path'
23
+ end
24
+
25
+
26
+ ## Usage
27
+
28
+ The basic format of the approval is modeled after RSpec's `it`:
29
+
30
+ approve "something" do
31
+ "this is the received contents"
32
+ end
33
+
34
+
35
+ The `:to_s` method on the object will be used to generate the output for
36
+ the `*.received.txt` file. For custom objects you will need to override
37
+ the `:to_s` to get helpful output, rather than the default:
38
+
39
+ #<Object:0x0000010105ea40>
40
+
41
+ The first time the specs are run, two files will be created:
42
+
43
+ full_description_of_something.received.txt
44
+ full_description_of_something.approved.txt
45
+
46
+
47
+ Since you have not yet approved anything, the `*.approved.txt` file is
48
+ empty.
49
+
50
+ The contents of the two files are compared, and the approval will fail at this point.
51
+
52
+ ### Approving a spec
53
+
54
+ If the contents of the received file is to your liking, you can approve
55
+ the file by overwriting the approved file with the received file.
56
+
57
+ For an example who's full description is `My Spec`:
58
+
59
+ mv my_spec.received.txt my_spec.approved.txt
60
+
61
+ When you rerun the spec, it should now pass.
62
+
63
+ ### Formatters
64
+
65
+ You can specify a custom formatter when you run the specs.
66
+
67
+ E.g.
68
+
69
+ rspec --require /path/to/lib/rspec/approvals/formatters/opendiff_formatter.rb \
70
+ -f RSpec::Approvals::Formatters::OpendiffFormatter spec/
71
+
72
+ The OpendiffFormatter automatically launches opendiff for each failed
73
+ approval. It falls back on the ProgressFormatter for all non-approval
74
+ specs.
75
+
76
+ That's pretty unweildy, so I'm looking at options to add shortcuts for
77
+ this.
78
+
79
+
80
+
81
+ Copyright (c) 2011 Katrina Owen, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,80 @@
1
+ require 'rspec/expectations/errors'
2
+
3
+ module RSpec
4
+ module Approvals
5
+
6
+ class ReceivedDiffersError < RSpec::Expectations::ExpectationNotMetError; end
7
+
8
+ class Approval
9
+
10
+ def self.normalize(s)
11
+ s.gsub(/[\W]/, ' ').strip.squeeze(" ").gsub(' ', '_').downcase
12
+ end
13
+
14
+ def self.base_path(s)
15
+ Approvals.path + normalize(s)
16
+ end
17
+
18
+ attr_reader :location
19
+
20
+ def initialize(example, received = '')
21
+ @path = Approval.base_path(example.full_description)
22
+
23
+ example.options[:approval] = true
24
+ example.options[:approval_diff_paths] = {
25
+ :received => received_path,
26
+ :approved => approved_path,
27
+ }
28
+
29
+ write(:approved, '') unless File.exists?(approved_path)
30
+ write(:received, received)
31
+ end
32
+
33
+ def approved_path
34
+ "#{@path}.approved.txt"
35
+ end
36
+
37
+ def received_path
38
+ "#{@path}.received.txt"
39
+ end
40
+
41
+ def write(suffix, contents)
42
+ File.open("#{@path}.#{suffix}.txt", 'w') do |f|
43
+ f.write contents
44
+ end
45
+ end
46
+
47
+ def failure_message
48
+ <<-FAILURE_MESSAGE
49
+
50
+ Approval Failure:
51
+
52
+ The received contents did not match the approved contents.
53
+
54
+ Inspect the differences in the following files:
55
+ #{received_path}
56
+ #{approved_path}
57
+
58
+ If you like what you see in the *.received.txt file, you can approve it
59
+ like so:
60
+
61
+ mv #{received_path} #{approved_path}
62
+
63
+
64
+ FAILURE_MESSAGE
65
+ end
66
+
67
+ def location=(backtrace)
68
+ @location = [backtrace.first.gsub(Dir.pwd, '.')]
69
+ end
70
+
71
+ def verify
72
+ if FileUtils.cmp(received_path, approved_path)
73
+ File.unlink(received_path)
74
+ else
75
+ raise RSpec::Approvals::ReceivedDiffersError, failure_message, location
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,23 @@
1
+ require 'rspec/expectations/errors'
2
+
3
+ module RSpec
4
+ module Approvals
5
+ module DSL
6
+
7
+ def approve(description)
8
+
9
+ specify(description) do
10
+ approval = Approval.new(example, yield)
11
+
12
+ # We may be able to set file_path and
13
+ # line_number on example in the approval
14
+ # see RSpec::Core::Metadata::LocationKeys
15
+ approval.location = caller
16
+
17
+ approval.verify
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ require 'rspec/core/formatters/progress_formatter'
2
+
3
+ module RSpec
4
+ module Approvals
5
+ module Formatters
6
+
7
+ class OpendiffFormatter < RSpec::Core::Formatters::ProgressFormatter
8
+ def dump_failures
9
+ super
10
+ failed_examples.each do |example|
11
+ if example.options[:approval]
12
+ paths = example.options[:approval_diff_paths]
13
+ system("opendiff #{paths[:received]} #{paths[:approved]} &")
14
+ end
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ module RSpec
2
+ module Approvals
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,30 @@
1
+
2
+ require "rspec/approvals/version"
3
+ require "rspec/approvals/approval"
4
+ require "rspec/approvals/dsl"
5
+ require 'rspec/approvals/formatters/opendiff_formatter'
6
+
7
+
8
+ module RSpec
9
+ RSpec.configure do |c|
10
+ c.extend RSpec::Approvals::DSL
11
+ c.add_setting :approvals_path, :default => 'spec/approvals'
12
+ end
13
+
14
+ module Approvals
15
+
16
+ class << self
17
+ def initialize_approvals_path
18
+ FileUtils.makedirs(RSpec.configuration.approvals_path) unless Dir.exists?(RSpec.configuration.approvals_path)
19
+ end
20
+
21
+ def path
22
+ RSpec.configuration.approvals_path + "/"
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ RSpec::Approvals.initialize_approvals_path
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rspec/approvals/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rspec-approvals"
7
+ s.version = RSpec::Approvals::VERSION
8
+ s.authors = ["Katrina Owen"]
9
+ s.email = ["katrina.owen@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Approval Tests for Ruby}
12
+ s.description = %q{An RSpec extension that adds support for approvals. Based on the idea of the Golden Master.}
13
+
14
+ s.rubyforge_project = "rspec-approvals"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency 'rspec', '~> 2.6'
22
+ end
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+
3
+ describe Approvals::Approval do
4
+ let(:description) { 'spec/approvals/fairy_dust_and_unicorns' }
5
+ let(:example) { stub('example', :full_description => 'fairy dust and unicorns').as_null_object }
6
+
7
+ describe "#normalize" do
8
+ it "downcases" do
9
+ Approvals::Approval.normalize("KTHXBYE").should eq("kthxbye")
10
+ end
11
+
12
+ it "replaces spaces with underscores" do
13
+ Approvals::Approval.normalize("the spec").should eq("the_spec")
14
+ end
15
+
16
+ it "leaves numbers alone" do
17
+ Approvals::Approval.normalize('a 2009 party').should eq("a_2009_party")
18
+ end
19
+
20
+ it "deletes funky characters" do
21
+ Approvals::Approval.normalize('the !@\#$%^&*(){}+| name').should eq("the_name")
22
+ end
23
+
24
+ it "collapses spaces before replacing with underscores" do
25
+ Approvals::Approval.normalize('omf g').should eq('omf_g')
26
+ end
27
+
28
+ it "deletes all sorts of spaces" do
29
+ name = <<-FUNKY_NAME
30
+
31
+ The::Class \t \r\n \fname
32
+ FUNKY_NAME
33
+ Approvals::Approval.normalize(name).should eq('the_class_name')
34
+ end
35
+ end
36
+
37
+ it "knows the approved_path" do
38
+ approval = Approvals::Approval.new(example)
39
+ approval.approved_path.should eq("#{description}.approved.txt")
40
+ end
41
+
42
+ it "knows the received path" do
43
+ approval = Approvals::Approval.new(example)
44
+ approval.received_path.should eq("#{description}.received.txt")
45
+ end
46
+
47
+ it "can set a location" do
48
+ Dir.stub(:pwd => 'the/path')
49
+ approval = Approvals::Approval.new(example)
50
+ approval.location = ['the/path/to/my/heart:9372 <is through my stomach>', 'bla bla bla']
51
+ approval.location.should eq(['./to/my/heart:9372 <is through my stomach>'])
52
+ end
53
+
54
+ context "approvals" do
55
+ before :each do
56
+ @approved_file = "#{description}.approved.txt"
57
+ @received_file = "#{description}.received.txt"
58
+ end
59
+
60
+ after :each do
61
+ File.delete(@approved_file) if File.exists?(@approved_file)
62
+ File.delete(@received_file) if File.exists?(@received_file)
63
+ end
64
+
65
+ describe "on the filesystem" do
66
+
67
+ it "writes the approved file if it doesn't exist" do
68
+ File.delete(@approved_file) if File.exists?(@approved_file)
69
+
70
+ Approvals::Approval.new(example)
71
+
72
+ File.exists?(@approved_file).should be_true
73
+ File.read(@approved_file).should eq('')
74
+ end
75
+
76
+ it "doesn't overwrite an existing approved file" do
77
+ File.open(@approved_file, 'w') do |f|
78
+ f.write "this doesn't get deleted"
79
+ end
80
+
81
+ Approvals::Approval.new(example)
82
+
83
+ File.exists?(@approved_file).should be_true
84
+ File.read(@approved_file).should eq("this doesn't get deleted")
85
+ end
86
+
87
+ it "writes the received contents to file" do
88
+ approval = Approvals::Approval.new(example, 'oooh, shiney!')
89
+
90
+ File.exists?(@received_file).should be_true
91
+ File.read(@received_file).should eq("oooh, shiney!")
92
+ end
93
+ end
94
+
95
+ describe "verification" do
96
+
97
+ context "with a match" do
98
+ before :each do
99
+ @approval = Approvals::Approval.new(example, 'xyz')
100
+ @approval.write(:approved, 'xyz')
101
+ end
102
+
103
+ it "does not raise an error" do
104
+ lambda { @approval.verify }.should_not raise_error(RSpec::Approvals::ReceivedDiffersError)
105
+ end
106
+
107
+ it "does not leave a received file" do
108
+ lambda { @approval.verify }.call
109
+ File.exists?(@approval.received_path).should be_false
110
+ end
111
+ end
112
+
113
+ context "with a mismatch" do
114
+ before :each do
115
+ @approval = Approvals::Approval.new(example, 'xyz')
116
+ @approval.write(:approved, 'abc')
117
+ end
118
+
119
+ it "raises an error" do
120
+ lambda { @approval.verify }.should raise_error(RSpec::Approvals::ReceivedDiffersError)
121
+ end
122
+
123
+ it "leaves a received file" do
124
+ begin
125
+ @approval.verify
126
+ rescue RSpec::Approvals::ReceivedDiffersError => e
127
+ # we want to land here and then move on
128
+ end
129
+ File.exists?(@approval.received_path).should be_true
130
+ end
131
+ end
132
+ end
133
+
134
+ it "fails magnificently" do
135
+ approval = Approvals::Approval.new(example, 'xyz')
136
+ message = <<-FAILURE_MESSAGE
137
+
138
+ Approval Failure:
139
+
140
+ The received contents did not match the approved contents.
141
+
142
+ Inspect the differences in the following files:
143
+ #{approval.received_path}
144
+ #{approval.approved_path}
145
+
146
+ If you like what you see in the *.received.txt file, you can approve it
147
+ like so:
148
+
149
+ mv #{approval.received_path} #{approval.approved_path}
150
+
151
+
152
+ FAILURE_MESSAGE
153
+
154
+ approval.failure_message.should eq(message)
155
+ end
156
+ end
157
+ end
@@ -0,0 +1 @@
1
+ {:universe=>{:side=>:dark, :other_side=>:light}, :force=>true}
@@ -0,0 +1 @@
1
+ We have, I fear, confused power with greatness.
@@ -0,0 +1 @@
1
+ ["abc", 123, ["cheese", "burger", "ribs", "steak", "bacon"]]
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe Approvals do
4
+
5
+ it "defaults the output dir to spec/approvals" do
6
+ RSpec.configuration.approvals_path.should == 'spec/approvals'
7
+ end
8
+
9
+ describe "initializing approval directory" do
10
+ it "does nothing if directory exists" do
11
+ RSpec.configuration.stub(:approvals_path).and_return 'xyz'
12
+ Dir.stub(:exists?).and_return true
13
+ FileUtils.should_not_receive(:makedirs).with 'xyz'
14
+
15
+ Approvals.initialize_approvals_path
16
+ end
17
+
18
+ it "creates directory if it is missing" do
19
+ RSpec.configuration.stub(:approvals_path).and_return 'abc'
20
+ Dir.stub(:exists?).and_return false
21
+ FileUtils.should_receive(:makedirs).with('abc')
22
+
23
+ Approvals.initialize_approvals_path
24
+ end
25
+ end
26
+
27
+ it "needs to be able to run with :filtered => true"
28
+
29
+ approve "a string" do
30
+ "We have, I fear, confused power with greatness."
31
+ end
32
+
33
+ approve "a hash" do
34
+ {
35
+ :universe => {
36
+ :side => :dark,
37
+ :other_side => :light
38
+ },
39
+ :force => true
40
+ }
41
+ end
42
+
43
+ approve "an array" do
44
+ [
45
+ "abc",
46
+ 123,
47
+ %w(cheese burger ribs steak bacon)
48
+ ]
49
+ end
50
+
51
+ approve "a complex object" do
52
+ hello = Object.new
53
+ def hello.to_s
54
+ "Hello, World!"
55
+ end
56
+
57
+ def hello.inspect
58
+ "<HelloWorld id:#{object_id}>"
59
+ end
60
+
61
+ hello # => output matches hello.to_s
62
+ end
63
+
64
+ end
@@ -0,0 +1,2 @@
1
+ require 'rspec/approvals'
2
+ include RSpec
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-approvals
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Katrina Owen
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-07-11 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 2
30
+ - 6
31
+ version: "2.6"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ description: An RSpec extension that adds support for approvals. Based on the idea of the Golden Master.
35
+ email:
36
+ - katrina.owen@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - .gitignore
45
+ - Gemfile
46
+ - License.txt
47
+ - README.md
48
+ - Rakefile
49
+ - lib/rspec/approvals.rb
50
+ - lib/rspec/approvals/approval.rb
51
+ - lib/rspec/approvals/dsl.rb
52
+ - lib/rspec/approvals/formatters/opendiff_formatter.rb
53
+ - lib/rspec/approvals/version.rb
54
+ - rspec-approvals.gemspec
55
+ - spec/approval_spec.rb
56
+ - spec/approvals/rspec_approvals_a_complex_object.approved.txt
57
+ - spec/approvals/rspec_approvals_a_hash.approved.txt
58
+ - spec/approvals/rspec_approvals_a_string.approved.txt
59
+ - spec/approvals/rspec_approvals_an_array.approved.txt
60
+ - spec/approvals_spec.rb
61
+ - spec/spec_helper.rb
62
+ has_rdoc: true
63
+ homepage: ""
64
+ licenses: []
65
+
66
+ post_install_message:
67
+ rdoc_options: []
68
+
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ segments:
85
+ - 0
86
+ version: "0"
87
+ requirements: []
88
+
89
+ rubyforge_project: rspec-approvals
90
+ rubygems_version: 1.3.7
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: Approval Tests for Ruby
94
+ test_files: []
95
+