br-approvals 0.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +15 -0
  4. data/CHANGELOG.md +71 -0
  5. data/Gemfile +6 -0
  6. data/License.txt +22 -0
  7. data/README.md +263 -0
  8. data/Rakefile +6 -0
  9. data/approvals.gemspec +29 -0
  10. data/bin/approvals +8 -0
  11. data/ext/mkrf_conf.rb +22 -0
  12. data/lib/approvals.rb +33 -0
  13. data/lib/approvals/approval.rb +144 -0
  14. data/lib/approvals/cli.rb +36 -0
  15. data/lib/approvals/configuration.rb +29 -0
  16. data/lib/approvals/dotfile.rb +33 -0
  17. data/lib/approvals/dsl.rb +7 -0
  18. data/lib/approvals/error.rb +21 -0
  19. data/lib/approvals/executable.rb +18 -0
  20. data/lib/approvals/extensions/rspec.rb +13 -0
  21. data/lib/approvals/extensions/rspec/dsl.rb +44 -0
  22. data/lib/approvals/filter.rb +45 -0
  23. data/lib/approvals/namers/default_namer.rb +20 -0
  24. data/lib/approvals/namers/directory_namer.rb +30 -0
  25. data/lib/approvals/namers/rspec_namer.rb +32 -0
  26. data/lib/approvals/reporters.rb +14 -0
  27. data/lib/approvals/reporters/diff_reporter/diffmerge_reporter.rb +18 -0
  28. data/lib/approvals/reporters/diff_reporter/opendiff_reporter.rb +18 -0
  29. data/lib/approvals/reporters/diff_reporter/tortoisediff_reporter.rb +18 -0
  30. data/lib/approvals/reporters/diff_reporter/vimdiff_reporter.rb +18 -0
  31. data/lib/approvals/reporters/filelauncher_reporter.rb +18 -0
  32. data/lib/approvals/reporters/first_working_reporter.rb +21 -0
  33. data/lib/approvals/reporters/image_reporter.rb +13 -0
  34. data/lib/approvals/reporters/image_reporter/html_image_reporter.rb +35 -0
  35. data/lib/approvals/reporters/image_reporter/image_magick_reporter.rb +20 -0
  36. data/lib/approvals/reporters/launcher.rb +49 -0
  37. data/lib/approvals/reporters/reporter.rb +30 -0
  38. data/lib/approvals/rspec.rb +4 -0
  39. data/lib/approvals/scrubber.rb +43 -0
  40. data/lib/approvals/system_command.rb +13 -0
  41. data/lib/approvals/version.rb +3 -0
  42. data/lib/approvals/writer.rb +39 -0
  43. data/lib/approvals/writers/array_writer.rb +17 -0
  44. data/lib/approvals/writers/binary_writer.rb +49 -0
  45. data/lib/approvals/writers/hash_writer.rb +20 -0
  46. data/lib/approvals/writers/html_writer.rb +15 -0
  47. data/lib/approvals/writers/json_writer.rb +31 -0
  48. data/lib/approvals/writers/text_writer.rb +21 -0
  49. data/lib/approvals/writers/xml_writer.rb +15 -0
  50. data/spec/approvals_spec.rb +172 -0
  51. data/spec/configuration_spec.rb +28 -0
  52. data/spec/dotfile_spec.rb +22 -0
  53. data/spec/executable_spec.rb +17 -0
  54. data/spec/extensions/rspec_approvals_spec.rb +105 -0
  55. data/spec/filter_spec.rb +123 -0
  56. data/spec/fixtures/approvals/approvals_custom_writer_verifies_a_complex_object.approved.txt +1 -0
  57. data/spec/fixtures/approvals/approvals_passes_approved_files_through_erb.approved.txt +1 -0
  58. data/spec/fixtures/approvals/approvals_passes_the_received_files_through_erb.approved.txt +1 -0
  59. data/spec/fixtures/approvals/approvals_supports_excluded_keys_option_also_supports_an_array_of_hashes.approved.json +10 -0
  60. data/spec/fixtures/approvals/approvals_supports_excluded_keys_option_supports_the_array_writer.approved.txt +1 -0
  61. data/spec/fixtures/approvals/approvals_supports_excluded_keys_option_supports_the_hash_writer.approved.txt +1 -0
  62. data/spec/fixtures/approvals/approvals_supports_excluded_keys_option_verifies_json_with_excluded_keys.approved.json +8 -0
  63. data/spec/fixtures/approvals/approvals_verifies_a_complex_object.approved.txt +1 -0
  64. data/spec/fixtures/approvals/approvals_verifies_a_hash.approved.txt +6 -0
  65. data/spec/fixtures/approvals/approvals_verifies_a_malformed_html_fragment.approved.html +11 -0
  66. data/spec/fixtures/approvals/approvals_verifies_a_string.approved.txt +1 -0
  67. data/spec/fixtures/approvals/approvals_verifies_an_array.approved.txt +4 -0
  68. data/spec/fixtures/approvals/approvals_verifies_an_array_as_json_when_format_is_set_to_json.approved.json +10 -0
  69. data/spec/fixtures/approvals/approvals_verifies_an_executable.approved.txt +1 -0
  70. data/spec/fixtures/approvals/approvals_verifies_html.approved.html +11 -0
  71. data/spec/fixtures/approvals/approvals_verifies_json.approved.json +7 -0
  72. data/spec/fixtures/approvals/approvals_verifies_json_and_is_newline_agnostic.approved.json +7 -0
  73. data/spec/fixtures/approvals/approvals_verifies_xml.approved.xml +9 -0
  74. data/spec/fixtures/approvals/verifications_a_string.approved.txt +1 -0
  75. data/spec/fixtures/approvals/verifies_a_complex_object.approved.txt +1 -0
  76. data/spec/fixtures/approvals/verifies_a_failure.approved.txt +1 -0
  77. data/spec/fixtures/approvals/verifies_a_failure_diff.approved.txt +1 -0
  78. data/spec/fixtures/approvals/verifies_a_string.approved.txt +1 -0
  79. data/spec/fixtures/approvals/verifies_an_array.approved.txt +4 -0
  80. data/spec/fixtures/approvals/verifies_an_executable.approved.txt +1 -0
  81. data/spec/fixtures/approvals/verifies_directory/a_complex_object.approved.txt +1 -0
  82. data/spec/fixtures/approvals/verifies_directory/a_failure.approved.txt +0 -0
  83. data/spec/fixtures/approvals/verifies_directory/a_failure_diff.approved.txt +0 -0
  84. data/spec/fixtures/approvals/verifies_directory/a_string.approved.txt +1 -0
  85. data/spec/fixtures/approvals/verifies_directory/an_array.approved.txt +4 -0
  86. data/spec/fixtures/approvals/verifies_directory/an_executable.approved.txt +1 -0
  87. data/spec/fixtures/approvals/verifies_directory/html.approved.html +11 -0
  88. data/spec/fixtures/approvals/verifies_directory/json.approved.json +7 -0
  89. data/spec/fixtures/approvals/verifies_directory/xml.approved.xml +9 -0
  90. data/spec/fixtures/approvals/verifies_html.approved.html +11 -0
  91. data/spec/fixtures/approvals/verifies_json.approved.json +7 -0
  92. data/spec/fixtures/approvals/verifies_xml.approved.xml +9 -0
  93. data/spec/fixtures/one.png +0 -0
  94. data/spec/fixtures/one.txt +1 -0
  95. data/spec/fixtures/two.png +0 -0
  96. data/spec/fixtures/two.txt +1 -0
  97. data/spec/namers/default_namer_spec.rb +37 -0
  98. data/spec/namers/directory_namer_spec.rb +31 -0
  99. data/spec/namers/rspec_namer_spec.rb +30 -0
  100. data/spec/namers_spec.rb +16 -0
  101. data/spec/reporters/first_working_reporter_spec.rb +30 -0
  102. data/spec/reporters/html_image_reporter_spec.rb +22 -0
  103. data/spec/reporters/image_magick_reporter_spec.rb +16 -0
  104. data/spec/reporters/launcher_spec.rb +24 -0
  105. data/spec/reporters/opendiff_reporter_spec.rb +15 -0
  106. data/spec/reporters/reporter_spec.rb +21 -0
  107. data/spec/scrubber_spec.rb +26 -0
  108. data/spec/spec_helper.rb +7 -0
  109. data/spec/system_command_spec.rb +13 -0
  110. metadata +196 -0
@@ -0,0 +1,39 @@
1
+ require 'approvals/writers/text_writer'
2
+ require 'approvals/writers/array_writer'
3
+ require 'approvals/writers/hash_writer'
4
+ require 'approvals/writers/html_writer'
5
+ require 'approvals/writers/xml_writer'
6
+ require 'approvals/writers/json_writer'
7
+ require 'approvals/writers/binary_writer'
8
+
9
+ module Approvals
10
+ module Writer
11
+ extend Writers
12
+
13
+ REGISTRY = {
14
+ json: Writers::JsonWriter.new,
15
+ xml: Writers::XmlWriter.new,
16
+ html: Writers::HtmlWriter.new,
17
+ hash: Writers::HashWriter.new,
18
+ array: Writers::ArrayWriter.new,
19
+ txt: Writers::TextWriter.new,
20
+ }
21
+
22
+
23
+ class << self
24
+ def for(format)
25
+ begin
26
+ REGISTRY[format] || Object.const_get(format).new
27
+ rescue NameError => e
28
+ error = ApprovalError.new(
29
+ "Approval Error: #{ e }. Please define a custom writer as outlined"\
30
+ " in README section 'Customizing formatted output': "\
31
+ "https://github.com/kytrinyx/approvals#customizing-formatted-output"
32
+ )
33
+ raise error
34
+ end
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,17 @@
1
+ module Approvals
2
+ module Writers
3
+ class ArrayWriter < TextWriter
4
+
5
+ def format(data)
6
+ filter(data).map.with_index do |value, i|
7
+ "[#{i.inspect}] #{value.inspect}\n"
8
+ end.join
9
+ end
10
+
11
+ def filter data
12
+ filter = ::Approvals::Filter.new(Approvals.configuration.excluded_json_keys)
13
+ filter.apply(data)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,49 @@
1
+ module Approvals
2
+ module Writers
3
+ class BinaryWriter
4
+ EXCEPTION_WRITER = Proc.new do |data, file|
5
+ raise "BinaryWriter#callback missing"
6
+ end
7
+
8
+ def initialize(opts = {})
9
+ @autoregister = opts[:autoregister] || true
10
+ @detect = opts[:detect]
11
+ @extension = opts[:extension] || ''
12
+ @write = opts[:write] || EXCEPTION_WRITER
13
+ self.format = opts[:format] || :binary
14
+ end
15
+
16
+ attr_reader :format
17
+
18
+ def format=(sym)
19
+ unregister if autoregister
20
+
21
+ @format = sym
22
+
23
+ register if autoregister
24
+
25
+ end
26
+
27
+ def register
28
+ if @format
29
+ Writer::REGISTRY[@format] = self
30
+ Approval::BINARY_FORMATS << @format
31
+ Approval::IDENTITIES[@format] = @detect if @detect
32
+ end
33
+ end
34
+
35
+ def unregister
36
+ if @format
37
+ Writer::REGISTRY.delete!(@format)
38
+ Approval::BINARY_FORMATS.delete!(@format)
39
+ Approval::IDENTITIES.delete!(@format)
40
+ end
41
+ end
42
+
43
+ def write(data,path)
44
+ @write.call(data,path)
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,20 @@
1
+ module Approvals
2
+ module Writers
3
+ class HashWriter < TextWriter
4
+
5
+ def format(data)
6
+ lines = filter(data).map do |key, value|
7
+ "\t#{key.inspect} => #{value.inspect}"
8
+ end.join("\n")
9
+
10
+ "{\n#{lines}\n}\n"
11
+ end
12
+
13
+ def filter data
14
+ filter = ::Approvals::Filter.new(Approvals.configuration.excluded_json_keys)
15
+ filter.apply(data)
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ module Approvals
2
+ module Writers
3
+ class HtmlWriter < TextWriter
4
+
5
+ def extension
6
+ 'html'
7
+ end
8
+
9
+ def format(data)
10
+ Nokogiri::HTML(data.to_s.strip,&:noblanks).to_xhtml(:indent => 2, :encoding => 'UTF-8')
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ module Approvals
2
+ module Writers
3
+ class JsonWriter < TextWriter
4
+ def extension
5
+ 'json'
6
+ end
7
+
8
+ def format(data)
9
+ hash_or_array = filter(parse_data(data))
10
+
11
+ JSON.pretty_generate(hash_or_array) + "\n"
12
+ end
13
+
14
+ private
15
+
16
+ def parse_data(data)
17
+ if data.respond_to?(:to_str)
18
+ # if the data is a string, assume it has been already json-ified
19
+ JSON.parse(data)
20
+ else
21
+ JSON.parse(data.to_json)
22
+ end
23
+ end
24
+
25
+ def filter data
26
+ filter = ::Approvals::Filter.new(Approvals.configuration.excluded_json_keys)
27
+ filter.apply(data)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,21 @@
1
+ module Approvals
2
+ module Writers
3
+ class TextWriter
4
+ def extension
5
+ 'txt'
6
+ end
7
+
8
+ def write(data, path)
9
+ FileUtils.mkdir_p(File.dirname(path))
10
+ File.open(path, 'w') do |f|
11
+ f.write format(data)
12
+ end
13
+ end
14
+
15
+ def format(data)
16
+ data.to_s
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ module Approvals
2
+ module Writers
3
+ class XmlWriter < TextWriter
4
+
5
+ def extension
6
+ 'xml'
7
+ end
8
+
9
+ def format(data)
10
+ Nokogiri::XML(data.to_s.strip,&:noblanks).to_xml(:indent => 2, :encoding => 'UTF-8')
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,172 @@
1
+ require 'spec_helper'
2
+ require 'approvals/namers/rspec_namer'
3
+
4
+ describe Approvals do
5
+
6
+ let(:namer) { |example| Approvals::Namers::RSpecNamer.new(example) }
7
+
8
+ it "fails" do
9
+ allow(Approvals::Dotfile).to receive(:path).and_return('/dev/null')
10
+
11
+ expect do
12
+ Approvals.verify "this one doesn't exist", :namer => namer
13
+ end.to raise_error Approvals::ApprovalError
14
+ end
15
+
16
+ it "verifies a string" do
17
+ string = "We have, I fear, confused power with greatness."
18
+ Approvals.verify string, :namer => namer
19
+ end
20
+
21
+ it "verifies an array" do
22
+ array = [
23
+ "abc",
24
+ 123,
25
+ :zomg_fooooood,
26
+ %w(cheese burger ribs steak bacon)
27
+ ]
28
+ Approvals.verify array, :namer => namer
29
+ end
30
+
31
+ it "verifies a hash" do
32
+ hash = {
33
+ :meal => 'breakfast',
34
+ :proteins => '90%',
35
+ :price => 38,
36
+ :delicious => true
37
+ }
38
+ Approvals.verify hash, :namer => namer
39
+ end
40
+
41
+ it "verifies a complex object" do
42
+ hello = Object.new
43
+ def hello.to_s
44
+ "Hello, World!"
45
+ end
46
+
47
+ def hello.inspect
48
+ "#<The World Says: Hello!>"
49
+ end
50
+
51
+ Approvals.verify hello, :namer => namer
52
+ end
53
+
54
+ context "custom writer" do
55
+ let(:hello) { Object.new }
56
+
57
+ class MyCustomWriter < Approvals::Writers::TextWriter
58
+ def format(data)
59
+ filter(data)
60
+ end
61
+
62
+ def filter(data)
63
+ data.to_s.chars.reject {|c| c =~ /[a-zA-Z0-9]/}
64
+ end
65
+ end
66
+
67
+ it "verifies a complex object" do
68
+ Approvals.verify hello, :namer => namer, :format => "MyCustomWriter"
69
+ end
70
+
71
+ it "raises an error with an uninitialized custom writer class" do
72
+ expect{
73
+ Approvals.verify hello, :namer => namer, :format => "UninitializedWriter"
74
+ }.to raise_error.with_message(
75
+ /Please define a custom writer as outlined in README section 'Customizing formatted output':/
76
+ )
77
+ end
78
+ end
79
+
80
+ it "verifies html" do
81
+ html = <<-HTML
82
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"><html><head><title>Approval</title></head><body><h1>An Approval</h1><p>It has a paragraph</p></body></html>
83
+ HTML
84
+ Approvals.verify html, :format => :html, :namer => namer
85
+ end
86
+
87
+ it "verifies a malformed html fragment" do
88
+ html = <<-HTML
89
+ <!DOCTYPE html>
90
+ <html>
91
+ <title>Hoi</title>
92
+ <script async defer src="http://foo.com/bar.js"></script>
93
+ <h1>yo</h1>
94
+ HTML
95
+ Approvals.verify html, :format => :html, :namer => namer
96
+ end
97
+
98
+ it "verifies xml" do
99
+ xml = "<xml char=\"kiddo\"><node><content name='beatrice' /></node><node aliases='5'><content /></node></xml>"
100
+ Approvals.verify xml, :format => :xml, :namer => namer
101
+ end
102
+
103
+ it "verifies json" do
104
+ json = '{"pet":{"species":"turtle","color":"green","name":"Anthony"}}'
105
+ Approvals.verify json, :format => :json, :namer => namer
106
+ end
107
+
108
+
109
+ it "verifies json and is newline agnostic" do
110
+ json = '{"pet":{"species":"turtle","color":"green","name":"Anthony"}}'
111
+ Approvals.verify json, :format => :json, :namer => namer
112
+ end
113
+
114
+ it "verifies an array as json when format is set to json" do
115
+ people = [
116
+ {"name" => "Alice", "age" => 28},
117
+ {"name" => "Bob", "age" => 22}
118
+ ]
119
+
120
+ Approvals.verify(people, format: :json, namer: namer)
121
+ end
122
+
123
+ it "verifies an executable" do
124
+ executable = Approvals::Executable.new('SELECT 1') do |command|
125
+ puts "your slip is showing (#{command})"
126
+ end
127
+
128
+ Approvals.verify executable, :namer => namer
129
+ end
130
+
131
+ it "passes approved files through ERB" do
132
+ $what = 'greatness'
133
+ string = "We have, I fear, confused power with greatness."
134
+ Approvals.verify string, :namer => namer
135
+ end
136
+
137
+ # Bugfix: If only the approved file gets passed through ERB,
138
+ # then <% (received) is not equal to <% (approved).
139
+ it "passes the received files through ERB" do
140
+ string = "<%"
141
+ Approvals.verify string, :namer => namer
142
+ end
143
+
144
+ describe "supports excluded keys option" do
145
+ let(:hash) { {:object => {:id => rand(100), :created_at => Time.now, :name => 'test', deleted_at: nil}} }
146
+
147
+ before do
148
+ Approvals.configure do |c|
149
+ c.excluded_json_keys = {
150
+ :id => /(\A|_)id$/,
151
+ :date => /_at$/
152
+ }
153
+ end
154
+ end
155
+
156
+ it "verifies json with excluded keys" do
157
+ Approvals.verify JSON.dump(hash), :format => :json, :namer => namer
158
+ end
159
+
160
+ it "also supports an array of hashes" do
161
+ Approvals.verify JSON.dump([hash]), :format => :json, :namer => namer
162
+ end
163
+
164
+ it "supports the array writer" do
165
+ Approvals.verify [hash], :format => :array, :namer => namer
166
+ end
167
+
168
+ it "supports the hash writer" do
169
+ Approvals.verify hash, :format => :array, :namer => namer
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+ require 'approvals/configuration'
3
+
4
+ describe Approvals::Configuration do
5
+
6
+ it "defaults to 'fixtures/approvals/'" do
7
+ expect(Approvals.configuration.approvals_path).to eq('fixtures/approvals/')
8
+ end
9
+
10
+ describe "when set" do
11
+ before(:each) do
12
+ Approvals.configure do |c|
13
+ c.approvals_path = 'output/dir/'
14
+ end
15
+ end
16
+
17
+ after(:each) do
18
+ Approvals.configure do |c|
19
+ c.approvals_path = nil
20
+ end
21
+ end
22
+
23
+ it "overrides the output directory" do
24
+ expect(Approvals.configuration.approvals_path).to eq('output/dir/')
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'approvals/dotfile'
3
+
4
+ describe Approvals::Dotfile do
5
+ let(:dotfile) { '/tmp/.approvals' }
6
+
7
+ before(:each) do
8
+ allow(Approvals::Dotfile).to receive(:path).and_return dotfile
9
+ Approvals::Dotfile.reset
10
+ end
11
+
12
+ it "appends the text" do
13
+ Approvals::Dotfile.append('text')
14
+ expect(File.readlines(dotfile).map(&:chomp)).to eq ['text']
15
+ end
16
+
17
+ it "appends the text exactly once" do
18
+ Approvals::Dotfile.append('text')
19
+ Approvals::Dotfile.append('text')
20
+ expect(File.readlines(dotfile).map(&:chomp)).to eq ['text']
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+ require 'approvals/executable'
3
+
4
+ describe Approvals::Executable do
5
+
6
+ it "reflects the its value in inspect" do
7
+ executable = Approvals::Executable.new('SELECT 1')
8
+ expect(executable.inspect).to eq 'SELECT 1'
9
+ end
10
+
11
+ it "takes a block" do
12
+ executable = Approvals::Executable.new('SELECT 1') do |command|
13
+ "execute query: #{command}"
14
+ end
15
+ expect(executable.on_failure.call('SELECT 1')).to eq 'execute query: SELECT 1'
16
+ end
17
+ end