rspec_approvals 0.8.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bf7766ed4c1e6890d248ccabf8e6cd02c6656f07f702e95b95c06a89ef1b7fd2
4
+ data.tar.gz: ec5c8219a4129d2bcfc8a3ebfa4bdcf5cdf658a0a08a677906e0bffa8cd8e4c1
5
+ SHA512:
6
+ metadata.gz: 7d3fb8aa6519c178621967564338eb822973660034511bf753156fbffa88468aaad58bc0c5e7367d56c2a7759d788b2b40f539576483ea16c9a5cada7f7f2c90
7
+ data.tar.gz: 5e503b9463bc9ed88de91e98a1e72e91be4a987241acda2a6673a63f6dd2b32a5e2639016864637dc4faa7902789132c9e28b64c3cf3781f6f3a0e0fb99bf9c3
data/README.md ADDED
@@ -0,0 +1,247 @@
1
+ # RSpec Approvals
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/rspec_approvals.svg)](https://badge.fury.io/rb/rspec_approvals)
4
+ [![Build Status](https://github.com/DannyBen/rspec_approvals/workflows/Test/badge.svg)](https://github.com/DannyBen/rspec_approvals/actions?query=workflow%3ATest)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/a06ed5e30412062c454c/maintainability)](https://codeclimate.com/github/DannyBen/rspec_approvals/maintainability)
6
+
7
+ ---
8
+
9
+ RSpec Approvals allows you to interactively review and approve testable
10
+ content.
11
+
12
+ ![Demo](demo/cast.svg)
13
+
14
+ ---
15
+
16
+ ## Install
17
+
18
+ ```
19
+ $ gem install rspec_approvals
20
+ ```
21
+
22
+ Or with bundler:
23
+
24
+ ```ruby
25
+ gem 'rspec_approvals'
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ Require the gem in your spec helper:
31
+
32
+ ```ruby
33
+ # spec/spec_helper.rb
34
+ require 'rspec_approvals'
35
+ ```
36
+
37
+ And use any of the matchers in your specs.
38
+
39
+ ```ruby
40
+ describe 'ls' do
41
+ it "works" do
42
+ expect(`ls`).to match_approval('ls_approval')
43
+ end
44
+ end
45
+ ```
46
+
47
+ ## Matchers
48
+
49
+ ### `match_approval` - Compare Strings
50
+
51
+ Compare a string with a pre-approved approval.
52
+
53
+ ```ruby
54
+ expect('some string').to match_approval('approval_filename')
55
+ ```
56
+
57
+
58
+ ### `output_approval` - Compare STDOUT/STDERR
59
+
60
+ Compare an output (stdout or stderr) with a pre-approved approval.
61
+
62
+ ```ruby
63
+ expect { puts "hello" }.to output_approval('approval_filename')
64
+ expect { puts "hello" }.to output_approval('approval_filename').to_stdout
65
+ expect { $stderr.puts "hello" }.to output_approval('approval_filename').to_stderr
66
+
67
+ # The first two are the same, as the default stream is stdout.
68
+ ```
69
+
70
+
71
+ ### `raise_approval` - Compare raised exceptions
72
+
73
+ Compare a raised exception with a pre-approved approval.
74
+
75
+ ```ruby
76
+ expect { raise 'some error' }.to raise_approval('approval_filename')
77
+ ```
78
+
79
+ ## Modifiers
80
+
81
+ ### `diff` - String similarity
82
+
83
+ Adding `diff(distance)` to either `match_approval` or `output_approval` will
84
+ change the matching behavior. Instead of expecting the strings to be exactly
85
+ the same, using `diff` compares the strings using the
86
+ [Levenshtein distance][levenshtein] algorithm.
87
+
88
+ In the below example, we allow up to 5 characters to be different.
89
+
90
+ ```ruby
91
+ expect ('some string').to match_approval('approval_filename').diff(5)
92
+ expect { puts 'some string' }.to output_approval('approval_filename').diff(5)
93
+ ```
94
+
95
+ ### `except` - Exclude by regular expression
96
+
97
+ Adding `except(regex)` to either `match_approval` or `output_approval` will
98
+ modify the string under test before running. By default, the regular
99
+ expression will be replaced with `...`.
100
+
101
+ In the below example, we ignore the full path of the file.
102
+
103
+ ```ruby
104
+ expect('path: /path/to/file').to match_approval('approval_filename').except(/path: .*file/)
105
+ ```
106
+
107
+ You may provide a second argument, which will be used as an alternative
108
+ replace string:
109
+
110
+ In the below example, all time strings will be replaced with `HH:MM`:
111
+
112
+ ```ruby
113
+ expect('22:30').to match_approval('approval_filename').except(/\d2:\d2/, 'HH:MM')
114
+ ```
115
+
116
+ ### `before` - Alter the string before testing
117
+
118
+ The `before(proc)` method is a low level method and should normally not be
119
+ used directly (as it is used by the `except` modifier).
120
+
121
+ Adding `before(proc)` to either `match_approval` or `output_approval` will
122
+ call the block and supply the actual string. The proc is expected to return
123
+ the new actual string.
124
+
125
+ In the below example, we replace all email addresses in a string.
126
+
127
+ ```ruby
128
+ expect('hello rspec@approvals.com').to match_approval('approval_filename').before ->(actual) do
129
+ actual.gsub /\w+@\w+\.\w+/, 'some@email.com'
130
+ end
131
+
132
+ ```
133
+
134
+ ## Configuration
135
+
136
+ ### `interactive_approvals`
137
+
138
+ By default, interactive approvals are enabled in any environment that
139
+ does not define the `CI` or the `GITHUB_ACTIONS` environment variables.
140
+ You can change this by adding this to your `spec_helper`
141
+
142
+ ```ruby
143
+ RSpec.configure do |config|
144
+ config.interactive_approvals = false # or any logic
145
+ end
146
+ ```
147
+
148
+ ### `approvals_path`
149
+
150
+ By default, approvals are stored in `spec/approvals`. To change the path,
151
+ add this to your `spec_helper`.
152
+
153
+ ```ruby
154
+ RSpec.configure do |config|
155
+ config.approvals_path = 'spec/anywhere/else'
156
+ end
157
+ ```
158
+
159
+ ### `auto_approve`
160
+
161
+ If you wish to automatically approve all new or changed approvals, you can
162
+ set the `auto_approve` configuration option to `true`. By default,
163
+ auto approval is enabled if the environment variable `AUTO_APPROVE` is set.
164
+
165
+ ```ruby
166
+ RSpec.configure do |config|
167
+ config.auto_approve = true # or any logic
168
+ end
169
+ ```
170
+
171
+ This feature is intended to help clean up the approvals folder from old, no
172
+ longer used files. Simply run the specs once, to ensure they all oass,
173
+ delete the approvals folder, and run the specs again with:
174
+
175
+ ```
176
+ $ AUTO_APPROVE=1 rspec
177
+ ```
178
+
179
+ ### `strip_ansi_escape`
180
+
181
+ In case your output strings contain ANSI escape codes that you wish to avoid
182
+ storing in your approvals, you can set the `strip_ansi_escape` to `true`.
183
+
184
+ ```ruby
185
+ RSpec.configure do |config|
186
+ config.strip_ansi_escape = true
187
+ end
188
+ ```
189
+
190
+ ### `before_approval`
191
+
192
+ In case you need to alter the actual output globally, you can provide the
193
+ `before_approval` option with a proc. The proc will receive the actual
194
+ output - similarly to the `before` modifier - and is expectedd to return
195
+ a modified actual string.
196
+
197
+ ```ruby
198
+ RSpec.configure do |config|
199
+ config.before_approval = ->(actual) do
200
+ # return the actual string, without IP addresses
201
+ actual.gsub(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/, '[IP REMOVED]')
202
+ end
203
+ end
204
+ ```
205
+
206
+ ## Advanced Usage Tips
207
+
208
+ ### Sending output directly to RSpecApprovals
209
+
210
+ In some cases, you might need to send output directly to the `RSpecApproval`
211
+ stream capturer.
212
+
213
+ An example use case, is when you are testing `Logger` output.
214
+
215
+ The `RSpecApproval#stdout` and `RSpecApproval#stderr` can be used as an
216
+ alternative to `$stdout` and `$stderr`. These methods both return the
217
+ `StringIO` object that is used by `RSpecApprovals` to capture the output.
218
+
219
+ For example, you can use this:
220
+
221
+ ```ruby
222
+ logger = Logger.new(RSpecApprovals.stdout)
223
+ ```
224
+
225
+ as an alternative to this:
226
+
227
+ ```
228
+ logger = Logger.new($stdout)
229
+ ```
230
+
231
+ ### Consistent terminal width
232
+
233
+ In case you are testing standard output with long lines, you may encounter inconsistencies when testing on different hosts, with varying terminal width. In order to ensure consistent output to stdout, you may want to set a known terminal size in your `spec_helper`:
234
+
235
+ ```ruby
236
+ ENV['COLUMNS'] = '80'
237
+ ENV['LINES'] = '24'
238
+ ```
239
+
240
+ ## Contributing / Support
241
+
242
+ If you experience any issue, have a question or a suggestion, or if you wish
243
+ to contribute, feel free to [open an issue][issues].
244
+
245
+
246
+ [levenshtein]: https://en.wikipedia.org/wiki/Levenshtein_distance
247
+ [issues]: https://github.com/DannyBen/rspec_approvals/issues
@@ -0,0 +1,12 @@
1
+ require 'rspec_approvals/extensions/file'
2
+
3
+ require 'rspec_approvals/module_functions'
4
+ require 'rspec_approvals/stream'
5
+ require 'rspec_approvals/approval_handler'
6
+ require 'rspec_approvals/matchers/base'
7
+ require 'rspec_approvals/matchers/match_approval'
8
+ require 'rspec_approvals/matchers/output_approval'
9
+ require 'rspec_approvals/matchers/raise_approval'
10
+
11
+ require 'rspec_approvals/rspec_config'
12
+
@@ -0,0 +1,99 @@
1
+ require 'io/console'
2
+ require 'colsole'
3
+ require 'tty-prompt'
4
+ require 'diffy'
5
+
6
+ module RSpecApprovals
7
+
8
+ # Handles user input and interactive approvals
9
+ class ApprovalHandler
10
+ include Colsole
11
+
12
+ attr_reader :expected, :actual, :approval_file
13
+
14
+ def run(expected, actual, approval_file)
15
+ @expected = expected
16
+ @actual = actual
17
+ @approval_file = approval_file
18
+
19
+ show expected.empty? ? actual : diff
20
+ prompt_user
21
+ end
22
+
23
+ private
24
+
25
+ def prompt_user
26
+ response = auto_approve? ? :approve : get_response
27
+
28
+ case response
29
+
30
+ when :approve, :reject
31
+ send response
32
+
33
+ when :actual, :expected, :diff
34
+ show send response
35
+ prompt_user
36
+
37
+ else
38
+ false
39
+
40
+ end
41
+ end
42
+
43
+ def auto_approve?
44
+ RSpec.configuration.auto_approve
45
+ end
46
+
47
+ def get_response
48
+ prompt.select "Please Choose:", menu_options, symbols: { marker: '>' }
49
+ rescue TTY::Reader::InputInterrupt
50
+ # :nocov:
51
+ return :reject
52
+ # :nocov:
53
+ end
54
+
55
+ def menu_options
56
+ base = {
57
+ 'Reject (and fail test)' => :reject,
58
+ 'Approve (and save)' => :approve,
59
+ }
60
+
61
+ extra = {
62
+ 'Show actual output' => :actual,
63
+ 'Show expected output' => :expected,
64
+ 'Show diff' => :diff,
65
+ }
66
+
67
+ expected.empty? ? base : base.merge(extra)
68
+ end
69
+
70
+ def approve
71
+ say "!txtgrn!Approved"
72
+ File.deep_write approval_file, actual
73
+ true
74
+ end
75
+
76
+ def reject
77
+ say "!txtred!Not Approved"
78
+ false
79
+ end
80
+
81
+ def separator
82
+ "!txtgrn!" + ('_' * terminal_width)
83
+ end
84
+
85
+ def diff
86
+ Diffy::Diff.new(expected, actual, context: 2).to_s :color
87
+ end
88
+
89
+ def show(what)
90
+ say separator
91
+ say what
92
+ say separator
93
+ end
94
+
95
+ def prompt
96
+ @prompt ||= TTY::Prompt.new
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,9 @@
1
+ require 'fileutils'
2
+
3
+ class File
4
+ def self.deep_write(file, content)
5
+ dir = File.dirname file
6
+ FileUtils.mkdir_p dir unless Dir.exist? dir
7
+ File.write file, content
8
+ end
9
+ end
@@ -0,0 +1,148 @@
1
+ module RSpecApprovals
2
+ module Matchers
3
+
4
+ # A base matcher for approvals
5
+ class Base
6
+ attr_reader :approval_name, :actual, :distance, :actual_distance
7
+
8
+ def initialize(approval_name=nil)
9
+ @before = nil
10
+ @approval_name = approval_name
11
+ end
12
+
13
+ # Called by RSpec. This will be overridden by child matchers.
14
+ def matches?(actual)
15
+ @actual ||= actual
16
+ return false if @actual.empty?
17
+
18
+ @actual = sanitize @actual
19
+ success = strings_match?
20
+
21
+ if success or !interactive?
22
+ success
23
+ else
24
+ approve_approval
25
+ end
26
+ end
27
+
28
+ # Enables the ability to do something like:
29
+ # `expect(string).to match_approval(file).diff(10)
30
+ # The distance argument is the max allowed Levenshtein Distance.
31
+ def diff(distance)
32
+ @distance = distance
33
+ self
34
+ end
35
+
36
+ # Enables the ability to do something like:
37
+ # `expect(string).to match_approval(file).except(/\d+/)
38
+ def except(regex, replace = '...')
39
+ before ->(str) do
40
+ str.gsub regex, replace
41
+ end
42
+ end
43
+
44
+ # Enables the ability to adjust the actual string before checking
45
+ # for matches:
46
+ # `expect(a).to match_approval(f).before ->(actual) { actual.gsub /one/, 'two' }`
47
+ def before(proc)
48
+ @before ||= []
49
+ @before << proc
50
+ self
51
+ end
52
+
53
+ # Returns the expected value, from an approval file
54
+ def expected
55
+ @expected ||= expected!
56
+ end
57
+
58
+ # Called by RSpec when there is a failure
59
+ def failure_message
60
+ return "actual string is empty" if actual.empty?
61
+
62
+ result = "expected: #{actual}\nto match: #{expected}"
63
+
64
+ if distance
65
+ result = "#{result}\n(actual distance is #{actual_distance} instead of the expected #{distance})"
66
+ end
67
+
68
+ result
69
+ end
70
+
71
+ # Lets RSpec know these matchers support diffing
72
+ def diffable?
73
+ true
74
+ end
75
+
76
+ # Returns true if RSpec is configured to sanitize (remove ANSI escape
77
+ # codes) from the actual strings before proceeeding to comparing them.
78
+ def sanitize?
79
+ RSpec.configuration.strip_ansi_escape
80
+ end
81
+
82
+ # Returns true if RSpec is configured to allow interactivity.
83
+ # By default, interactivity is enabled unless the environment
84
+ # variable `CI` is set.
85
+ def interactive?
86
+ RSpec.configuration.interactive_approvals
87
+ end
88
+
89
+ # Returns the path to the approvals directory.
90
+ # Default: `spec/approvals`
91
+ def approvals_dir
92
+ RSpec.configuration.approvals_path
93
+ end
94
+
95
+ # Returns the path to the approval file
96
+ def approval_file
97
+ "#{approvals_dir}/#{approval_name}"
98
+ end
99
+
100
+ protected
101
+
102
+ # Asks for user approval. Used by child classes.
103
+ def approve_approval
104
+ approval_handler = ApprovalHandler.new
105
+ approval_handler.run expected, actual, approval_file
106
+ end
107
+
108
+ # Returns the actual approval file content.
109
+ def expected!
110
+ File.exist?(approval_file) ? File.read(approval_file) : ''
111
+ end
112
+
113
+ # Do the actual test.
114
+ # - If .before() was used, we foreward the actual output to the
115
+ # proc for processing first.
116
+ # - If before_approval proc was configured, forward the acual output
117
+ # to the proc for processing.
118
+ # - If .diff() was used, then distance will be set and then
119
+ # we "levenshtein it".
120
+ # - Otherwise, compare with ==
121
+ def strings_match?
122
+ if @before
123
+ @before.each do |proc|
124
+ @actual = proc.call actual
125
+ end
126
+ end
127
+
128
+ if RSpec.configuration.before_approval.is_a? Proc
129
+ @actual = RSpec.configuration.before_approval.call actual
130
+ end
131
+
132
+ if distance
133
+ @actual_distance = String::Similarity.levenshtein_distance expected, actual
134
+ @actual_distance <= distance
135
+ else
136
+ actual == expected
137
+ end
138
+ end
139
+
140
+ # Returns the input string stripped of ANSI escape codes if the
141
+ # strip_ansi_escape configuration setting was set to true
142
+ def sanitize(string)
143
+ sanitize? ? Strings::ANSI.sanitize(string) : string
144
+ end
145
+ end
146
+
147
+ end
148
+ end
@@ -0,0 +1,15 @@
1
+ require 'string-similarity'
2
+
3
+ module RSpecApprovals
4
+ module Matchers
5
+ # Adds the matcher to RSpec:
6
+ # `expect(string).to match_approval(file)`
7
+ def match_approval(expected)
8
+ MatchApproval.new expected
9
+ end
10
+
11
+ class MatchApproval < Base
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,43 @@
1
+ module RSpecApprovals
2
+ module Matchers
3
+ # Adds the matcher to RSpec:
4
+ # `expect { stream }.to output_approval(file)`
5
+ def output_approval(expected)
6
+ OutputApproval.new expected
7
+ end
8
+
9
+ class OutputApproval < Base
10
+ # Called by RSpec
11
+ def matches?(block)
12
+ return false unless block.is_a? Proc
13
+ @actual = stream_capturer.capture block
14
+ super
15
+ end
16
+
17
+ # Lets RSpec know that this matcher requires a block.
18
+ def supports_block_expectations?
19
+ true
20
+ end
21
+
22
+ # Adds chained matcher, to allow:
23
+ # expect { stream }.to output_approval(file).to_stdout
24
+ # This is the default, and only provided for completeness.
25
+ def to_stdout
26
+ @stream_capturer = Stream::Stdout
27
+ self
28
+ end
29
+
30
+ # Adds chained matcher, to allow:
31
+ # expect { stream }.to output_approval(file).to_stderr
32
+ def to_stderr
33
+ @stream_capturer = Stream::Stderr
34
+ self
35
+ end
36
+
37
+ def stream_capturer
38
+ @stream_capturer ||= Stream::Stdout
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,30 @@
1
+ module RSpecApprovals
2
+ module Matchers
3
+ # Adds the matcher to RSpec:
4
+ # `expect { something_that_errors }.to raise_approval(file)`
5
+ def raise_approval(expected)
6
+ RaiseApproval.new expected
7
+ end
8
+
9
+ class RaiseApproval < Base
10
+ # Called by RSpec
11
+ def matches?(block)
12
+ return false unless block.is_a? Proc
13
+ @actual = 'Nothing raised'
14
+
15
+ begin
16
+ block.call
17
+ rescue => e
18
+ @actual = e.inspect
19
+ end
20
+
21
+ super
22
+ end
23
+
24
+ # Lets RSpec know that this matcher requires a block.
25
+ def supports_block_expectations?
26
+ true
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ module RSpecApprovals
2
+ class << self
3
+ def stdout
4
+ @stdout ||= StringIO.new
5
+ end
6
+
7
+ def stderr
8
+ @stderr ||= StringIO.new
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ # Add our custom matchers and configuration options to RSpec
2
+ if defined? RSpec
3
+ # Fix for rails
4
+ require 'rspec/core' unless RSpec.respond_to? :configure
5
+
6
+ RSpec.configure do |config|
7
+ config.include RSpecApprovals::Matchers
8
+ config.add_setting :approvals_path, default: File.expand_path('spec/approvals')
9
+ config.add_setting :interactive_approvals, default: (!ENV['CI'] and !ENV['GITHUB_ACTIONS'])
10
+ config.add_setting :auto_approve, default: ENV['AUTO_APPROVE']
11
+ config.add_setting :strip_ansi_escape, default: false
12
+ config.add_setting :before_approval, default: nil
13
+ end
14
+ end
@@ -0,0 +1,41 @@
1
+ require 'strings-ansi'
2
+
3
+ module RSpecApprovals
4
+ # Capture stdout and stderr
5
+ #
6
+ # These methods are borrowed from rspec's built in matchers
7
+ # https://github.com/rspec/rspec-expectations/blob/add9b271ecb1d65f7da5bc8a9dd8c64d81d92303/lib/rspec/matchers/built_in/output.rb
8
+ module Stream
9
+ module Stdout
10
+ def self.capture(block)
11
+ RSpecApprovals.stdout.truncate 0
12
+ RSpecApprovals.stdout.rewind
13
+
14
+ original_stream = $stdout
15
+ $stdout = RSpecApprovals.stdout
16
+ block.call
17
+ RSpecApprovals.stdout.string.dup
18
+
19
+ ensure
20
+ $stdout = original_stream
21
+
22
+ end
23
+ end
24
+
25
+ module Stderr
26
+ def self.capture(block)
27
+ RSpecApprovals.stderr.truncate 0
28
+ RSpecApprovals.stderr.rewind
29
+
30
+ original_stream = $stderr
31
+ $stderr = RSpecApprovals.stderr
32
+ block.call
33
+ RSpecApprovals.stderr.string.dup
34
+
35
+ ensure
36
+ $stderr = original_stream
37
+
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module RSpecApprovals
2
+ VERSION = "0.8.0.rc1"
3
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec_approvals
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.0.rc1
5
+ platform: ruby
6
+ authors:
7
+ - Danny Ben Shitrit
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-05-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colsole
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: string-similarity
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: diffy
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: tty-prompt
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.19'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.19'
69
+ - !ruby/object:Gem::Dependency
70
+ name: strings-ansi
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.1'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.1'
83
+ description: Automatic interactive approvals for rspec
84
+ email: db@dannyben.com
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - README.md
90
+ - lib/rspec_approvals.rb
91
+ - lib/rspec_approvals/approval_handler.rb
92
+ - lib/rspec_approvals/extensions/file.rb
93
+ - lib/rspec_approvals/matchers/base.rb
94
+ - lib/rspec_approvals/matchers/match_approval.rb
95
+ - lib/rspec_approvals/matchers/output_approval.rb
96
+ - lib/rspec_approvals/matchers/raise_approval.rb
97
+ - lib/rspec_approvals/module_functions.rb
98
+ - lib/rspec_approvals/rspec_config.rb
99
+ - lib/rspec_approvals/stream.rb
100
+ - lib/rspec_approvals/version.rb
101
+ homepage: https://github.com/DannyBen/rspec_approvals
102
+ licenses:
103
+ - MIT
104
+ metadata: {}
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: 2.3.0
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">"
117
+ - !ruby/object:Gem::Version
118
+ version: 1.3.1
119
+ requirements: []
120
+ rubygems_version: 3.1.2
121
+ signing_key:
122
+ specification_version: 4
123
+ summary: Interactive RSpec Approvals
124
+ test_files: []