moarspec 0.1.0
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/config/rubocop-rspec.yml +15 -0
- data/lib/moarspec/example_groups/instant_context.rb +58 -0
- data/lib/moarspec/example_groups.rb +20 -0
- data/lib/moarspec/its/block.rb +59 -0
- data/lib/moarspec/its/block_with.rb +46 -0
- data/lib/moarspec/its/call.rb +52 -0
- data/lib/moarspec/its/map.rb +77 -0
- data/lib/moarspec/its/with.rb +42 -0
- data/lib/moarspec/its.rb +49 -0
- data/lib/moarspec/matchers/be_json.rb +105 -0
- data/lib/moarspec/matchers/dont.rb +92 -0
- data/lib/moarspec/matchers/eq_multiline.rb +42 -0
- data/lib/moarspec/matchers/request_webmock.rb +60 -0
- data/lib/moarspec/matchers/ret.rb +103 -0
- data/lib/moarspec/matchers/send_message.rb +173 -0
- data/lib/moarspec/matchers.rb +22 -0
- data/lib/moarspec/util.rb +18 -0
- data/lib/moarspec/version.rb +8 -0
- data/lib/moarspec.rb +17 -0
- metadata +160 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: cbe02b386d8bb5096462151db51d932f06787af44b10f3710113ca27ce107d91
|
4
|
+
data.tar.gz: 8329a3c29236662afa9614d31094389d13e2ddcc1611b1873dc2c48fa8053b50
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: '000817d751bd76bd08cdb817f66f40fb57f93ac39cdf7c666c06fde4837aed7d3df21924e85a79a4932d79bece239315cdc50b3db201ab07a87876847de6132f'
|
7
|
+
data.tar.gz: 4424ee10f7129fe955efb375d50607eb95ac291e58afda48f2f2425c2ca3657e0a37f10c00973239e15a5b1426aebe622aef064c499dc9aad46b5003597fb147
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014-15 Victor 'Zverok' Shepelev
|
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
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moarspec
|
4
|
+
module ExampleGroups
|
5
|
+
# Provides a shorter way to define a context and its `let` values in one statement.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# subject { x + y }
|
9
|
+
#
|
10
|
+
# instant_context 'with numeric values', lets: {x: 1, y: 2} do
|
11
|
+
# it { is_expected.to eq 3 }
|
12
|
+
# end
|
13
|
+
# # or, without an explicit description:
|
14
|
+
# instant_context lets: {x: 1, y: 2} do
|
15
|
+
# it { is_expected.to eq 3 }
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# # is equivalent to
|
19
|
+
#
|
20
|
+
# context 'with numeric values (x=1, y=2)' do
|
21
|
+
# let(:x) { 1 }
|
22
|
+
# let(:y) { 2 }
|
23
|
+
#
|
24
|
+
# it { is_expected.to eq 3 }
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# # without explicit description, it is equivalent to
|
28
|
+
# context 'with x=1, y=2' do
|
29
|
+
# # ...
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# See also {Moarspec::Its::With#it_with #it_with} for a way to define just one example with
|
33
|
+
# its `let`s.
|
34
|
+
module InstantContext
|
35
|
+
def instant_context(description = nil, lets:, **metadata, &block)
|
36
|
+
full_description = "with #{lets.map { "#{_1}=#{_2.inspect}" }.join(', ')}"
|
37
|
+
full_description = "#{description} (#{full_description})" if description
|
38
|
+
absolute_path, line_number = caller_locations.first.then { [_1.absolute_path, _1.lineno] }
|
39
|
+
|
40
|
+
context full_description, **metadata do
|
41
|
+
# Tricking RSpec to think this context was defined where `instant_context` was called,
|
42
|
+
# so `rspec that_spec.rb:123` knew it is related
|
43
|
+
self.metadata.merge!(absolute_file_path: absolute_path, line_number: line_number)
|
44
|
+
|
45
|
+
lets.each do |name, val|
|
46
|
+
let(name) { val }
|
47
|
+
end
|
48
|
+
instance_eval(&block)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
RSpec.configure do |rspec|
|
56
|
+
rspec.extend Moarspec::ExampleGroups::InstantContext
|
57
|
+
rspec.backtrace_exclusion_patterns << %r{/lib/moarspec/example_groups/instant_context}
|
58
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moarspec
|
4
|
+
# Wrapper module for all RSpec additions that adjust example groups (`context`) creation.
|
5
|
+
#
|
6
|
+
# ## {InstantContext#instant_context #instant_context}
|
7
|
+
#
|
8
|
+
# ```ruby
|
9
|
+
# subject { x + y }
|
10
|
+
#
|
11
|
+
# instant_context 'with numeric values', lets: {x: 1, y: 2} do
|
12
|
+
# it { is_expected.to eq 3 }
|
13
|
+
# end
|
14
|
+
# ```
|
15
|
+
#
|
16
|
+
module ExampleGroups
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
require_relative 'example_groups/instant_context'
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moarspec
|
4
|
+
module Its
|
5
|
+
module Block
|
6
|
+
# Creates nested example that redefines implicit `is_expected` to use subject as a block.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# subject { calc_something(params) }
|
11
|
+
#
|
12
|
+
# # without its_block
|
13
|
+
# context 'with this params' do
|
14
|
+
# it { expect { subject }.to change(some, :value).by(1) }
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# context 'with that params' do
|
18
|
+
# it { expect { subject }.to raise_error(SomeError) }
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# # with its_block
|
22
|
+
# context 'with this params' do
|
23
|
+
# its_block { is_expected.to change(some, :value).by(1) }
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# context 'with that params' do
|
27
|
+
# its_block { is_expected.to raise_error(SomeError) }
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# @param options Options (metadata) that can be passed to usual RSpec example.
|
31
|
+
# @param block [Proc] The test itself. Inside it, `is_expected` is a synonom
|
32
|
+
# for `expect { subject }`.
|
33
|
+
#
|
34
|
+
def its_block(*options, &block)
|
35
|
+
# rubocop:disable Lint/NestedMethodDefinition
|
36
|
+
describe('as block') do
|
37
|
+
# FIXME: Not necessary? (Previously, wrapped the subject in lambda, now just repeats it)
|
38
|
+
let(:__call_subject) do
|
39
|
+
subject
|
40
|
+
end
|
41
|
+
|
42
|
+
def is_expected
|
43
|
+
expect { __call_subject }
|
44
|
+
end
|
45
|
+
|
46
|
+
example(nil, *options, &block)
|
47
|
+
end
|
48
|
+
# rubocop:enable Lint/NestedMethodDefinition
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
RSpec.configure do |rspec|
|
55
|
+
rspec.extend Moarspec::Its::Block
|
56
|
+
rspec.backtrace_exclusion_patterns << %r{/lib/moarspec/its/block}
|
57
|
+
end
|
58
|
+
|
59
|
+
RSpec::SharedContext.include Moarspec::Its::Block
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moarspec
|
4
|
+
module Its
|
5
|
+
module BlockWith
|
6
|
+
# Creates a nested context + example with `let` values defined from the arguments, and
|
7
|
+
# the subject treated as a block.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# subject { x + y }
|
12
|
+
#
|
13
|
+
# its_block_with(x: 1, y: nil) { is_expected.to raise_error }
|
14
|
+
#
|
15
|
+
# # is equivalent to
|
16
|
+
#
|
17
|
+
# context "with x=1, y=2" do
|
18
|
+
# let(:x) { 1 }
|
19
|
+
# let(:y) { nil }
|
20
|
+
#
|
21
|
+
# it { expect { subject }.to raise_error }
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
def its_block_with(**lets, &block)
|
25
|
+
context "with #{lets.map { "#{_1}=#{_2.inspect}" }.join(', ')} as block" do
|
26
|
+
lets.each do |name, val|
|
27
|
+
let(name) { val }
|
28
|
+
end
|
29
|
+
|
30
|
+
def is_expected # rubocop:disable Lint/NestedMethodDefinition
|
31
|
+
expect { subject }
|
32
|
+
end
|
33
|
+
|
34
|
+
example(nil, &block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
RSpec.configure do |rspec|
|
42
|
+
rspec.extend Moarspec::Its::BlockWith
|
43
|
+
rspec.backtrace_exclusion_patterns << %r{/lib/moarspec/its/block_with}
|
44
|
+
end
|
45
|
+
|
46
|
+
RSpec::SharedContext.include Moarspec::Its::BlockWith
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moarspec
|
4
|
+
module Its
|
5
|
+
module Call
|
6
|
+
# For `#call`-able subject, creates nested example where subject is called with arguments
|
7
|
+
# provided, allowing to apply block matchers like `.to change(something)` or `.to raise_error`
|
8
|
+
# to different calls in a DRY way.
|
9
|
+
#
|
10
|
+
# Also, plays really well with {RSpec::Matchers#ret #ret} block matcher.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# let(:array) { %i[a b c] }
|
14
|
+
#
|
15
|
+
# describe '#[]' do
|
16
|
+
# subject { array.method(:[]) }
|
17
|
+
#
|
18
|
+
# its_call(1) { is_expected.to ret :b }
|
19
|
+
# its_call(1..-1) { is_expected.to ret %i[b c] }
|
20
|
+
# its_call('foo') { is_expected.to raise_error TypeError }
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# describe '#push' do
|
24
|
+
# subject { array.method(:push) }
|
25
|
+
# its_call(5) { is_expected.to change(array, :length).by(1) }
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
def its_call(*args, **kwargs, &block)
|
29
|
+
# rubocop:disable Lint/NestedMethodDefinition
|
30
|
+
describe("(#{args.map(&:inspect).join(', ')})") do
|
31
|
+
let(:__call_subject) do
|
32
|
+
subject.call(*args, **kwargs)
|
33
|
+
end
|
34
|
+
|
35
|
+
def is_expected
|
36
|
+
expect { __call_subject }
|
37
|
+
end
|
38
|
+
|
39
|
+
example(nil, &block)
|
40
|
+
end
|
41
|
+
# rubocop:enable Lint/NestedMethodDefinition
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
RSpec.configure do |rspec|
|
48
|
+
rspec.extend Moarspec::Its::Call
|
49
|
+
rspec.backtrace_exclusion_patterns << %r{/lib/moarspec/its/call}
|
50
|
+
end
|
51
|
+
|
52
|
+
RSpec::SharedContext.include Moarspec::Its::Call
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moarspec
|
4
|
+
module Its
|
5
|
+
module Map
|
6
|
+
# Creates nested example which has current subject mapped
|
7
|
+
# by specified attribute as its subject.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# # with attribute
|
12
|
+
# subject { %w[test me please] }
|
13
|
+
# its_map(:length) { is_expected.to eq [4, 2, 6] }
|
14
|
+
#
|
15
|
+
# # with attribute chain
|
16
|
+
# its_map('reverse.upcase') { is_expected.to eq %w[TSET EM ESAELP] }
|
17
|
+
#
|
18
|
+
# # with Hash (or any other object responding to `#[]`)
|
19
|
+
# subject {
|
20
|
+
# [
|
21
|
+
# {title: 'Slaughterhouse Five', author: {first: 'Kurt', last: 'Vonnegut'}},
|
22
|
+
# {title: 'Hitchhickers Guide To The Galaxy', author: {first: 'Duglas', last: 'Adams'}}
|
23
|
+
# ]
|
24
|
+
# }
|
25
|
+
# its_map([:title]) { are_expected.to eq ['Slaughterhouse Five', 'Hitchhickers Guide To The Galaxy'] }
|
26
|
+
# # multiple attributes for nested hashes
|
27
|
+
# its_map([:author, :last]) { are_expected.to eq ['Vonnegut', 'Adams'] }
|
28
|
+
#
|
29
|
+
# @param attribute [String, Symbol, Array<String, Symbol>] Attribute name (String or Symbol), attribute chain
|
30
|
+
# (string separated with dots) or arguments to `#[]` method (Array)
|
31
|
+
# @param options Other options that can be passed to usual RSpec example.
|
32
|
+
# @param block [Proc] The test itself. Inside it, `is_expected` (or `are_expected`) is related to result
|
33
|
+
# of `map`ping the subject.
|
34
|
+
#
|
35
|
+
def its_map(attribute, *options, &block) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
36
|
+
# rubocop:disable Lint/NestedMethodDefinition
|
37
|
+
# TODO: better desciption for different cases
|
38
|
+
describe("map(&:#{attribute})") do
|
39
|
+
let(:__its_map_subject) do
|
40
|
+
if Array === attribute
|
41
|
+
if subject.all? { |s| Hash === s }
|
42
|
+
subject.map do |s|
|
43
|
+
attribute.inject(s) { |inner, attr| inner[attr] }
|
44
|
+
end
|
45
|
+
else
|
46
|
+
subject.map { |inner| inner[*attribute] }
|
47
|
+
end
|
48
|
+
else
|
49
|
+
attribute_chain = attribute.to_s.split('.').map(&:to_sym)
|
50
|
+
attribute_chain.inject(subject) do |inner_subject, attr|
|
51
|
+
inner_subject.map(&attr)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def is_expected
|
57
|
+
expect(__its_map_subject)
|
58
|
+
end
|
59
|
+
|
60
|
+
alias_method :are_expected, :is_expected
|
61
|
+
|
62
|
+
options << {} unless options.last.is_a?(Hash)
|
63
|
+
|
64
|
+
example(nil, *options, &block)
|
65
|
+
end
|
66
|
+
# rubocop:enable Lint/NestedMethodDefinition
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
RSpec.configure do |rspec|
|
73
|
+
rspec.extend Moarspec::Its::Map
|
74
|
+
rspec.backtrace_exclusion_patterns << %r{/lib/moarspec/its/map}
|
75
|
+
end
|
76
|
+
|
77
|
+
RSpec::SharedContext.include Moarspec::Its::Map
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moarspec
|
4
|
+
module Its
|
5
|
+
module With
|
6
|
+
# Creates a nested context + example with `let` values defined from the arguments.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# subject { x + y }
|
11
|
+
#
|
12
|
+
# it_with(x: 1, y: 2) { is_expected.to eq 3 }
|
13
|
+
#
|
14
|
+
# # is equivalent to
|
15
|
+
#
|
16
|
+
# context "with x=1, y=2" do
|
17
|
+
# let(:x) { 1 }
|
18
|
+
# let(:y) { 2 }
|
19
|
+
#
|
20
|
+
# it { is_expected.to eq 3 }
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# See also {Its::BlockWith#its_block_with #its_block_with} for a block form, and
|
24
|
+
# {ExampleGroups::InstantContext#instant_context #instant_context} for inline `context`+`let` definitions.
|
25
|
+
def it_with(**lets, &block)
|
26
|
+
context "with #{lets.map { "#{_1}=#{_2.inspect}" }.join(', ')}" do
|
27
|
+
lets.each do |name, val|
|
28
|
+
let(name) { val }
|
29
|
+
end
|
30
|
+
example(nil, &block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
RSpec.configure do |rspec|
|
38
|
+
rspec.extend Moarspec::Its::With
|
39
|
+
rspec.backtrace_exclusion_patterns << %r{/lib/moarspec/its/with}
|
40
|
+
end
|
41
|
+
|
42
|
+
RSpec::SharedContext.include Moarspec::Its::With
|
data/lib/moarspec/its.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moarspec
|
4
|
+
# Wrapper module for all `its_*` RSpec additions.
|
5
|
+
#
|
6
|
+
# ## {Map#its_map #its_map}
|
7
|
+
#
|
8
|
+
# ```ruby
|
9
|
+
# subject { %w[1 2 3] }
|
10
|
+
# its_map(:to_s) { is_expected.to eq [1, 2, 3] }
|
11
|
+
# ```
|
12
|
+
#
|
13
|
+
# ## {Call#its_call #its_call}
|
14
|
+
#
|
15
|
+
# ```ruby
|
16
|
+
# subject { [1, 2, 3].method(:[]) }
|
17
|
+
# its_call(2) { is_expected.to ret 3 }
|
18
|
+
# its_call('foo') { is_expected.to raise_error }
|
19
|
+
# ```
|
20
|
+
#
|
21
|
+
# ## {Block#its_block #its_block}
|
22
|
+
#
|
23
|
+
# ```ruby
|
24
|
+
# subject { something_action }
|
25
|
+
# its_block { is_expected.not_to raise_error }
|
26
|
+
# its_block { is_expected.to change(some, :value).by(1) }
|
27
|
+
# ```
|
28
|
+
#
|
29
|
+
# ## {With#it_with #it_with}
|
30
|
+
#
|
31
|
+
# ```ruby
|
32
|
+
# subject { x + y }
|
33
|
+
# it_with(x: 1, y: 2) { is_expected.to eq 3 }
|
34
|
+
# ```
|
35
|
+
#
|
36
|
+
# ## {BlockWith#its_block_with #its_block_with}
|
37
|
+
#
|
38
|
+
# ```ruby
|
39
|
+
# subject { x + y }
|
40
|
+
# its_block_with(x: 1, y: nil) { is_expected.to raise_error }
|
41
|
+
# ```
|
42
|
+
#
|
43
|
+
module Its
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
require_relative 'its/map'
|
48
|
+
require_relative 'its/block'
|
49
|
+
require_relative 'its/call'
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Moarspec
|
6
|
+
module Matchers
|
7
|
+
# @private
|
8
|
+
class BeJson
|
9
|
+
include RSpec::Matchers::Composable
|
10
|
+
include RSpec::Matchers # to have #match
|
11
|
+
|
12
|
+
ANY = Object.new.freeze
|
13
|
+
|
14
|
+
attr_reader :actual, :expected
|
15
|
+
|
16
|
+
def initialize(expected, **parse_opts)
|
17
|
+
@expected_matcher = @expected = expected
|
18
|
+
|
19
|
+
# wrap to make be_json('foo' => matcher) work, too
|
20
|
+
unless expected == ANY || expected.respond_to?(:matches?)
|
21
|
+
@expected_matcher = match(expected)
|
22
|
+
end
|
23
|
+
@parse_opts = parse_opts
|
24
|
+
end
|
25
|
+
|
26
|
+
def matches?(json)
|
27
|
+
@actual = JSON.parse(json, **@parse_opts)
|
28
|
+
@expected_matcher == ANY || @expected_matcher === @actual
|
29
|
+
rescue JSON::ParserError => e
|
30
|
+
@parser_error = e
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def does_not_match?(*args)
|
35
|
+
!matches?(*args)
|
36
|
+
end
|
37
|
+
|
38
|
+
def diffable?
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
def description
|
43
|
+
if @expected == ANY
|
44
|
+
'be a valid JSON string'
|
45
|
+
else
|
46
|
+
expected = @expected.respond_to?(:description) ? @expected.description : @expected
|
47
|
+
"be a valid JSON matching (#{expected})"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def failure_message
|
52
|
+
failed =
|
53
|
+
case
|
54
|
+
when @parser_error
|
55
|
+
"failed: #{@parser_error}"
|
56
|
+
when @expected != ANY
|
57
|
+
"was #{@actual}"
|
58
|
+
end
|
59
|
+
"expected value to #{description} but #{failed}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def failure_message_when_negated
|
63
|
+
'expected value not to be parsed as JSON, but succeeded'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module RSpec
|
70
|
+
module Matchers
|
71
|
+
# `be_json` checks if provided value is JSON, and optionally checks it contents.
|
72
|
+
#
|
73
|
+
# If you need to check against some hashes, it is more convenient to use `be_json_sym`, which
|
74
|
+
# parses JSON with `symbolize_names: true`.
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
#
|
78
|
+
# expect('{}').to be_json # ok
|
79
|
+
# expect('garbage').to be_json
|
80
|
+
# # expected value to be a valid JSON string but failed: 765: unexpected token at 'garbage'
|
81
|
+
#
|
82
|
+
# expect('{"foo": "bar"}').to be_json('foo' => 'bar') # ok
|
83
|
+
# expect('{"foo": "bar"}').to be_json_sym(foo: 'bar') # more convenient
|
84
|
+
#
|
85
|
+
# expect('{"foo": [1, 2, 3]').to be_json_sym(foo: array_including(3)) # nested matchers work
|
86
|
+
# expect(something_large).to be_json_sym(include(meta: include(next_page: Integer)))
|
87
|
+
#
|
88
|
+
# @param expected Value or matcher to check JSON against. It should implement `#===` method,
|
89
|
+
# so all standard and custom RSpec matchers work.
|
90
|
+
def be_json(expected = Moarspec::Matchers::BeJson::ANY)
|
91
|
+
Moarspec::Matchers::BeJson.new(expected)
|
92
|
+
end
|
93
|
+
|
94
|
+
# `be_json_sym` checks if value is a valid JSON and parses it with `symbolize_names: true`. This
|
95
|
+
# way, it is convenient to check hashes content with Ruby's short symbolic keys syntax.
|
96
|
+
#
|
97
|
+
# See {#be_json_sym} for examples.
|
98
|
+
#
|
99
|
+
# @param expected Value or matcher to check JSON against. It should implement `#===` method,
|
100
|
+
# so all standard and custom RSpec matchers work.
|
101
|
+
def be_json_sym(expected = Moarspec::Matchers::BeJson::ANY)
|
102
|
+
Moarspec::Matchers::BeJson.new(expected, symbolize_names: true)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moarspec
|
4
|
+
module Matchers
|
5
|
+
# @private
|
6
|
+
class Not < RSpec::Matchers::BuiltIn::BaseMatcher
|
7
|
+
def initialize(*)
|
8
|
+
super
|
9
|
+
@delegator = Delegator.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def description
|
13
|
+
"not #{@matcher.description}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def match(_expected, actual)
|
17
|
+
@matcher or fail ArgumentError, '`dont` matcher used without any matcher to negate. ' \
|
18
|
+
'Usage: dont.other_matcher(args)'
|
19
|
+
|
20
|
+
# https://www.rubydoc.info/github/rspec/rspec-expectations/RSpec%2FMatchers%2FMatcherProtocol:does_not_match%3F
|
21
|
+
# In a negative expectation such as `expect(x).not_to foo`, RSpec will call
|
22
|
+
# `foo.does_not_match?(x)` if this method is defined. If it's not defined it
|
23
|
+
# will fall back to using `!foo.matches?(x)`.
|
24
|
+
if @matcher.respond_to?(:does_not_match?)
|
25
|
+
@matcher.does_not_match?(actual)
|
26
|
+
else
|
27
|
+
!@matcher.matches?(actual)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def failure_message
|
32
|
+
@matcher.failure_message_when_negated
|
33
|
+
end
|
34
|
+
|
35
|
+
def supports_block_expectations?
|
36
|
+
@matcher.supports_block_expectations?
|
37
|
+
end
|
38
|
+
|
39
|
+
def method_missing(m, *a, &)
|
40
|
+
if @matcher
|
41
|
+
@matcher.send(m, *a, &)
|
42
|
+
else
|
43
|
+
@matcher = @delegator.send(m, *a, &)
|
44
|
+
end
|
45
|
+
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def respond_to_missing?(method, include_private = false)
|
50
|
+
if @matcher
|
51
|
+
@matcher.respond_to?(method, include_private)
|
52
|
+
else
|
53
|
+
@delegator.respond_to_missing?(method, include_private)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# ActiveSupport 7.1+ defines Object#with, and thus it doesn't go to `method_missing`.
|
58
|
+
# So, matchers like `dont.send_message(...).with(...)` stop working correctly.
|
59
|
+
#
|
60
|
+
# This is dirty, but I don't see another way.
|
61
|
+
if Object.instance_methods.include?(:with)
|
62
|
+
def with(...)
|
63
|
+
@matcher.with(...)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Delegator
|
68
|
+
include RSpec::Matchers
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
module RSpec
|
75
|
+
module Matchers
|
76
|
+
# Negates attached matcher, allowing creating negated matchers on the fly.
|
77
|
+
#
|
78
|
+
# While not being 100% grammatically correct, seems to be readable enough.
|
79
|
+
#
|
80
|
+
# @example
|
81
|
+
# # before
|
82
|
+
# RSpec.define_negated_matcher :not_change, :change
|
83
|
+
# it { expect { code }.to do_stuff.and not_change(obj, :attr) }
|
84
|
+
#
|
85
|
+
# # after: no `define_negated_matcher` needed
|
86
|
+
# it { expect { code }.to do_stuff.and dont.change(obj, :attr) }
|
87
|
+
#
|
88
|
+
def dont
|
89
|
+
Moarspec::Matchers::Not.new
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../util'
|
4
|
+
|
5
|
+
module Moarspec
|
6
|
+
module Matchers
|
7
|
+
# @private
|
8
|
+
class EqMultiline < RSpec::Matchers::BuiltIn::Eq
|
9
|
+
include Util
|
10
|
+
def initialize(expected)
|
11
|
+
super(multiline(expected))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module RSpec
|
18
|
+
module Matchers
|
19
|
+
# Allows to pretty test multiline strings with complex indentation (for example, results of
|
20
|
+
# code generation).
|
21
|
+
#
|
22
|
+
# In provided string, removes first and last empty line, trailing spaces and leading spaces up
|
23
|
+
# to `|` character.
|
24
|
+
#
|
25
|
+
# If you need to preserve trailing spaces, end them with another `|`.
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# require 'moarspec/matchers/eq_multiline'
|
29
|
+
#
|
30
|
+
# expect(some_code_gen).to eq_multiline(%{
|
31
|
+
# |def something
|
32
|
+
# | a = 5
|
33
|
+
# | a**2
|
34
|
+
# |end
|
35
|
+
# })
|
36
|
+
#
|
37
|
+
# @param expected [String]
|
38
|
+
def eq_multiline(expected)
|
39
|
+
Moarspec::Matchers::EqMultiline.new(expected)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# TODO: PR to webmock itself?..
|
2
|
+
#
|
3
|
+
# RSpec::Matchers.define :request_webmock do |url, method: :get|
|
4
|
+
# match do |block|
|
5
|
+
# WebMock.reset!
|
6
|
+
# stub_request(method, url)
|
7
|
+
# .tap { |req| req.with(@with_options) if @with_options && !@with_block }
|
8
|
+
# .tap { |req| req.with(@with_options, &@with_block) if @with_block }
|
9
|
+
# .tap { |req| req.to_return(@response) if @response }
|
10
|
+
# block.call
|
11
|
+
# matcher = have_requested(method, url)
|
12
|
+
# .tap { |matcher| matcher.with(@with_options) if @with_options && !@with_block }
|
13
|
+
# .tap { |matcher| matcher.with(@with_options, &@with_block) if @with_block }
|
14
|
+
# expect(WebMock).to matcher
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# chain :with do |options = {}, &block|
|
18
|
+
# @with_options = options
|
19
|
+
# @with_block = block
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# chain :once do
|
23
|
+
# times(1)
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# chain :twice do
|
27
|
+
# times(2)
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# chain :times do |n|
|
31
|
+
# @times = n
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# chain :at_least_once do
|
35
|
+
# at_least_times(1)
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# chain :at_least_twice do
|
39
|
+
# at_least_times(2)
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# chain :at_least_times do |n|
|
43
|
+
# @at_least_times = n
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# chain :returning do |response|
|
47
|
+
# @response =
|
48
|
+
# case response
|
49
|
+
# when String
|
50
|
+
# {body: response}
|
51
|
+
# when Hash
|
52
|
+
# response
|
53
|
+
# else
|
54
|
+
# fail "Expected string or Hash of params, got #{response.inspect}"
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# supports_block_expectations
|
59
|
+
# end
|
60
|
+
#
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moarspec
|
4
|
+
module Matchers
|
5
|
+
# @private
|
6
|
+
class Ret
|
7
|
+
include RSpec::Matchers::Composable
|
8
|
+
|
9
|
+
attr_reader :actual, :expected
|
10
|
+
|
11
|
+
def initialize(expected)
|
12
|
+
@expected = expected
|
13
|
+
end
|
14
|
+
|
15
|
+
def matches?(subject)
|
16
|
+
@subject = subject
|
17
|
+
return false unless subject.respond_to?(:call)
|
18
|
+
|
19
|
+
@actual = subject.call
|
20
|
+
@expected === @actual
|
21
|
+
end
|
22
|
+
|
23
|
+
def supports_block_expectations?
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def diffable?
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
def description
|
32
|
+
"return #{@expected.respond_to?(:description) ? @expected.description : @expected.inspect}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def failure_message
|
36
|
+
case
|
37
|
+
when !@subject.respond_to?(:call)
|
38
|
+
"expected to #{description}, but was not callable"
|
39
|
+
when @expected.respond_to?(:failure_message)
|
40
|
+
"return value mismatch: #{@expected.failure_message}"
|
41
|
+
else
|
42
|
+
"expected to #{description}, but returned #{@actual.inspect}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def failure_message_when_negated
|
47
|
+
case
|
48
|
+
when @expected.respond_to?(:failure_message_when_negated)
|
49
|
+
"return value mismatch: #{@expected.failure_message_when_negated}"
|
50
|
+
else
|
51
|
+
"expected not to #{description}, but returned it"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module RSpec
|
59
|
+
module Matchers
|
60
|
+
# `ret` (short for `return`) checks if provided block **returns** value specified.
|
61
|
+
#
|
62
|
+
# It should be considered instead of simple value matchers (like `eq`) in the situations:
|
63
|
+
#
|
64
|
+
# 1. Several block behaviors tested in the same test, joined with `.and`, or in separate tests
|
65
|
+
# 2. You test what some block or method returns with arguments, using
|
66
|
+
# {Moarspec::Its::Call#its_call #its_call}
|
67
|
+
#
|
68
|
+
# Values are tested with `===`, which allows chaining other matchers and patterns to the check.
|
69
|
+
#
|
70
|
+
# @note
|
71
|
+
# There is a case when `ret` fails: when it is _not the first_ in a chain of matchers joined
|
72
|
+
# by `.and`. That's not exactly the matchers bug, that's how RSpec works (loses block's return
|
73
|
+
# value passing the block between matchers)
|
74
|
+
#
|
75
|
+
# @example
|
76
|
+
# # case 1: block is a subject
|
77
|
+
# subject { -> { do_something } }
|
78
|
+
#
|
79
|
+
# it { is_expected.not_to raise_error }
|
80
|
+
# it { is_expected.to change(some, :value).by(1) }
|
81
|
+
# it { is_expected.to ret 8 }
|
82
|
+
#
|
83
|
+
# # or, joined:
|
84
|
+
# specify {
|
85
|
+
# expect { do_something }.to ret(8).and change(some, :value).by(1)
|
86
|
+
# }
|
87
|
+
#
|
88
|
+
# # case 2: with arguments
|
89
|
+
# subject { %i[a b c].method(:[]) }
|
90
|
+
#
|
91
|
+
# its_call(1) { is_expected.to ret :b }
|
92
|
+
# its_call(1..-1) { is_expected.to ret %i[b c] }
|
93
|
+
# its_call('foo') { is_expected.to raise_error TypeError }
|
94
|
+
#
|
95
|
+
# # Note, that values are tested with ===, which means all other matchers could be chained:
|
96
|
+
# its_call(1) { is_expected.to ret instance_of(Symbol) }
|
97
|
+
# its_call(1..-1) { is_expected.to ret instance_of(Array).and have_attributes(length: 2) }
|
98
|
+
#
|
99
|
+
def ret(expected)
|
100
|
+
Moarspec::Matchers::Ret.new(expected)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moarspec
|
4
|
+
module Matchers
|
5
|
+
# @private
|
6
|
+
class SendMessage
|
7
|
+
include RSpec::Mocks::ExampleMethods
|
8
|
+
include RSpec::Matchers::Composable
|
9
|
+
|
10
|
+
def initialize(target, method)
|
11
|
+
@target = target
|
12
|
+
@method = method
|
13
|
+
end
|
14
|
+
|
15
|
+
# DSL
|
16
|
+
def with(*arguments)
|
17
|
+
@arguments = arguments
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def returning(*res)
|
22
|
+
@res = [*res]
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def calling_original
|
27
|
+
@call_original = true
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def exactly(n)
|
32
|
+
@times = n
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def at_least(n)
|
37
|
+
@at_least = n
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def at_most(n)
|
42
|
+
@at_most = n
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def times
|
47
|
+
fail NoMethodError unless @times || @at_least
|
48
|
+
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def once
|
53
|
+
exactly(1)
|
54
|
+
end
|
55
|
+
|
56
|
+
def twice
|
57
|
+
exactly(2)
|
58
|
+
end
|
59
|
+
|
60
|
+
def thrice
|
61
|
+
exactly(3)
|
62
|
+
end
|
63
|
+
|
64
|
+
def ordered
|
65
|
+
@ordered = true
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def yielding(*args, &block)
|
70
|
+
@yield_args = args
|
71
|
+
@yield_block = block
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
# Matching
|
76
|
+
def matches?(subject)
|
77
|
+
run(subject)
|
78
|
+
expect(@target).to expectation
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
def does_not_match?(subject)
|
83
|
+
run(subject)
|
84
|
+
expect(@target).not_to expectation
|
85
|
+
true
|
86
|
+
end
|
87
|
+
|
88
|
+
# Static properties
|
89
|
+
def supports_block_expectations?
|
90
|
+
true
|
91
|
+
end
|
92
|
+
|
93
|
+
def description
|
94
|
+
format('send %p.%s', @target, @method)
|
95
|
+
end
|
96
|
+
|
97
|
+
def failure_message
|
98
|
+
"expected #{description}, but sent nothing"
|
99
|
+
end
|
100
|
+
|
101
|
+
def failure_message_when_negated
|
102
|
+
"expected not #{description}, but sent it"
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def run(subject)
|
108
|
+
@target.respond_to?(@method, true) or
|
109
|
+
fail NoMethodError,
|
110
|
+
"undefined method `#{@method}' for#{@target.inspect}:#{@target.class}"
|
111
|
+
allow(@target).to allower
|
112
|
+
subject.call
|
113
|
+
end
|
114
|
+
|
115
|
+
def allower
|
116
|
+
receive(@method).tap do |a|
|
117
|
+
a.and_return(*@res) if @res
|
118
|
+
a.and_call_original if @call_original
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def expectation
|
123
|
+
have_received(@method).tap do |e|
|
124
|
+
e.with(*@arguments) if @arguments
|
125
|
+
e.exactly(@times).times if @times
|
126
|
+
e.at_least(@at_least).times if @at_least
|
127
|
+
e.at_most(@at_most).times if @at_most
|
128
|
+
e.ordered if @ordered
|
129
|
+
e.and_yield(*@yield_args, &@yield_block) if @yield_args
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
module RSpec
|
137
|
+
module Matchers
|
138
|
+
# Checks if the (block) subject sends specified message to specified object.
|
139
|
+
#
|
140
|
+
# @example
|
141
|
+
# # before:
|
142
|
+
# specify {
|
143
|
+
# allow(double).to receive(:fetch)
|
144
|
+
# code_being_tested
|
145
|
+
# expect(double).to have_received(:fetch).with(something)
|
146
|
+
# }
|
147
|
+
#
|
148
|
+
# # after:
|
149
|
+
# require 'moarspec/matchers/send_message'
|
150
|
+
#
|
151
|
+
# it { expect { code_being_tested }.to send_message(double, :fetch).with(something) }
|
152
|
+
#
|
153
|
+
# # after + its_block
|
154
|
+
# require 'moarspec/its/block'
|
155
|
+
#
|
156
|
+
# subject { code_being_tested }
|
157
|
+
# its_block { is_expected.to send_message(double, :fetch).with(something) }
|
158
|
+
#
|
159
|
+
# @param target Object which expects message, double or real object
|
160
|
+
# @param method [Symbol] Message being expected
|
161
|
+
#
|
162
|
+
# @return Instance of a matcher, allowing the following additional methods:
|
163
|
+
#
|
164
|
+
# * `once`, `twice`, `exactly(n).times`;
|
165
|
+
# * `with(arguments)`;
|
166
|
+
# * `calling_original`;
|
167
|
+
# * `returning(response)`.
|
168
|
+
#
|
169
|
+
def send_message(target, method)
|
170
|
+
Moarspec::Matchers::SendMessage.new(target, method)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moarspec
|
4
|
+
# All Moarspec matchers, when required, included into `RSpec::Matchers` namespace.
|
5
|
+
#
|
6
|
+
# See:
|
7
|
+
#
|
8
|
+
# * {RSpec::Matchers#dont #dont}: `expect { block }.to change(this).and dont.change(that)`
|
9
|
+
# * {RSpec::Matchers#send_message #send_message}: `expect { block }.to send_message(File, :write)`
|
10
|
+
# * {RSpec::Matchers#ret #ret}: `expect { block }.to ret value`
|
11
|
+
# * {RSpec::Matchers#be_json #be_json}: `expect(response.body).to be_json('foo' => 'bar')`
|
12
|
+
# * {RSpec::Matchers#eq_multiline #eq_multiline}: multiline equality akin to squiggly heredoc
|
13
|
+
#
|
14
|
+
module Matchers
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
require_relative 'matchers/eq_multiline'
|
19
|
+
require_relative 'matchers/send_message'
|
20
|
+
require_relative 'matchers/ret'
|
21
|
+
require_relative 'matchers/dont'
|
22
|
+
require_relative 'matchers/be_json'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Moarspec
|
4
|
+
module Util
|
5
|
+
def multiline(string)
|
6
|
+
# 1. for all lines looking like "<spaces>|" -- remove this.
|
7
|
+
# 2. remove trailing spaces
|
8
|
+
# 3. preserve trailing spaces ending with "|", but remove the pipe
|
9
|
+
# 4. remove one empty line before & after, allows prettier %Q{}
|
10
|
+
# TODO: check if all lines start with "|"?
|
11
|
+
string
|
12
|
+
.gsub(/^ *\|/, '')
|
13
|
+
.gsub(/ +$/, '')
|
14
|
+
.gsub(/\|$/, '')
|
15
|
+
.gsub(/(\A *\n|\n *\z)/, '')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/moarspec.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
defined?(RSpec) or
|
4
|
+
fail 'RSpec is not present in the current environment, check that `rspec` ' \
|
5
|
+
'is present in your Gemfile and is in the same group as `moarspec`' \
|
6
|
+
|
7
|
+
# Umbrella module for all Moarspec RSpec DRY-ing features.
|
8
|
+
#
|
9
|
+
# See {file:README.md} or {Its}, {Matchers}, and {ExampleGroups} separately.
|
10
|
+
#
|
11
|
+
module Moarspec
|
12
|
+
end
|
13
|
+
|
14
|
+
require_relative 'moarspec/its'
|
15
|
+
require_relative 'moarspec/matchers'
|
16
|
+
require_relative 'moarspec/example_groups'
|
17
|
+
require_relative 'moarspec/util'
|
metadata
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: moarspec
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Victor Shepelev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-06-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rubocop
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.76.0
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.76.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.7.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.7.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec-its
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
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: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.9'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.9'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubygems-tasks
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: yard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description:
|
112
|
+
email: zverok.offline@gmail.com
|
113
|
+
executables: []
|
114
|
+
extensions: []
|
115
|
+
extra_rdoc_files: []
|
116
|
+
files:
|
117
|
+
- LICENSE.txt
|
118
|
+
- config/rubocop-rspec.yml
|
119
|
+
- lib/moarspec.rb
|
120
|
+
- lib/moarspec/example_groups.rb
|
121
|
+
- lib/moarspec/example_groups/instant_context.rb
|
122
|
+
- lib/moarspec/its.rb
|
123
|
+
- lib/moarspec/its/block.rb
|
124
|
+
- lib/moarspec/its/block_with.rb
|
125
|
+
- lib/moarspec/its/call.rb
|
126
|
+
- lib/moarspec/its/map.rb
|
127
|
+
- lib/moarspec/its/with.rb
|
128
|
+
- lib/moarspec/matchers.rb
|
129
|
+
- lib/moarspec/matchers/be_json.rb
|
130
|
+
- lib/moarspec/matchers/dont.rb
|
131
|
+
- lib/moarspec/matchers/eq_multiline.rb
|
132
|
+
- lib/moarspec/matchers/request_webmock.rb
|
133
|
+
- lib/moarspec/matchers/ret.rb
|
134
|
+
- lib/moarspec/matchers/send_message.rb
|
135
|
+
- lib/moarspec/util.rb
|
136
|
+
- lib/moarspec/version.rb
|
137
|
+
homepage: https://github.com/zverok/moarspec
|
138
|
+
licenses:
|
139
|
+
- MIT
|
140
|
+
metadata: {}
|
141
|
+
post_install_message:
|
142
|
+
rdoc_options: []
|
143
|
+
require_paths:
|
144
|
+
- lib
|
145
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 3.1.0
|
150
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
155
|
+
requirements: []
|
156
|
+
rubygems_version: 3.5.22
|
157
|
+
signing_key:
|
158
|
+
specification_version: 4
|
159
|
+
summary: Several additions for DRYer RSpec code
|
160
|
+
test_files: []
|