Skip to content

Instantly share code, notes, and snippets.

@mhaylock
Created August 29, 2016 04:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mhaylock/6186465ff043874165c0c8b63ff6d4bb to your computer and use it in GitHub Desktop.
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).
# 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