Skip to content

Instantly share code, notes, and snippets.

@pboling
Created September 3, 2023 23:30
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 pboling/767b0b237d7eb4188fd08e0eab7089d1 to your computer and use it in GitHub Desktop.
Save pboling/767b0b237d7eb4188fd08e0eab7089d1 to your computer and use it in GitHub Desktop.
Simple Status Persistence & Tracking for RSpec
# This will not run on CI, because we don't need to store a "last run" on CI.
# On local dev it will allow you to run:
# bundle exec rspec --only-failures
# Which will only run the tests that failed last time.
persistence_file_path = "test-reports/last_run_status.txt"
class SpecOut
attr_reader :run_tracker
@instance_mutex = Mutex.new
private_class_method :new
# Called by #instance
def initialize(examples, persistence_path)
@run_tracker = {}
self[:tracking] = File.exist?(persistence_path)
return unless tracking?
self[:previous_example_ids] = RSpec::Core::ExampleStatusPersister
.load_from(persistence_path)
.pluck(:example_id)
self[:current_example_ids] = examples.map(&:id)
self[:current_example_count] = self[:current_example_ids].length
self[:previous_example_count] = self[:previous_example_ids].length
self[:full_suite] = (self[:previous_example_ids] - self[:current_example_ids]).empty?
self[:newly_added_tests_count] = (self[:current_example_ids] - self[:previous_example_ids]).length
end
def [](key)
self.run_tracker[key]
end
def []=(key, val)
self.run_tracker[key] = val
end
def tracking?
self[:tracking]
end
def partial?
!full_suite?
end
def full_suite?
self[:full_suite]
end
class << self
extend Forwardable
def_delegators :@instance, :[], :[]=, :full_suite?, :partial?, :tracking?
# The static method that controls the access to the singleton instance.
#
# NOTE: This implementation let you subclass the Singleton class while keeping just
# one instance of each subclass.
# See: https://refactoring.guru/design-patterns/singleton/ruby/example#example-1
# rubocop:disable ThreadSafety/InstanceVariableInClassMethod.
def instance(*args)
return @instance if @instance
@instance_mutex.synchronize do
@instance ||= new(*args)
end
@instance
end
def spout(message)
out = message.dup
out = "\e[A" + message.gsub(/^(.*?)(\n|$)/, "\e[34m\\1\e[39m\\2") if $stderr.tty?
puts out
end
end
end
RSpec.configure do |config|
config.example_status_persistence_file_path = persistence_file_path
### TRACK RUNS
config.before :suite do
next unless SpecOut.instance(config.world.all_examples, persistence_file_path).tracking?
SpecOut.spout("[SpecOut]: Will run #{SpecOut[:current_example_count]} tests\n\n")
SpecOut.spout("[SpecOut]: Newly added tests: #{SpecOut[:newly_added_tests_count]}\n\n")
SpecOut.spout("[SpecOut]: Last complete run was #{SpecOut[:previous_example_count]} tests\n\n")
end
# Run at_exit so that it prints after RSpec's summary
config.after :suite do
at_exit do
next unless SpecOut.instance.tracking?
message = if SpecOut.instance.full_suite?
"Test Run: Full Suite\n\n"
else
"Test Run: Partial Suite\n\n"
end
SpecOut.spout(message)
end
end
end
@pboling
Copy link
Author

pboling commented Sep 3, 2023

Made this bauble, and then found https://github.com/avmnu-sng/rspec-tracer 😆 which is far better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment