Transform RSpec's JSON output into formatted HTML with syntax highlighting, side-by-side diffs, and Bootstrap styling.
rspec-html_messages
takes the enriched JSON output from rspec-enriched_json
and renders it as HTML. It provides:
- Beautiful formatting - Clean, Bootstrap-styled output.
- Side-by-side diffs - Visual comparison of expected vs actual values.
- Smart data rendering - Pretty-printing for complex objects.
- Composable API - Use individual components or the full renderer.
- Flexible options - Control diff display and message formatting.
Add to your Gemfile:
gem "rspec-html_messages"
Or install directly:
$ gem install rspec-html_messages
require "rspec/html_messages"
# Get enriched JSON from RSpec
# (typically from running with rspec-enriched_json formatter)
example_json = {
"id" => "spec/example_spec.rb[1:1]",
"description" => "should equal 42",
"status" => "failed",
"file_path" => "spec/example_spec.rb",
"line_number" => 10,
"details" => {
"expected" => '"42"',
"actual" => '"41"',
"matcher_name" => "RSpec::Matchers::BuiltIn::Eq",
"diffable" => true
},
"exception" => {
"message" => "expected: \"42\"\n got: \"41\"\n\n(compared using ==)"
}
}
# Render as HTML
renderer = Rspec::HtmlMessages.new(example_json)
html = renderer.render_html
# Output includes styled HTML with diff
puts html
The gem provides a composable API where you can use individual components to build your own custom layouts:
renderer = Rspec::HtmlMessages.new(example_json)
# Check what content is available
if renderer.has_output?
output_html = renderer.output_html
end
if renderer.has_failure_message?
failure_html = renderer.failure_message_html
end
if renderer.has_exception_details?
exception_html = renderer.exception_details_html
end
if renderer.has_backtrace?
exception_html = renderer.backtrace_html
end
# Build your own custom layout
html = <<~HTML
<div class="my-custom-test-result">
#{output_html if renderer.has_output?}
#{failure_html if renderer.has_failure_message?}
#{exception_html if renderer.has_exception_details?}
</div>
HTML
You can customize the rendering with various options:
# Options can be passed to the render_html method
html = renderer.render_html(
force_diffable: ["CustomMatcher"], # Array of matchers to always show diffs for
force_not_diffable: ["RSpec::Matchers::BuiltIn::Include"], # Array of matchers to never show diffs for
rspec_diff_in_message: true, # Include RSpec's text diff in failure message (default: false)
backtrace_max_lines: 10, # Maximum backtrace lines to show (default: 10)
backtrace_silence_gems: true # Filter out gem frames from backtraces (default: true)
)
# Or to individual component methods
output_html = renderer.output_html(force_diffable: ["CustomMatcher"])
-
force_diffable
: Array of matcher class names that should always show diffs, even if they report as non-diffable.- Default:
["RSpec::Matchers::BuiltIn::ContainExactly"]
(used bycontain_exactly
andmatch_array
). - Override by passing your own array.
- Default:
-
force_not_diffable
: Array of matcher class names that should never show diffs, even if they report as diffable.- Default:
["RSpec::Matchers::BuiltIn::Include", "RSpec::Matchers::BuiltIn::Compound::And", "RSpec::Matchers::BuiltIn::Compound::Or"]
. - Override by passing your own array.
- Default:
-
rspec_diff_in_message
: By default, RSpec's text-based diff is stripped from failure messages since we show a visual diff. Set totrue
to keep it. -
backtrace_max_lines
: Maximum number of backtrace lines to display for errors.- Default:
10
. - Set to a higher number to see more of the stack trace.
- Default:
-
backtrace_silence_gems
: Whether to filter out gem frames from backtraces.- Default:
true
(hides frames from installed gems). - Set to
false
to see the complete backtrace including gem internals.
- Default:
Here's a complete example that processes RSpec output and generates an HTML report:
require "json"
require "rspec/html_messages"
# Run RSpec with enriched JSON formatter
json_output = `bundle exec rspec --require rspec/enriched_json \
-f RSpec::EnrichedJson::Formatters::EnrichedJsonFormatter`
# Parse the JSON
results = JSON.parse(json_output)
# Generate HTML report
html = <<~HTML
<!DOCTYPE html>
<html>
<head>
<title>RSpec Results</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container my-4">
<h1>Test Results</h1>
<p>#{results["summary_line"]}</p>
HTML
# Render each example
results["examples"].each do |example|
renderer = Rspec::HtmlMessages.new(example)
html << <<~EXAMPLE
<div class="mb-4">
<h3>#{example["description"]}</h3>
#{renderer.render_html}
</div>
EXAMPLE
end
html << <<~HTML
</div>
</body>
</html>
HTML
File.write("rspec_results.html", html)
For a passing test, you'll see:
- Green checkmark and background.
- Test description and file location.
- No failure message or diff.
For a failing test with diffable values:
- Red X and background.
- Test description and file location.
- Side-by-side comparison showing differences.
- Failure message (with RSpec's diff stripped by default).
For tests that encounter errors (exceptions) before assertions:
- Exception class name highlighted in red.
- Stack trace with configurable depth.
- Gem frames filtered by default (configurable).
This gem is designed to work with rspec-enriched_json
, which provides structured data about test failures including:
- Expected and actual values as structured data (not just strings).
- Matcher information.
- Diffable status.
- Original failure messages.
To use both gems together:
-
Add both gems to your Gemfile:
gem "rspec-enriched_json" gem "rspec-html_messages"
-
Run RSpec with the enriched JSON formatter:
bundle exec rspec --require rspec/enriched_json \ -f RSpec::EnrichedJson::Formatters::EnrichedJsonFormatter
-
Process the output with rspec-html_messages as shown in the examples above.
Creates a new renderer instance with the example JSON data.
Returns true
if the example has output to display (failed tests or tests with actual values).
Returns true
if the example has a failure message to display.
Returns true
if the example has exception/error details to display.
Returns true
if the example has a backtrace to display.
Renders just the output section (diff or actual value). Returns nil
if no output to display.
Renders just the failure message section. Returns nil
if no failure message.
Renders just the exception details section. Returns nil
if no exception.
Renders just the backtrace section. Returns nil
if no backtrace.
Renders just the status section.
Convenience method that renders all four sections in a standard layout.
Returns the CSS needed for diff display styling. This is a class method that provides the minimal CSS required to properly display the side-by-side diffs generated by the Diffy gem.
Important: This CSS is NOT automatically included in the HTML output. You must manually include it in your CSS pipeline or HTML template.
# Get the CSS for diffs
css = Rspec::HtmlMessages.diff_css
# Include it in your HTML
html = <<~HTML
<!DOCTYPE html>
<html>
<head>
<style>
#{css}
</style>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<!-- Your test results here -->
</body>
</html>
HTML
The CSS includes:
- Styles for diff containers (
.diff
) and lists - Color coding for additions (
.ins
- green) and deletions (.del
- red) - Hover effects (yellow highlight) for better readability
- Proper handling of whitespace in diffs
- Highlighting for changed portions within lines (using
<strong>
tags)
Without this CSS, the diff output will not display correctly.
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests.
To install this gem onto your local machine, run bundle exec rake install
.
Bug reports and pull requests are welcome on GitHub at https://github.com/firstdraft/rspec-html_messages.
The gem is available as open source under the terms of the MIT License.