1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
module RSpec
module Matchers
RSpec.describe Composable do
RSpec::Matchers.define :matcher_using_surface_descriptions_in do |expected|
match { false }
failure_message { surface_descriptions_in(expected).to_s }
end
it "does not blow up when surfacing descriptions from an unreadable IO object" do
expect {
expect(3).to matcher_using_surface_descriptions_in(STDOUT)
}.to fail_with(STDOUT.inspect)
end
it "does not blow up when surfacing descriptions from an unreadable Range object" do
infinity = (1.0/0.0)
infinite_range = -infinity..infinity
expect {
expect(1).to matcher_using_surface_descriptions_in(infinite_range)
}.to fail_with(infinite_range.inspect)
end
it "does not blow up when surfacing descriptions from an Enumerable object whose #each includes the object itself" do
array = ['something']
array << array
expect {
expect(1).to matcher_using_surface_descriptions_in(array)
}.to fail_with(array.to_s)
end
it "does not enumerate normal ranges" do
range = 1..3
expect {
expect(1).to matcher_using_surface_descriptions_in(range)
}.to fail_with(range.inspect)
end
it "doesn't mangle struct descriptions" do
model = Struct.new(:a).new(1)
expect {
expect(1).to matcher_using_surface_descriptions_in(model)
}.to fail_with(model.inspect)
end
RSpec::Matchers.define :all_but_one do |matcher|
match do |actual|
match_count = actual.count { |v| values_match?(matcher, v) }
actual.size == match_count + 1
end
end
context "when using a matcher instance that memoizes state multiple times in a composed expression" do
it "works properly in spite of the memoization" do
expect(["foo", "bar", "a"]).to all_but_one(have_string_length(3))
end
context "when passing a compound expression" do
it "works properly in spite of the memoization" do
expect(["A", "AB", "ABC"]).to all_but_one(
have_string_length(1).or have_string_length(2)
)
end
end
end
describe "cloning data structures containing matchers" do
include Composable
it "clones only the contained matchers" do
matcher_1 = eq(1)
matcher_2 = eq(2)
object = Object.new
uncloneable = nil
data_structure = {
"foo" => matcher_1,
"bar" => [matcher_2, uncloneable],
"bazz" => object
}
cloned = with_matchers_cloned(data_structure)
expect(cloned).not_to equal(data_structure)
expect(cloned["foo"]).to be_a_clone_of(matcher_1)
expect(cloned["bar"].first).to be_a_clone_of(matcher_2)
expect(cloned["bazz"]).to equal(object)
end
it "copies custom matchers properly so they can work even though they have singleton behavior" do
expect("foo").to with_matchers_cloned(have_string_length 3)
end
it 'does not blow up when passed an array containing an IO object' do
stdout = STDOUT
expect(with_matchers_cloned([stdout]).first).to equal(stdout)
end
end
describe "when an unexpected call stack jump occurs" do
RSpec::Matchers.define :cause_call_stack_jump do
supports_block_expectations
match do |block|
begin
block.call
false
rescue Exception # rubocop:disable Lint/RescueException
true
end
end
end
it "issue a warning suggesting `expects_call_stack_jump?` has been improperly declared" do
expect {
x = 0
expect { x += 1; exit }.to change { x }.and cause_call_stack_jump
}.to raise_error(/no match results, [\.\w\s]+ declare `expects_call_stack_jump\?`/)
end
end
end
end
end
|