Created
August 29, 2016 04:10
-
-
Save mhaylock/6186465ff043874165c0c8b63ff6d4bb to your computer and use it in GitHub Desktop.
An Spec support file for capturing useful debug artifacts when a feature spec fails (integrates with CircleCI to make those artifacts available for review in the "Artifacts" tab of the build).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
# When a feature spec fails it can sometimes be difficult to determine the | |
# cause, here we hook into RSpec and if a feature spec fails we save the | |
# following artifacts to disk: | |
# | |
# - A copy of the HTML of the currently open page. | |
# - A screenshot of the currently open page (if the Capybara driver supports | |
# screenshots). | |
# - A copy of the segment of log/test.log that was generated for the failing | |
# spec example. | |
# | |
# The filename and location of these artifacts is printed to STDOUT when the | |
# failure occurs. When run on CircleCI these artifacts are saved into | |
# $CIRCLE_ARTIFACTS, making them available within the CircleCI build interface. | |
# | |
# Only the first few failures in a spec run have artifacts saved, to avoid | |
# wastage of CPU and disk space if a large number of failures of occur at once. | |
RSpec.configure do |config| | |
feature_failure_count = 0 | |
artifacts_path = Rails.root.join('tmp', 'spec_artifacts', 'features') | |
if ENV['CIRCLE_ARTIFACTS'].present? | |
circle_artifacts_path = Pathname.new(ENV['CIRCLE_ARTIFACTS']) | |
artifacts_path = circle_artifacts_path.join('specs', 'features') | |
end | |
def relative_artifact_path(path) | |
if ENV['CIRCLE_ARTIFACTS'].present? | |
circle_artifacts_path = Pathname.new(ENV['CIRCLE_ARTIFACTS']) | |
relative_path = path.relative_path_from circle_artifacts_path | |
"$CIRCLE_ARTIFACTS/#{relative_path}" | |
else | |
path.relative_path_from Rails.root | |
end | |
end | |
config.before(:suite) do | |
feature_failure_count = 0 | |
if artifacts_path.directory? | |
# If the artifacts directory already exists, delete it and any files | |
# it contains: | |
artifacts_path.rmtree | |
end | |
# Create directory: | |
artifacts_path.mkpath | |
end | |
config.before(:each, type: :feature) do | |
# Make sure all logger output so far has been flushed to disk: | |
Rails.logger.flush | |
# Save the byte size from the Rails log - if there is a failure we will | |
# use this size to determine how many bytes we want to save from the log | |
# as an artifact: | |
@rails_log_bytesize_before = Rails.root.join('log', 'test.log').size | |
end | |
config.after(:each, type: :feature) do |example| | |
# Save debug information for this feature spec failure | |
if example.exception | |
feature_failure_count += 1 | |
if feature_failure_count > 3 | |
puts "Feature spec '#{example.description}' failed:" | |
puts "\tNot saving artifacts as too many failures " \ | |
"(#{feature_failure_count}) have occurred" | |
else | |
artifact_prefix = format('failure-%02d-', feature_failure_count) | |
puts "\n\n" | |
puts "Feature spec '#{example.description}' failed:" | |
page_path = artifacts_path.join "#{artifact_prefix}page.html" | |
save_page page_path | |
puts "\tPage saved to #{relative_artifact_path page_path}" | |
if example.metadata[:js] | |
screenshot_path = | |
artifacts_path.join "#{artifact_prefix}screenshot.png" | |
save_screenshot screenshot_path # rubocop:disable Lint/Debugger | |
puts "\tScreenshot saved to #{relative_artifact_path screenshot_path}" | |
else | |
puts "\tScreenshot not available outside of javascript tests" | |
end | |
# Make sure all logger output so far has been flushed to disk: | |
Rails.logger.flush | |
# Determine the number of bytes added to the log since this | |
# spec example began: | |
rails_log_path = Rails.root.join('log', 'test.log') | |
current_rails_log_bytesize = rails_log_path.size | |
bytes_to_copy = current_rails_log_bytesize - @rails_log_bytesize_before | |
log_path = artifacts_path.join "#{artifact_prefix}test.log" | |
# Copy the last N bytes into the artifact log path: | |
`tail -c#{bytes_to_copy} #{rails_log_path} > #{log_path}` | |
puts "\tLog segment saved to #{relative_artifact_path log_path}" | |
puts "\n" | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment