From 55a8c21728c4e01fa16778052d2b82d2fd6dd8bf Mon Sep 17 00:00:00 2001 From: Swaathi Kakarla Date: Tue, 5 Aug 2025 20:03:14 +0530 Subject: [PATCH 1/3] Captures proc output instead of calling it --- .../expectation_helper_wrapper.rb | 11 ++- spec/stdout_matcher_spec.rb | 70 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 spec/stdout_matcher_spec.rb diff --git a/lib/rspec/enriched_json/expectation_helper_wrapper.rb b/lib/rspec/enriched_json/expectation_helper_wrapper.rb index 005483d..cdadb9e 100644 --- a/lib/rspec/enriched_json/expectation_helper_wrapper.rb +++ b/lib/rspec/enriched_json/expectation_helper_wrapper.rb @@ -4,6 +4,7 @@ require "oj" require "rspec/expectations" require "rspec/support/differ" +require "stringio" module RSpec module EnrichedJson @@ -46,7 +47,15 @@ def serialize_value(value) if value.is_a?(Regexp) return Oj.dump(value.inspect, mode: :compat) elsif value.is_a?(Proc) - return value.call + original_stdout = $stdout + $stdout = StringIO.new + + value.call + + output = $stdout.string + $stdout = original_stdout + + return output end Oj.dump(value, OJ_OPTIONS) diff --git a/spec/stdout_matcher_spec.rb b/spec/stdout_matcher_spec.rb new file mode 100644 index 0000000..6f8c6fe --- /dev/null +++ b/spec/stdout_matcher_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe "Stdout matcher value capture" do + let(:formatter) { RSpec::EnrichedJson::Formatters::EnrichedJsonFormatter.new(StringIO.new) } + + # Use the same Oj options for loading that we use for dumping + let(:oj_load_options) do + { + mode: :object, + symbol_keys: true, + auto_define: false, + create_additions: false + } + end + + it "captures actual when spec passes" do + test_file = "spec/support/stdout_passing_spec.rb" + File.write(test_file, <<~RUBY) + RSpec.describe "Test" do + it "checks output" do + expect { puts "Hello" }.to output("Hello\\n").to_stdout + end + end + RUBY + + output = run_rspec(test_file) + json = Oj.load(output) + example = json["examples"].first + + expect(example["details"]["expected"]).to eq("\"Hello\\n\"") + expect(example["details"]["actual"]).to eq("Hello\n") + ensure + File.delete(test_file) if File.exist?(test_file) + end + + it "captures actual when spec fails" do + test_file = "spec/support/stdout_passing_spec.rb" + File.write(test_file, <<~RUBY) + RSpec.describe "Test" do + it "checks output" do + expect { puts "Hello" }.to output("Bye\\n").to_stdout + end + end + RUBY + + output = run_rspec(test_file) + json = Oj.load(output) + example = json["examples"].first + + expect(example["details"]["expected"]).to eq("\"Bye\\n\"") + expect(example["details"]["actual"]).to eq("\"Hello\\n\"") + ensure + File.delete(test_file) if File.exist?(test_file) + end + + private + + def run_rspec(test_file) + output = nil + Dir.mktmpdir do |dir| + output_file = File.join(dir, "output.json") + cmd = "bundle exec rspec #{test_file} --format RSpec::EnrichedJson::Formatters::EnrichedJsonFormatter --out #{output_file} 2>&1" + system(cmd, out: File::NULL) + output = File.read(output_file) + end + output + end +end From 30b338812ce14add51e61dae7a59d4d3f0ccb5e5 Mon Sep 17 00:00:00 2001 From: Swaathi Kakarla Date: Tue, 5 Aug 2025 20:03:27 +0530 Subject: [PATCH 2/3] Bumps version --- lib/rspec/enriched_json/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rspec/enriched_json/version.rb b/lib/rspec/enriched_json/version.rb index 6d88539..45847a0 100644 --- a/lib/rspec/enriched_json/version.rb +++ b/lib/rspec/enriched_json/version.rb @@ -2,6 +2,6 @@ module RSpec module EnrichedJson - VERSION = "0.8.2" + VERSION = "0.8.3" end end From 28c7483808f2601a737286954b414d464b4eb988 Mon Sep 17 00:00:00 2001 From: Swaathi Kakarla Date: Tue, 5 Aug 2025 20:08:36 +0530 Subject: [PATCH 3/3] Improves demo and removes irrelevant spec --- demo.rb | 3 ++- spec/oj_serialization_spec.rb | 9 --------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/demo.rb b/demo.rb index 4365d25..d2d8084 100755 --- a/demo.rb +++ b/demo.rb @@ -414,7 +414,8 @@ def initialize "Custom Messages" => ["custom failure message", "custom message with block"], "Complex Objects" => ["struct comparison", "custom class object comparison", "test with metadata", "custom object with many instance variables"], "Fuzzy Matchers" => ["a_string_matching", "a_hash_including", "a_collection_containing_exactly", "an_instance_of", "include with hash conditions"], - "Yield Matchers" => ["yield_control", "yield_with_args"] + "Yield Matchers" => ["yield_control", "yield_with_args"], + "Output Matchers" => ["output to stdout", "output to stderr"] } categories.each do |category, descriptions| diff --git a/spec/oj_serialization_spec.rb b/spec/oj_serialization_spec.rb index 0bb7b7c..4f7a2f1 100644 --- a/spec/oj_serialization_spec.rb +++ b/spec/oj_serialization_spec.rb @@ -42,15 +42,6 @@ expect(result).to eq("/http:\\/\\/example\\.com/") end end - - describe "serialization of Proc objects" do - it "calls a simple Proc" do - helloworld = proc { "Hello, world!" } - result = serializer.serialize_value(helloworld) - expect(result).to eq("Hello, world!") - end - end - describe "Fallback behavior for errors" do it "uses fallback format when Oj.dump fails" do # Create an object that we'll mock to fail