git-approvals 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +48 -4
- data/lib/git/approvals/approval.rb +11 -38
- data/lib/git/approvals/plain_formatter.rb +19 -0
- data/lib/git/approvals/utils.rb +18 -0
- data/lib/git/approvals/version.rb +1 -1
- data/lib/git/approvals.rb +1 -0
- data/spec/fixtures/input +1 -0
- data/spec/fixtures/input.css +2 -0
- data/spec/fixtures/input.js +2 -0
- data/spec/fixtures/input.json +1 -0
- data/spec/fixtures/output +1 -0
- data/spec/fixtures/{asset.css → output.css} +1 -0
- data/spec/fixtures/{asset.js → output.js} +0 -0
- data/spec/fixtures/{hash.json → output.json} +0 -0
- data/spec/fixtures/output.txt +8 -0
- data/spec/git/approvals/approval_spec.rb +13 -48
- data/spec/git/approvals/formatter_spec.rb +29 -0
- data/spec/git/approvals/rspec_spec/rspec_integration/verify/fails_when_changed +1 -0
- data/spec/git/approvals/rspec_spec/rspec_integration/verify/passes_when_unchanged +1 -0
- data/spec/git/approvals/rspec_spec.rb +1 -1
- data/spec/git/approvals/utils_spec.rb +16 -0
- metadata +27 -18
- data/spec/fixtures/array.txt +0 -5
- data/spec/fixtures/hash.txt +0 -4
- data/spec/fixtures/string.txt +0 -1
- data/spec/git/approvals/rspec_spec/rspec_integration/verify/fails_when_changed.txt +0 -1
- data/spec/git/approvals/rspec_spec/rspec_integration/verify/passes_when_unchanged.txt +0 -1
data/README.md
CHANGED
@@ -16,14 +16,31 @@ Or install it yourself as:
|
|
16
16
|
|
17
17
|
$ gem install git-approvals
|
18
18
|
|
19
|
+
## Background
|
20
|
+
|
21
|
+
[Approval tests][approvaltests] are a testing device popularized by Llewellyn Falco. The basic premise of an approval test is that the subject under test is captured and written to a file, thereby "approving" it. The test assertion is literally a diff of the approved file (expected) with the subject under test (actual). If there is a diff, the test fails because the result does not match the approved file.
|
22
|
+
|
23
|
+
For a more comprehensive background and use cases, see [this blog post][blog] I've written on the subject of approval tests.
|
24
|
+
|
25
|
+
**Git::Approvals** leverages `git` as a backend to make approval testing simple and the workflow familiar. Your project doesn't necessarily have to be version controlled by `git` to work, but the approved files must. This library assumes a basic working knowledge of how to use `git`.
|
26
|
+
|
19
27
|
## Usage
|
20
28
|
|
21
|
-
|
29
|
+
### Standalone
|
30
|
+
|
31
|
+
You can use approval tests manually in Ruby if you wish. Suppose you have a file named `foo/bar.txt` that contains the string "baz". The `Approval#diff` method takes a string and a block. It will shell out to `git` and will diff the given string with the file and will call the block if there is a diff or an error occurs.
|
32
|
+
|
33
|
+
``` ruby
|
34
|
+
approval = Git::Approvals::Approval.new 'foo/bar.txt'
|
35
|
+
approval.diff( "baz" ){ |err| puts "fail" } # nothing
|
36
|
+
approval.diff( "qux" ){ |err| puts "fail" } # "fail"
|
37
|
+
```
|
38
|
+
|
39
|
+
The constructor method also accepts a hash of options, which are explained below.
|
22
40
|
|
23
|
-
###
|
41
|
+
### RSpec Integration
|
24
42
|
|
25
|
-
In your spec_helper.rb or wherever, simply `require 'rspec/approvals'`.
|
26
|
-
Now you can use the `verify` expectation in your specs:
|
43
|
+
In your `spec_helper.rb` or wherever, simply `require 'rspec/approvals'`. You can now use the `verify` expectation in your specs:
|
27
44
|
|
28
45
|
`spec/foo_spec.rb`
|
29
46
|
|
@@ -48,6 +65,26 @@ Another test run shows that the test now passes. On the next test run, the
|
|
48
65
|
same file will be written out. If git says the file has changed, the
|
49
66
|
test will fail.
|
50
67
|
|
68
|
+
The `verify` method accepts the same options hash as the Approval constructor.
|
69
|
+
|
70
|
+
### Options
|
71
|
+
|
72
|
+
The following options are supported:
|
73
|
+
|
74
|
+
- `:format`: specifies the format to use when writing out the approved file. The format is inferred from the extension of the approved file, but if you specify it here, it will change the extension to match. This option accepts any extension that is registered with [tilt][tilt].
|
75
|
+
- `:filename`: specifies the filename of the approved file.
|
76
|
+
|
77
|
+
### Formatters
|
78
|
+
|
79
|
+
**Git::Approvals** uses [tilt][tilt] to write out approved files, so any template format that tilt supports can be used. **Git::Approvals** adds the following formats in addition to the standard tilt templates:
|
80
|
+
|
81
|
+
- `txt` writes out a ruby object using [awesome_print][awesomeprint].
|
82
|
+
- `json` pretty-prints a json string using the standard JSON library.
|
83
|
+
- `js` pretty-prints JavaScript source using [uglifier][uglifier].
|
84
|
+
- `css` pretty-prints CSS source using [SASS][sass].
|
85
|
+
|
86
|
+
The format will be determined by checking the `:format` key of the options hash, then the extension of the approved filename, and finally will default to `txt`.
|
87
|
+
|
51
88
|
## Contributing
|
52
89
|
|
53
90
|
1. Fork it
|
@@ -55,3 +92,10 @@ test will fail.
|
|
55
92
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
56
93
|
4. Push to the branch (`git push origin my-new-feature`)
|
57
94
|
5. Create new Pull Request
|
95
|
+
|
96
|
+
[blog]: http://jeremyruppel.tumblr.com/post/52734828127/committed-for-your-approval
|
97
|
+
[tilt]: https://github.com/rtomayko/tilt
|
98
|
+
[approvaltests]: http://approvaltests.sourceforge.net/
|
99
|
+
[awesomeprint]: https://github.com/michaeldv/awesome_print
|
100
|
+
[uglifier]: https://github.com/lautis/uglifier
|
101
|
+
[sass]: https://github.com/nex3/sass
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'open3'
|
2
|
-
require 'pathname'
|
3
2
|
require 'tilt'
|
4
3
|
|
5
4
|
module Git
|
@@ -7,40 +6,13 @@ module Git
|
|
7
6
|
|
8
7
|
##
|
9
8
|
#
|
10
|
-
class Approval
|
9
|
+
class Approval
|
10
|
+
include Git::Approvals::Utils
|
11
11
|
|
12
12
|
def initialize( path, options={} ) # :nodoc:
|
13
|
-
@
|
14
|
-
|
15
|
-
path = Pathname( path )
|
16
|
-
path = munge_format( path )
|
17
|
-
path = munge_filename( path )
|
18
|
-
|
19
|
-
super path.to_path
|
20
|
-
end
|
21
|
-
attr_reader :options
|
22
|
-
|
23
|
-
##
|
24
|
-
# Makes sure the given pathname has an extension.
|
25
|
-
def munge_format( path )
|
26
|
-
ext = [ options[ :format ], path.extname, 'txt' ].detect do |arg|
|
27
|
-
!arg.nil? && !arg.empty?
|
28
|
-
end
|
29
|
-
ext = ext.to_s
|
30
|
-
ext = '.' + ext unless ext.start_with?( '.' )
|
31
|
-
path.sub_ext ext
|
32
|
-
end
|
33
|
-
|
34
|
-
##
|
35
|
-
# Makes sure the given pathname has the correct filename.
|
36
|
-
def munge_filename( path )
|
37
|
-
if filename = options[ :filename ]
|
38
|
-
dir, base = path.split
|
39
|
-
dir.join( filename + base.extname )
|
40
|
-
else
|
41
|
-
path
|
42
|
-
end
|
13
|
+
@path = transform_filename( path, options )
|
43
14
|
end
|
15
|
+
attr_reader :path
|
44
16
|
|
45
17
|
##
|
46
18
|
# Diffs the given string with this approval file. If the
|
@@ -49,20 +21,20 @@ module Git
|
|
49
21
|
# called if the diff fails, meaning there are differences.
|
50
22
|
def diff( string, &block )
|
51
23
|
# Make sure the directory of the file exists.
|
52
|
-
dirname
|
24
|
+
FileUtils.mkdir_p File.dirname( path )
|
53
25
|
|
54
26
|
# Write the new string to the file.
|
55
|
-
open 'w' do |f|
|
56
|
-
f << Tilt.new(
|
27
|
+
File.open path, 'w' do |f|
|
28
|
+
f << Tilt.new( path ).render( string )
|
57
29
|
end
|
58
30
|
|
59
31
|
# If the file hasn't been checked in, raise an error.
|
60
|
-
sh "git ls-files #{
|
61
|
-
raise Errno::ENOENT,
|
32
|
+
sh "git ls-files #{path} --error-unmatch" do |err|
|
33
|
+
raise Errno::ENOENT, path
|
62
34
|
end
|
63
35
|
|
64
36
|
# If the file has changed, call the block.
|
65
|
-
sh "git diff --exit-code #{
|
37
|
+
sh "git diff --exit-code #{path}" do |err|
|
66
38
|
block.call err
|
67
39
|
end
|
68
40
|
end
|
@@ -79,6 +51,7 @@ module Git
|
|
79
51
|
|
80
52
|
##
|
81
53
|
# Register all formatters as tilt templates.
|
54
|
+
Tilt.register PlainFormatter, ''
|
82
55
|
Tilt.register AwesomePrintFormatter, 'txt'
|
83
56
|
Tilt.register JSONFormatter, 'json'
|
84
57
|
Tilt.register UglifierFormatter, 'js'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'tilt'
|
2
|
+
|
3
|
+
module Git
|
4
|
+
module Approvals
|
5
|
+
class PlainFormatter < Tilt::Template
|
6
|
+
|
7
|
+
def self.engine_initialized?
|
8
|
+
true
|
9
|
+
end
|
10
|
+
|
11
|
+
def prepare
|
12
|
+
end
|
13
|
+
|
14
|
+
def evaluate( context, locals, &block )
|
15
|
+
context.to_s
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/git/approvals/utils.rb
CHANGED
@@ -12,6 +12,24 @@ module Git
|
|
12
12
|
.downcase
|
13
13
|
end
|
14
14
|
module_function :filenamify
|
15
|
+
|
16
|
+
##
|
17
|
+
#
|
18
|
+
def transform_filename( str, options={} )
|
19
|
+
|
20
|
+
if opt = options.delete( :format )
|
21
|
+
str.chomp! File.extname( str )
|
22
|
+
str << '.' << opt.to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
if opt = options.delete( :filename )
|
26
|
+
base = File.basename( str, File.extname( str ) )
|
27
|
+
str.sub! /#{base}(?!\/)/, opt
|
28
|
+
end
|
29
|
+
|
30
|
+
str
|
31
|
+
end
|
32
|
+
module_function :transform_filename
|
15
33
|
end
|
16
34
|
end
|
17
35
|
end
|
data/lib/git/approvals.rb
CHANGED
@@ -5,6 +5,7 @@ module Git
|
|
5
5
|
autoload :Approval, 'git/approvals/approval'
|
6
6
|
autoload :AwesomePrintFormatter, 'git/approvals/awesome_print_formatter.rb'
|
7
7
|
autoload :JSONFormatter, 'git/approvals/json_formatter.rb'
|
8
|
+
autoload :PlainFormatter, 'git/approvals/plain_formatter.rb'
|
8
9
|
autoload :SassFormatter, 'git/approvals/sass_formatter.rb'
|
9
10
|
autoload :UglifierFormatter, 'git/approvals/uglifier_formatter.rb'
|
10
11
|
autoload :Utils, 'git/approvals/utils'
|
data/spec/fixtures/input
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Plain text.
|
@@ -0,0 +1 @@
|
|
1
|
+
{"foo":"bar","baz":"quux"}
|
@@ -0,0 +1 @@
|
|
1
|
+
Plain text.
|
File without changes
|
File without changes
|
@@ -3,24 +3,13 @@ require 'spec_helper'
|
|
3
3
|
describe Git::Approvals::Approval do
|
4
4
|
|
5
5
|
describe '#initialize' do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
its( :options ){ should == { } }
|
10
|
-
end
|
11
|
-
|
12
|
-
describe '#options' do
|
13
|
-
describe ':format' do
|
14
|
-
it 'replaces the extension' do
|
15
|
-
subject = described_class.new 'foo/bar.txt', :format => :json
|
16
|
-
subject.to_path.should == 'foo/bar.json'
|
17
|
-
end
|
6
|
+
context 'straight up' do
|
7
|
+
subject { described_class.new 'foo/bar.txt' }
|
8
|
+
its( :path ){ should == 'foo/bar.txt' }
|
18
9
|
end
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
subject.to_path.should == 'foo/baz.txt'
|
23
|
-
end
|
10
|
+
context 'with options' do
|
11
|
+
subject { described_class.new 'foo/bar.txt', :format => :json, :filename => 'baz' }
|
12
|
+
its( :path ){ should == 'foo/baz.json' }
|
24
13
|
end
|
25
14
|
end
|
26
15
|
|
@@ -56,36 +45,12 @@ describe Git::Approvals::Approval do
|
|
56
45
|
end
|
57
46
|
end
|
58
47
|
|
59
|
-
describe '
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
it
|
65
|
-
|
66
|
-
approval.diff( [ :foo, :bar, :baz ] ){ |diff| fail diff }
|
67
|
-
end
|
68
|
-
it 'formats hashes' do
|
69
|
-
approval = described_class.new './spec/fixtures/hash.txt'
|
70
|
-
approval.diff( { :foo => 'bar', :quux => 'bar' } ){ |diff| fail diff }
|
71
|
-
end
|
72
|
-
it 'formats json' do
|
73
|
-
approval = described_class.new './spec/fixtures/hash.json'
|
74
|
-
approval.diff( '{"foo":"bar","baz":"quux"}' ){ |diff| fail diff }
|
75
|
-
end
|
76
|
-
it 'formats javascript' do
|
77
|
-
approval = described_class.new './spec/fixtures/asset.js'
|
78
|
-
approval.diff( <<-EOS ){ |diff| fail diff }
|
79
|
-
// Comments are preserved
|
80
|
-
(function(){return {status:"IT WERKS"};})();
|
81
|
-
EOS
|
82
|
-
end
|
83
|
-
it 'formats css' do
|
84
|
-
approval = described_class.new './spec/fixtures/asset.css'
|
85
|
-
approval.diff( <<-EOS ){ |diff| fail diff }
|
86
|
-
// Comments are preserved
|
87
|
-
html,body{font-family:"Helvetica Neue";}
|
88
|
-
EOS
|
89
|
-
end
|
48
|
+
describe 'Tilt mappings' do
|
49
|
+
subject { Tilt.mappings }
|
50
|
+
it { should include( '' => [ Git::Approvals::PlainFormatter ] ) }
|
51
|
+
it { should include( 'txt' => [ Git::Approvals::AwesomePrintFormatter ] ) }
|
52
|
+
it { should include( 'css' => [ Git::Approvals::SassFormatter ] ) }
|
53
|
+
it { should include( 'json' => [ Git::Approvals::JSONFormatter ] ) }
|
54
|
+
it { should include( 'js' => [ Git::Approvals::UglifierFormatter ] ) }
|
90
55
|
end
|
91
56
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples 'formatter' do |ext|
|
4
|
+
let( :input ){ File.read "./spec/fixtures/input#{ext}" }
|
5
|
+
let( :output ){ "./spec/fixtures/output#{ext}" }
|
6
|
+
|
7
|
+
it 'formats the input appropriately' do
|
8
|
+
approval = Git::Approvals::Approval.new output
|
9
|
+
approval.diff( input ){ |diff| fail diff }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Git::Approvals::PlainFormatter do
|
14
|
+
include_examples 'formatter', ''
|
15
|
+
end
|
16
|
+
describe Git::Approvals::JSONFormatter do
|
17
|
+
include_examples 'formatter', '.json'
|
18
|
+
end
|
19
|
+
describe Git::Approvals::UglifierFormatter do
|
20
|
+
include_examples 'formatter', '.js'
|
21
|
+
end
|
22
|
+
describe Git::Approvals::SassFormatter do
|
23
|
+
include_examples 'formatter', '.css'
|
24
|
+
end
|
25
|
+
describe Git::Approvals::AwesomePrintFormatter do
|
26
|
+
include_examples 'formatter', '.txt' do
|
27
|
+
let( :input ){ { :foo => 'bar', :baz => [ 1, 2, 3 ] } }
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
IT WERKS
|
@@ -0,0 +1 @@
|
|
1
|
+
IT WERKS
|
@@ -18,7 +18,7 @@ describe 'RSpec integration' do
|
|
18
18
|
after { `git checkout #{approval_directory}` }
|
19
19
|
|
20
20
|
it 'passes when unchanged' do
|
21
|
-
verify { 'IT WERKS' }
|
21
|
+
expect { verify { 'IT WERKS' } }.not_to raise_error
|
22
22
|
end
|
23
23
|
it 'fails when changed' do
|
24
24
|
expect { verify { 'IT BROKE' } }.to raise_error( RSpec::Expectations::ExpectationNotMetError )
|
@@ -15,4 +15,20 @@ describe Git::Approvals::Utils do
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
18
|
+
|
19
|
+
describe '#transform_filename' do
|
20
|
+
{
|
21
|
+
[ 'foo' ] => 'foo',
|
22
|
+
[ 'foo', { :format => :json } ] => 'foo.json',
|
23
|
+
[ 'foo', { :format => 'json' } ] => 'foo.json',
|
24
|
+
[ 'foo.txt', { :format => :json } ] => 'foo.json',
|
25
|
+
[ 'foo', { :filename => 'bar' } ] => 'bar',
|
26
|
+
[ 'foo.txt', { :filename => 'bar' } ] => 'bar.txt',
|
27
|
+
[ 'foo/foo.txt', { :filename => 'bar' } ] => 'foo/bar.txt'
|
28
|
+
}.each do |args, filename|
|
29
|
+
example 'with %s' % [ args.inspect ] do
|
30
|
+
described_class.transform_filename( *args ).should == filename
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
18
34
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git-approvals
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-06-
|
12
|
+
date: 2013-06-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: tilt
|
@@ -194,22 +194,27 @@ files:
|
|
194
194
|
- lib/git/approvals/approval.rb
|
195
195
|
- lib/git/approvals/awesome_print_formatter.rb
|
196
196
|
- lib/git/approvals/json_formatter.rb
|
197
|
+
- lib/git/approvals/plain_formatter.rb
|
197
198
|
- lib/git/approvals/rspec.rb
|
198
199
|
- lib/git/approvals/sass_formatter.rb
|
199
200
|
- lib/git/approvals/uglifier_formatter.rb
|
200
201
|
- lib/git/approvals/utils.rb
|
201
202
|
- lib/git/approvals/version.rb
|
202
203
|
- lib/rspec/approvals.rb
|
203
|
-
- spec/fixtures/
|
204
|
-
- spec/fixtures/
|
205
|
-
- spec/fixtures/
|
206
|
-
- spec/fixtures/
|
207
|
-
- spec/fixtures/
|
208
|
-
- spec/fixtures/
|
204
|
+
- spec/fixtures/input
|
205
|
+
- spec/fixtures/input.css
|
206
|
+
- spec/fixtures/input.js
|
207
|
+
- spec/fixtures/input.json
|
208
|
+
- spec/fixtures/output
|
209
|
+
- spec/fixtures/output.css
|
210
|
+
- spec/fixtures/output.js
|
211
|
+
- spec/fixtures/output.json
|
212
|
+
- spec/fixtures/output.txt
|
209
213
|
- spec/git/approvals/approval_spec.rb
|
214
|
+
- spec/git/approvals/formatter_spec.rb
|
210
215
|
- spec/git/approvals/rspec_spec.rb
|
211
|
-
- spec/git/approvals/rspec_spec/rspec_integration/verify/fails_when_changed
|
212
|
-
- spec/git/approvals/rspec_spec/rspec_integration/verify/passes_when_unchanged
|
216
|
+
- spec/git/approvals/rspec_spec/rspec_integration/verify/fails_when_changed
|
217
|
+
- spec/git/approvals/rspec_spec/rspec_integration/verify/passes_when_unchanged
|
213
218
|
- spec/git/approvals/utils_spec.rb
|
214
219
|
- spec/spec_helper.rb
|
215
220
|
homepage: https://github.com/jeremyruppel/git-approvals
|
@@ -238,16 +243,20 @@ signing_key:
|
|
238
243
|
specification_version: 3
|
239
244
|
summary: Simple git-powered approval tests.
|
240
245
|
test_files:
|
241
|
-
- spec/fixtures/
|
242
|
-
- spec/fixtures/
|
243
|
-
- spec/fixtures/
|
244
|
-
- spec/fixtures/
|
245
|
-
- spec/fixtures/
|
246
|
-
- spec/fixtures/
|
246
|
+
- spec/fixtures/input
|
247
|
+
- spec/fixtures/input.css
|
248
|
+
- spec/fixtures/input.js
|
249
|
+
- spec/fixtures/input.json
|
250
|
+
- spec/fixtures/output
|
251
|
+
- spec/fixtures/output.css
|
252
|
+
- spec/fixtures/output.js
|
253
|
+
- spec/fixtures/output.json
|
254
|
+
- spec/fixtures/output.txt
|
247
255
|
- spec/git/approvals/approval_spec.rb
|
256
|
+
- spec/git/approvals/formatter_spec.rb
|
248
257
|
- spec/git/approvals/rspec_spec.rb
|
249
|
-
- spec/git/approvals/rspec_spec/rspec_integration/verify/fails_when_changed
|
250
|
-
- spec/git/approvals/rspec_spec/rspec_integration/verify/passes_when_unchanged
|
258
|
+
- spec/git/approvals/rspec_spec/rspec_integration/verify/fails_when_changed
|
259
|
+
- spec/git/approvals/rspec_spec/rspec_integration/verify/passes_when_unchanged
|
251
260
|
- spec/git/approvals/utils_spec.rb
|
252
261
|
- spec/spec_helper.rb
|
253
262
|
has_rdoc:
|
data/spec/fixtures/array.txt
DELETED
data/spec/fixtures/hash.txt
DELETED
data/spec/fixtures/string.txt
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
"IT WERKS"
|
@@ -1 +0,0 @@
|
|
1
|
-
"IT WERKS"
|
@@ -1 +0,0 @@
|
|
1
|
-
"IT WERKS"
|