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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
RSpec.shared_examples "output_to_stream" do |stream_name, matcher_method, helper_module|
include helper_module
extend helper_module
it_behaves_like "an RSpec block-only matcher" do
let(:matcher) { output(/fo/).send(matcher_method) }
def valid_block
print_to_stream('foo')
end
def invalid_block
end
end
define_method :matcher do |*args|
output(args.first).send(matcher_method)
end
it 'is diffable' do
expect(matcher).to be_diffable
end
it 'does not produce warnings when the failure message is accessed first' do
expect($VERBOSE).to be_truthy
expect { matcher.failure_message }.not_to output.to_stderr
end
context "expect { ... }.to output.#{matcher_method}" do
it "passes if the block outputs to #{stream_name}" do
expect { print_to_stream 'foo' }.to matcher
end
it "fails if the block does not output to #{stream_name}" do
expect {
expect {}.to matcher
}.to fail_with("expected block to output to #{stream_name}, but did not")
end
end
context "expect { ... }.not_to output.#{matcher_method}" do
it "passes if the block does not output to #{stream_name}" do
expect {}.not_to matcher
end
it "fails if the block outputs to #{stream_name}" do
expect {
expect { print_to_stream 'foo' }.not_to matcher
}.to fail_with("expected block to not output to #{stream_name}, but output \"foo\"")
end
end
context "expect { ... }.to output('string').#{matcher_method}" do
it "passes if the block outputs that string to #{stream_name}" do
expect { print_to_stream 'foo' }.to matcher("foo")
end
it "fails if the block does not output to #{stream_name}" do
expect {
expect {}.to matcher('foo')
}.to fail_with("expected block to output \"foo\" to #{stream_name}, but output nothing")
end
it "fails if the block outputs a different string to #{stream_name}" do
expect {
expect { print_to_stream 'food' }.to matcher('foo')
}.to fail_with("expected block to output \"foo\" to #{stream_name}, but output \"food\"")
end
end
context "expect { ... }.to_not output('string').#{matcher_method}" do
it "passes if the block outputs a different string to #{stream_name}" do
expect { print_to_stream 'food' }.to_not matcher('foo')
end
it "passes if the block does not output to #{stream_name}" do
expect {}.to_not matcher('foo')
end
it "fails if the block outputs the same string to #{stream_name}" do
expect {
expect { print_to_stream 'foo' }.to_not matcher('foo')
}.to fail_with("expected block to not output \"foo\" to #{stream_name}, but output \"foo\"")
end
end
context "expect { ... }.to output(/regex/).#{matcher_method}" do
it "passes if the block outputs a string to #{stream_name} that matches the regex" do
expect { print_to_stream 'foo' }.to matcher(/foo/)
end
it "fails if the block does not output to #{stream_name}" do
expect {
expect {}.to matcher(/foo/)
}.to fail_including("expected block to output /foo/ to #{stream_name}, but output nothing\nDiff")
end
it "fails if the block outputs a string to #{stream_name} that does not match" do
expect {
expect { print_to_stream 'foo' }.to matcher(/food/)
}.to fail_including("expected block to output /food/ to #{stream_name}, but output \"foo\"\nDiff")
end
end
context "expect { ... }.to_not output(/regex/).#{matcher_method}" do
it "passes if the block outputs a string to #{stream_name} that does not match the regex" do
expect { print_to_stream 'food' }.to_not matcher(/bar/)
end
it "passes if the block does not output to #{stream_name}" do
expect {}.to_not matcher(/foo/)
end
it "fails if the block outputs a string to #{stream_name} that matches the regex" do
expect {
expect { print_to_stream 'foo' }.to_not matcher(/foo/)
}.to fail_including("expected block to not output /foo/ to #{stream_name}, but output \"foo\"\nDiff")
end
end
context "expect { ... }.to output(matcher).#{matcher_method}" do
it "passes if the block outputs a string to #{stream_name} that passes the given matcher" do
expect { print_to_stream 'foo' }.to matcher(a_string_starting_with("f"))
end
it "fails if the block outputs a string to #{stream_name} that does not pass the given matcher" do
expect {
expect { print_to_stream 'foo' }.to matcher(a_string_starting_with("b"))
}.to fail_including("expected block to output a string starting with \"b\" to #{stream_name}, but output \"foo\"\nDiff")
end
end
context "expect { ... }.to_not output(matcher).#{matcher_method}" do
it "passes if the block does not output a string to #{stream_name} that passes the given matcher" do
expect { print_to_stream 'foo' }.to_not matcher(a_string_starting_with("b"))
end
it "fails if the block outputs a string to #{stream_name} that passes the given matcher" do
expect {
expect { print_to_stream 'foo' }.to_not matcher(a_string_starting_with("f"))
}.to fail_including("expected block to not output a string starting with \"f\" to #{stream_name}, but output \"foo\"\nDiff")
end
end
end
module RSpec
module Matchers
RSpec.describe "output.to_stderr matcher" do
include_examples "output_to_stream", :stderr, :to_stderr, Module.new {
def print_to_stream(msg)
$stderr.print(msg)
end
}
end
RSpec.describe "output.to_stdout matcher" do
include_examples "output_to_stream", :stdout, :to_stdout, Module.new {
def print_to_stream(msg)
print(msg)
end
}
end
RSpec.describe "output.to_stderr_from_any_process matcher" do
include_examples "output_to_stream", :stderr, :to_stderr_from_any_process, Module.new {
def print_to_stream(msg)
if RSpec::Support::OS.windows?
system("<nul set /p msg=\"#{msg}\" 1>&2")
else
system("printf #{msg} 1>&2")
end
end
}
end
RSpec.describe "output.to_stdout_from_any_process matcher" do
include_examples "output_to_stream", :stdout, :to_stdout_from_any_process, Module.new {
def print_to_stream(msg)
if RSpec::Support::OS.windows?
system("<nul set /p msg=#{msg}")
else
system("printf #{msg}")
end
end
}
end
RSpec.describe "output (without `to_stdout` or `to_stderr`)" do
it 'raises an error explaining the use is invalid' do
expect {
expect { print 'foo' }.to output
}.to raise_error(/must chain.*to_stdout.*to_stderr/)
end
it 'still provides a description (e.g. when used in a one-liner)' do
expect(output("foo").description).to eq('output "foo" to some stream')
end
end
RSpec.describe "can capture stdin and stderr" do
it "prints diff for both when both fail" do
expect {
expect {
print "foo"; $stderr.print("bar")
}.to output(/baz/).to_stdout.and output(/qux/).to_stderr
}.to fail_including(
'expected block to output /baz/ to stdout, but output "foo"',
'...and:',
'expected block to output /qux/ to stderr, but output "bar"',
'Diff for (output /baz/ to stdout):',
'-/baz/',
'+"foo"',
'Diff for (output /qux/ to stderr):',
'-/qux/',
'+"bar"'
)
end
end
end
end
|