rspec-rcv 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +82 -0
- data/Rakefile +6 -0
- data/lib/rspec-rcv.rb +26 -0
- data/lib/rspec-rcv/configuration.rb +77 -0
- data/lib/rspec-rcv/data_changed_error.rb +3 -0
- data/lib/rspec-rcv/handler.rb +63 -0
- data/lib/rspec-rcv/test_frameworks/rspec.rb +21 -0
- data/lib/rspec-rcv/version.rb +3 -0
- data/rspec-rcv.gemspec +24 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b08931127f4e12942b32aa34d5006d18728cc395
|
4
|
+
data.tar.gz: ba816c39170e25462586f6b26b130794ebe223f7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 100479086b2e3f09d3573c01934d93eb294da1c33b0f89acde87b468a8a3bb0554f3b4b68a16f4df53d05947d1ab9f8cc698aaee8fdb1ad8b742a3dc3bcadae7
|
7
|
+
data.tar.gz: 2c4af36ebf6f81dcbf52d746081fb3e9ca487e1d82c4d068089cebae0448d579eba5431a1eda057c10c2e05b3564b0425c0c984a0a173dd91ef2c39df4573425
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 TODO: Write your name
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# Purpose
|
2
|
+
|
3
|
+
The purpose of this gem is to provide a way to export the results of rspec tests to disk. In this way, a controller test can output the response body of the request, and then a front-end test can read that file in order to have predictable and maintainable front-end fixtures.
|
4
|
+
|
5
|
+
This gem currently only applies to the rspec testing framework.
|
6
|
+
|
7
|
+
# Usage
|
8
|
+
|
9
|
+
The gem is require'd in the rspec loading script, and will take effect from there by using the "fixture" option. For example, the following test would record response.body to spec/fixtures/widgets/index.json"
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
describe "GET index" do
|
13
|
+
it "is successful", fixture: "widgets/index.json" do
|
14
|
+
get :index
|
15
|
+
expect(response).to be_success
|
16
|
+
end
|
17
|
+
end
|
18
|
+
```
|
19
|
+
|
20
|
+
The test itself does not need to do anything different, it is all in configuration.
|
21
|
+
|
22
|
+
If the file already exists, and the new content is not identical, the test will fail with a message indicating that this happened. This will prevent surprises from happening, and can be turned off in a configuration variable.
|
23
|
+
|
24
|
+
# Lifecycle
|
25
|
+
|
26
|
+
The result of the exportable variable is captured at the end of the test, after everything else has run. Because this is an
|
27
|
+
outer `after(:each)`, it will run after all of the other `after` blocks in the suite. This means you can't use the output of
|
28
|
+
the file in an after block (not that you should need to). See the spec suite here to understand how you *could* use the output of
|
29
|
+
the file in the spec suite.
|
30
|
+
|
31
|
+
# Configuration Options
|
32
|
+
|
33
|
+
The following options are available to override, as well as their default values:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
config.exportable_proc = Proc.new { response.body }
|
37
|
+
config.compare_with = Proc.new { |existing, new| existing == new }
|
38
|
+
config.export_with = Proc.new do |hash|
|
39
|
+
begin
|
40
|
+
hash[:data] = JSON.parse(hash[:data])
|
41
|
+
rescue JSON::ParserError
|
42
|
+
end
|
43
|
+
JSON.pretty_generate(hash)
|
44
|
+
end
|
45
|
+
config.base_path = nil
|
46
|
+
config.fail_on_changed_output = true
|
47
|
+
```
|
48
|
+
|
49
|
+
`exportable_proc`, `compare_with`, `export_with` must implement `.call`. For `exportable_proc`, the result will be written to disk
|
50
|
+
and should be a String. For `compare_with`, the proc should return true when existing and new are considered equal. For `export_with`
|
51
|
+
a hash will be passed in and the result will be a String written to disk.
|
52
|
+
|
53
|
+
`export_with` by default tries to take hash[:data] (a string) and JSON parse it. If it isn't successful, that is fine and hash[:data] isn't
|
54
|
+
changed. If it is JSON, then the pretty print will work more optimally.
|
55
|
+
|
56
|
+
Note: This can support things that aren't JSON because you can override Proc's that you need to. However, some of the internal workings assume JSON and so it is recommended to just keep the file as JSON.
|
57
|
+
|
58
|
+
# What about fields that change everytime I run the specs?
|
59
|
+
|
60
|
+
This is why you can override `compare_with`. For instance, here is a configuration to ignore `id, created_at, updated_at` in a Rails app:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
RSpecRcv.configure do |config|
|
64
|
+
config.configure_rspec_metadata!
|
65
|
+
|
66
|
+
filters = [:id, :created_at, :updated_at]
|
67
|
+
config.compare_with = lambda do |existing, new|
|
68
|
+
existing = ActiveSupport::HashWithIndifferentAccess.new(existing)
|
69
|
+
new = ActiveSupport::HashWithIndifferentAccess.new(JSON.parse(new))
|
70
|
+
|
71
|
+
existing.except(*filters) != new.except(*filters)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
# What happens when fields change that shouldn't?
|
77
|
+
|
78
|
+
You will get a diff of the output to your console. Here is an example:
|
79
|
+
|
80
|
+
![image](https://cloud.githubusercontent.com/assets/1231659/8729785/2a2aaa24-2bbb-11e5-90fe-99572a95ab7f.png)
|
81
|
+
|
82
|
+
|
data/Rakefile
ADDED
data/lib/rspec-rcv.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "rspec-rcv/version"
|
2
|
+
require "rspec-rcv/configuration"
|
3
|
+
require "rspec-rcv/test_frameworks/rspec"
|
4
|
+
|
5
|
+
require "rspec-rcv/handler"
|
6
|
+
require "rspec-rcv/data_changed_error"
|
7
|
+
|
8
|
+
module RSpecRcv
|
9
|
+
extend self
|
10
|
+
|
11
|
+
module RSpec
|
12
|
+
autoload :Metadata, "rspec-stripe/test_frameworks/rspec"
|
13
|
+
end
|
14
|
+
|
15
|
+
def configure
|
16
|
+
yield configuration
|
17
|
+
end
|
18
|
+
|
19
|
+
def configuration
|
20
|
+
@configuration ||= Configuration.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def config(overrides=nil)
|
24
|
+
@configuration.opts(overrides)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module RSpecRcv
|
2
|
+
class Configuration
|
3
|
+
DEFAULTS = {
|
4
|
+
exportable_proc: Proc.new { response.body },
|
5
|
+
compare_with: Proc.new { |existing, new| existing == new },
|
6
|
+
export_with: Proc.new do |hash|
|
7
|
+
begin
|
8
|
+
hash[:data] = JSON.parse(hash[:data])
|
9
|
+
rescue JSON::ParserError
|
10
|
+
end
|
11
|
+
JSON.pretty_generate(hash)
|
12
|
+
end,
|
13
|
+
fail_on_changed_output: true,
|
14
|
+
base_path: nil,
|
15
|
+
fixture: nil
|
16
|
+
}
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
reset!
|
20
|
+
end
|
21
|
+
|
22
|
+
def configure_rspec_metadata!
|
23
|
+
unless @rspec_metadata_configured
|
24
|
+
RSpecRcv::RSpec::Metadata.configure!
|
25
|
+
@rspec_metadata_configured = true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def reset!
|
30
|
+
@opts = DEFAULTS.dup
|
31
|
+
end
|
32
|
+
|
33
|
+
def opts(overrides={})
|
34
|
+
@opts.merge(overrides)
|
35
|
+
end
|
36
|
+
|
37
|
+
def exportable_proc
|
38
|
+
@opts[:exportable_proc]
|
39
|
+
end
|
40
|
+
|
41
|
+
def exportable_proc=(val)
|
42
|
+
@opts[:exportable_proc] = val
|
43
|
+
end
|
44
|
+
|
45
|
+
def export_with
|
46
|
+
@opts[:export_with]
|
47
|
+
end
|
48
|
+
|
49
|
+
def export_with=(val)
|
50
|
+
@opts[:export_with] = val
|
51
|
+
end
|
52
|
+
|
53
|
+
def compare_with
|
54
|
+
@opts[:compare_with]
|
55
|
+
end
|
56
|
+
|
57
|
+
def compare_with=(val)
|
58
|
+
@opts[:compare_with] = val
|
59
|
+
end
|
60
|
+
|
61
|
+
def base_path
|
62
|
+
@opts[:base_path]
|
63
|
+
end
|
64
|
+
|
65
|
+
def base_path=(val)
|
66
|
+
@opts[:base_path] = val
|
67
|
+
end
|
68
|
+
|
69
|
+
def fail_on_changed_output
|
70
|
+
@opts[:fail_on_changed_output]
|
71
|
+
end
|
72
|
+
|
73
|
+
def fail_on_changed_output=(val)
|
74
|
+
@opts[:fail_on_changed_output] = val
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'diffy'
|
3
|
+
|
4
|
+
module RSpecRcv
|
5
|
+
class Handler
|
6
|
+
def initialize(file_path, data, metadata: {})
|
7
|
+
@file_path = file_path
|
8
|
+
@opts = RSpecRcv.config(metadata)
|
9
|
+
@data = data.call(@opts)
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
return if existing_data && existing_data["file"] == file_path && existing_data["data"] == data
|
14
|
+
|
15
|
+
output = { recorded_at: Time.now, file: file_path, data: data }
|
16
|
+
output = opts[:export_with].call(output) + "\n"
|
17
|
+
|
18
|
+
if existing_data
|
19
|
+
eq = opts[:compare_with].call(existing_data["data"], data)
|
20
|
+
|
21
|
+
if !eq && opts[:fail_on_changed_output]
|
22
|
+
diff = Diffy::Diff.new(existing_file, output)
|
23
|
+
raise RSpecRcv::DataChangedError.new("Existing data will be overwritten. Turn off this feature with fail_on_changed_output=false\n\n#{diff}")
|
24
|
+
end
|
25
|
+
|
26
|
+
return if eq
|
27
|
+
end
|
28
|
+
|
29
|
+
FileUtils.mkdir_p(File.dirname(path))
|
30
|
+
File.open(path, 'w') do |file|
|
31
|
+
file.write(output)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_reader :file_path, :data, :opts
|
38
|
+
|
39
|
+
def path
|
40
|
+
if opts[:base_path]
|
41
|
+
File.join(opts[:base_path], opts[:fixture])
|
42
|
+
else
|
43
|
+
opts[:fixture]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def existing_file
|
48
|
+
@existing_file ||= if File.exists?(path)
|
49
|
+
File.read(path)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def existing_data
|
54
|
+
@existing_data ||= if File.exists?(path)
|
55
|
+
begin
|
56
|
+
JSON.parse(File.read(path))
|
57
|
+
rescue JSON::ParserError
|
58
|
+
# The file must not have been valid, so we will overwrite it
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RSpecRcv
|
2
|
+
module RSpec
|
3
|
+
module Metadata
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def configure!
|
7
|
+
::RSpec.configure do |config|
|
8
|
+
when_tagged_with_rcv = { rcv: lambda { |val| !!val } }
|
9
|
+
|
10
|
+
config.after(:each, when_tagged_with_rcv) do |ex|
|
11
|
+
example = ex.respond_to?(:metadata) ? ex : ex.example
|
12
|
+
opts = example.metadata[:rcv]
|
13
|
+
data_proc = lambda { |opts| instance_eval(&opts[:exportable_proc]) }
|
14
|
+
|
15
|
+
Handler.new(ex.file_path, data_proc, metadata: opts).call
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/rspec-rcv.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rspec-rcv/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rspec-rcv"
|
8
|
+
spec.version = RSpecRcv::VERSION
|
9
|
+
spec.authors = ["Stephen Bussey"]
|
10
|
+
spec.email = ["steve.bussey@salesloft.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Export results of rspec tests to disk for integration testing.}
|
13
|
+
spec.homepage = "https://github.com/SalesLoft/rspec-rcv"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.required_ruby_version = '>= 2.0.0'
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
+
spec.add_runtime_dependency "rspec"
|
23
|
+
spec.add_runtime_dependency "diffy"
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rspec-rcv
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Stephen Bussey
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: diffy
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- steve.bussey@salesloft.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".rspec"
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- lib/rspec-rcv.rb
|
83
|
+
- lib/rspec-rcv/configuration.rb
|
84
|
+
- lib/rspec-rcv/data_changed_error.rb
|
85
|
+
- lib/rspec-rcv/handler.rb
|
86
|
+
- lib/rspec-rcv/test_frameworks/rspec.rb
|
87
|
+
- lib/rspec-rcv/version.rb
|
88
|
+
- rspec-rcv.gemspec
|
89
|
+
homepage: https://github.com/SalesLoft/rspec-rcv
|
90
|
+
licenses:
|
91
|
+
- MIT
|
92
|
+
metadata: {}
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 2.0.0
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
requirements: []
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 2.4.6
|
110
|
+
signing_key:
|
111
|
+
specification_version: 4
|
112
|
+
summary: Export results of rspec tests to disk for integration testing.
|
113
|
+
test_files: []
|