Use a new RspecFlakyListener to detect flaky specs
Signed-off-by: Rémy Coutable <remy@rymai.me>
This commit is contained in:
parent
c62ae6cfd7
commit
8f8fd34231
8 changed files with 537 additions and 12 deletions
46
lib/rspec_flaky/example.rb
Normal file
46
lib/rspec_flaky/example.rb
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
module RspecFlaky
|
||||||
|
# This is a wrapper class for RSpec::Core::Example
|
||||||
|
class Example
|
||||||
|
delegate :status, :exception, to: :execution_result
|
||||||
|
|
||||||
|
def initialize(rspec_example)
|
||||||
|
@rspec_example = rspec_example.try(:example) || rspec_example
|
||||||
|
end
|
||||||
|
|
||||||
|
def uid
|
||||||
|
@uid ||= Digest::MD5.hexdigest("#{description}-#{file}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def example_id
|
||||||
|
rspec_example.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def file
|
||||||
|
metadata[:file_path]
|
||||||
|
end
|
||||||
|
|
||||||
|
def line
|
||||||
|
metadata[:line_number]
|
||||||
|
end
|
||||||
|
|
||||||
|
def description
|
||||||
|
metadata[:full_description]
|
||||||
|
end
|
||||||
|
|
||||||
|
def attempts
|
||||||
|
rspec_example.try(:attempts) || 1
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :rspec_example
|
||||||
|
|
||||||
|
def metadata
|
||||||
|
rspec_example.metadata
|
||||||
|
end
|
||||||
|
|
||||||
|
def execution_result
|
||||||
|
rspec_example.execution_result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
39
lib/rspec_flaky/flaky_example.rb
Normal file
39
lib/rspec_flaky/flaky_example.rb
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
module RspecFlaky
|
||||||
|
# This represents a flaky RSpec example and is mainly meant to be saved in a JSON file
|
||||||
|
class FlakyExample < OpenStruct
|
||||||
|
def initialize(example)
|
||||||
|
if example.respond_to?(:example_id)
|
||||||
|
super(
|
||||||
|
example_id: example.example_id,
|
||||||
|
file: example.file,
|
||||||
|
line: example.line,
|
||||||
|
description: example.description,
|
||||||
|
last_attempts_count: example.attempts,
|
||||||
|
flaky_reports: 1)
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def first_flaky_at
|
||||||
|
self[:first_flaky_at] || Time.now
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_flaky_at
|
||||||
|
Time.now
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_flaky_job
|
||||||
|
return unless ENV['CI_PROJECT_URL'] && ENV['CI_JOB_ID']
|
||||||
|
|
||||||
|
"#{ENV['CI_PROJECT_URL']}/-/jobs/#{ENV['CI_JOB_ID']}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_h
|
||||||
|
super.merge(
|
||||||
|
first_flaky_at: first_flaky_at,
|
||||||
|
last_flaky_at: last_flaky_at,
|
||||||
|
last_flaky_job: last_flaky_job)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
75
lib/rspec_flaky/listener.rb
Normal file
75
lib/rspec_flaky/listener.rb
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
module RspecFlaky
|
||||||
|
class Listener
|
||||||
|
attr_reader :all_flaky_examples, :new_flaky_examples
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@new_flaky_examples = {}
|
||||||
|
@all_flaky_examples = init_all_flaky_examples
|
||||||
|
end
|
||||||
|
|
||||||
|
def example_passed(notification)
|
||||||
|
current_example = RspecFlaky::Example.new(notification.example)
|
||||||
|
|
||||||
|
return unless current_example.attempts > 1
|
||||||
|
|
||||||
|
flaky_example_hash = all_flaky_examples[current_example.uid]
|
||||||
|
|
||||||
|
all_flaky_examples[current_example.uid] =
|
||||||
|
if flaky_example_hash
|
||||||
|
FlakyExample.new(flaky_example_hash).tap do |ex|
|
||||||
|
ex.last_attempts_count = current_example.attempts
|
||||||
|
ex.flaky_reports += 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
FlakyExample.new(current_example).tap do |ex|
|
||||||
|
new_flaky_examples[current_example.uid] = ex
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def dump_summary(_)
|
||||||
|
write_report_file(all_flaky_examples, all_flaky_examples_report_path)
|
||||||
|
|
||||||
|
if new_flaky_examples.any?
|
||||||
|
Rails.logger.warn "\nNew flaky examples detected:\n"
|
||||||
|
Rails.logger.warn JSON.pretty_generate(to_report(new_flaky_examples))
|
||||||
|
|
||||||
|
write_report_file(new_flaky_examples, new_flaky_examples_report_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_report(examples)
|
||||||
|
Hash[examples.map { |k, ex| [k, ex.to_h] }]
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def init_all_flaky_examples
|
||||||
|
return {} unless File.exist?(all_flaky_examples_report_path)
|
||||||
|
|
||||||
|
all_flaky_examples = JSON.parse(File.read(all_flaky_examples_report_path))
|
||||||
|
|
||||||
|
Hash[(all_flaky_examples || {}).map { |k, ex| [k, FlakyExample.new(ex)] }]
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_report_file(examples, file_path)
|
||||||
|
return unless ENV['FLAKY_RSPEC_GENERATE_REPORT'] == 'true'
|
||||||
|
|
||||||
|
report_path_dir = File.dirname(file_path)
|
||||||
|
FileUtils.mkdir_p(report_path_dir) unless Dir.exist?(report_path_dir)
|
||||||
|
File.write(file_path, JSON.pretty_generate(to_report(examples)))
|
||||||
|
end
|
||||||
|
|
||||||
|
def all_flaky_examples_report_path
|
||||||
|
@all_flaky_examples_report_path ||= ENV['ALL_FLAKY_RSPEC_REPORT_PATH'] ||
|
||||||
|
Rails.root.join("rspec_flaky/all-report.json")
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_flaky_examples_report_path
|
||||||
|
@new_flaky_examples_report_path ||= ENV['NEW_FLAKY_RSPEC_REPORT_PATH'] ||
|
||||||
|
Rails.root.join("rspec_flaky/new-report.json")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -106,12 +106,6 @@ describe Gitlab::HealthChecks::FsShardsCheck do
|
||||||
}.with_indifferent_access
|
}.with_indifferent_access
|
||||||
end
|
end
|
||||||
|
|
||||||
# Unsolved intermittent failure in CI https://gitlab.com/gitlab-org/gitlab-ce/issues/31128
|
|
||||||
around do |example| # rubocop:disable RSpec/AroundBlock
|
|
||||||
times_to_try = ENV['CI'] ? 4 : 1
|
|
||||||
example.run_with_retry retry: times_to_try
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'provides metrics' do
|
it 'provides metrics' do
|
||||||
metrics = described_class.metrics
|
metrics = described_class.metrics
|
||||||
|
|
||||||
|
|
89
spec/lib/rspec_flaky/example_spec.rb
Normal file
89
spec/lib/rspec_flaky/example_spec.rb
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe RspecFlaky::Example do
|
||||||
|
let(:example_attrs) do
|
||||||
|
{
|
||||||
|
id: 'spec/foo/bar_spec.rb:2',
|
||||||
|
metadata: {
|
||||||
|
file_path: 'spec/foo/bar_spec.rb',
|
||||||
|
line_number: 2,
|
||||||
|
full_description: 'hello world'
|
||||||
|
},
|
||||||
|
execution_result: double(status: 'passed', exception: 'BOOM!'),
|
||||||
|
attempts: 1
|
||||||
|
}
|
||||||
|
end
|
||||||
|
let(:rspec_example) { double(example_attrs) }
|
||||||
|
|
||||||
|
describe '#initialize' do
|
||||||
|
shared_examples 'a valid Example instance' do
|
||||||
|
it 'returns valid attributes' do
|
||||||
|
example = described_class.new(args)
|
||||||
|
|
||||||
|
expect(example.example_id).to eq(example_attrs[:id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when given an Rspec::Core::Example that responds to #example' do
|
||||||
|
let(:args) { double(example: rspec_example) }
|
||||||
|
|
||||||
|
it_behaves_like 'a valid Example instance'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when given an Rspec::Core::Example that does not respond to #example' do
|
||||||
|
let(:args) { rspec_example }
|
||||||
|
|
||||||
|
it_behaves_like 'a valid Example instance'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { described_class.new(rspec_example) }
|
||||||
|
|
||||||
|
describe '#uid' do
|
||||||
|
it 'returns a hash of the full description' do
|
||||||
|
expect(subject.uid).to eq(Digest::MD5.hexdigest("#{subject.description}-#{subject.file}"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#example_id' do
|
||||||
|
it 'returns the ID of the RSpec::Core::Example' do
|
||||||
|
expect(subject.example_id).to eq(rspec_example.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#attempts' do
|
||||||
|
it 'returns the attempts of the RSpec::Core::Example' do
|
||||||
|
expect(subject.attempts).to eq(rspec_example.attempts)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#file' do
|
||||||
|
it 'returns the metadata[:file_path] of the RSpec::Core::Example' do
|
||||||
|
expect(subject.file).to eq(rspec_example.metadata[:file_path])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#line' do
|
||||||
|
it 'returns the metadata[:line_number] of the RSpec::Core::Example' do
|
||||||
|
expect(subject.line).to eq(rspec_example.metadata[:line_number])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#description' do
|
||||||
|
it 'returns the metadata[:full_description] of the RSpec::Core::Example' do
|
||||||
|
expect(subject.description).to eq(rspec_example.metadata[:full_description])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#status' do
|
||||||
|
it 'returns the execution_result.status of the RSpec::Core::Example' do
|
||||||
|
expect(subject.status).to eq(rspec_example.execution_result.status)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#exception' do
|
||||||
|
it 'returns the execution_result.exception of the RSpec::Core::Example' do
|
||||||
|
expect(subject.exception).to eq(rspec_example.execution_result.exception)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
104
spec/lib/rspec_flaky/flaky_example_spec.rb
Normal file
104
spec/lib/rspec_flaky/flaky_example_spec.rb
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe RspecFlaky::FlakyExample do
|
||||||
|
let(:flaky_example_attrs) do
|
||||||
|
{
|
||||||
|
example_id: 'spec/foo/bar_spec.rb:2',
|
||||||
|
file: 'spec/foo/bar_spec.rb',
|
||||||
|
line: 2,
|
||||||
|
description: 'hello world',
|
||||||
|
first_flaky_at: 1234,
|
||||||
|
last_flaky_at: 2345,
|
||||||
|
last_attempts_count: 2,
|
||||||
|
flaky_reports: 1
|
||||||
|
}
|
||||||
|
end
|
||||||
|
let(:example_attrs) do
|
||||||
|
{
|
||||||
|
uid: 'abc123',
|
||||||
|
example_id: flaky_example_attrs[:example_id],
|
||||||
|
file: flaky_example_attrs[:file],
|
||||||
|
line: flaky_example_attrs[:line],
|
||||||
|
description: flaky_example_attrs[:description],
|
||||||
|
status: 'passed',
|
||||||
|
exception: 'BOOM!',
|
||||||
|
attempts: flaky_example_attrs[:last_attempts_count]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
let(:example) { double(example_attrs) }
|
||||||
|
|
||||||
|
describe '#initialize' do
|
||||||
|
shared_examples 'a valid FlakyExample instance' do
|
||||||
|
it 'returns valid attributes' do
|
||||||
|
flaky_example = described_class.new(args)
|
||||||
|
|
||||||
|
expect(flaky_example.uid).to eq(flaky_example_attrs[:uid])
|
||||||
|
expect(flaky_example.example_id).to eq(flaky_example_attrs[:example_id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when given an Rspec::Example' do
|
||||||
|
let(:args) { example }
|
||||||
|
|
||||||
|
it_behaves_like 'a valid FlakyExample instance'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when given a hash' do
|
||||||
|
let(:args) { flaky_example_attrs }
|
||||||
|
|
||||||
|
it_behaves_like 'a valid FlakyExample instance'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_h' do
|
||||||
|
before do
|
||||||
|
# Stub these env variables otherwise specs don't behave the same on the CI
|
||||||
|
stub_env('CI_PROJECT_URL', nil)
|
||||||
|
stub_env('CI_JOB_ID', nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'a valid FlakyExample hash' do
|
||||||
|
let(:additional_attrs) { {} }
|
||||||
|
|
||||||
|
it 'returns a valid hash' do
|
||||||
|
flaky_example = described_class.new(args)
|
||||||
|
final_hash = flaky_example_attrs
|
||||||
|
.merge(last_flaky_at: instance_of(Time), last_flaky_job: nil)
|
||||||
|
.merge(additional_attrs)
|
||||||
|
|
||||||
|
expect(flaky_example.to_h).to match(hash_including(final_hash))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when given an Rspec::Example' do
|
||||||
|
let(:args) { example }
|
||||||
|
|
||||||
|
context 'when run locally' do
|
||||||
|
it_behaves_like 'a valid FlakyExample hash' do
|
||||||
|
let(:additional_attrs) do
|
||||||
|
{ first_flaky_at: instance_of(Time) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when run on the CI' do
|
||||||
|
before do
|
||||||
|
stub_env('CI_PROJECT_URL', 'https://gitlab.com/gitlab-org/gitlab-ce')
|
||||||
|
stub_env('CI_JOB_ID', 42)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'a valid FlakyExample hash' do
|
||||||
|
let(:additional_attrs) do
|
||||||
|
{ first_flaky_at: instance_of(Time), last_flaky_job: "https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/42" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when given a hash' do
|
||||||
|
let(:args) { flaky_example_attrs }
|
||||||
|
|
||||||
|
it_behaves_like 'a valid FlakyExample hash'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
178
spec/lib/rspec_flaky/listener_spec.rb
Normal file
178
spec/lib/rspec_flaky/listener_spec.rb
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe RspecFlaky::Listener do
|
||||||
|
let(:flaky_example_report) do
|
||||||
|
{
|
||||||
|
'abc123' => {
|
||||||
|
example_id: 'spec/foo/bar_spec.rb:2',
|
||||||
|
file: 'spec/foo/bar_spec.rb',
|
||||||
|
line: 2,
|
||||||
|
description: 'hello world',
|
||||||
|
first_flaky_at: 1234,
|
||||||
|
last_flaky_at: instance_of(Time),
|
||||||
|
last_attempts_count: 2,
|
||||||
|
flaky_reports: 1,
|
||||||
|
last_flaky_job: nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
let(:example_attrs) do
|
||||||
|
{
|
||||||
|
id: 'spec/foo/baz_spec.rb:3',
|
||||||
|
metadata: {
|
||||||
|
file_path: 'spec/foo/baz_spec.rb',
|
||||||
|
line_number: 3,
|
||||||
|
full_description: 'hello GitLab'
|
||||||
|
},
|
||||||
|
execution_result: double(status: 'passed', exception: nil)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
# Stub these env variables otherwise specs don't behave the same on the CI
|
||||||
|
stub_env('CI_PROJECT_URL', nil)
|
||||||
|
stub_env('CI_JOB_ID', nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#initialize' do
|
||||||
|
shared_examples 'a valid Listener instance' do
|
||||||
|
let(:expected_all_flaky_examples) { {} }
|
||||||
|
|
||||||
|
it 'returns a valid Listener instance' do
|
||||||
|
listener = described_class.new
|
||||||
|
|
||||||
|
expect(listener.to_report(listener.all_flaky_examples))
|
||||||
|
.to match(hash_including(expected_all_flaky_examples))
|
||||||
|
expect(listener.new_flaky_examples).to eq({})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when no report file exists' do
|
||||||
|
it_behaves_like 'a valid Listener instance'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a report file exists and set by ALL_FLAKY_RSPEC_REPORT_PATH' do
|
||||||
|
let(:report_file) do
|
||||||
|
Tempfile.new(%w[rspec_flaky_report .json]).tap do |f|
|
||||||
|
f.write(JSON.pretty_generate(flaky_example_report))
|
||||||
|
f.rewind
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_env('ALL_FLAKY_RSPEC_REPORT_PATH', report_file.path)
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
report_file.close
|
||||||
|
report_file.unlink
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'a valid Listener instance' do
|
||||||
|
let(:expected_all_flaky_examples) { flaky_example_report }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#example_passed' do
|
||||||
|
let(:rspec_example) { double(example_attrs) }
|
||||||
|
let(:notification) { double(example: rspec_example) }
|
||||||
|
|
||||||
|
shared_examples 'a non-flaky example' do
|
||||||
|
it 'does not change the flaky examples hash' do
|
||||||
|
expect { subject.example_passed(notification) }
|
||||||
|
.not_to change { subject.all_flaky_examples }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when the RSpec example does not respond to attempts' do
|
||||||
|
it_behaves_like 'a non-flaky example'
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when the RSpec example has 1 attempt' do
|
||||||
|
let(:rspec_example) { double(example_attrs.merge(attempts: 1)) }
|
||||||
|
|
||||||
|
it_behaves_like 'a non-flaky example'
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when the RSpec example has 2 attempts' do
|
||||||
|
let(:rspec_example) { double(example_attrs.merge(attempts: 2)) }
|
||||||
|
let(:expected_new_flaky_example) do
|
||||||
|
{
|
||||||
|
example_id: 'spec/foo/baz_spec.rb:3',
|
||||||
|
file: 'spec/foo/baz_spec.rb',
|
||||||
|
line: 3,
|
||||||
|
description: 'hello GitLab',
|
||||||
|
first_flaky_at: instance_of(Time),
|
||||||
|
last_flaky_at: instance_of(Time),
|
||||||
|
last_attempts_count: 2,
|
||||||
|
flaky_reports: 1,
|
||||||
|
last_flaky_job: nil
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not change the flaky examples hash' do
|
||||||
|
expect { subject.example_passed(notification) }
|
||||||
|
.to change { subject.all_flaky_examples }
|
||||||
|
|
||||||
|
new_example = RspecFlaky::Example.new(rspec_example)
|
||||||
|
|
||||||
|
expect(subject.all_flaky_examples[new_example.uid].to_h)
|
||||||
|
.to match(hash_including(expected_new_flaky_example))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#dump_summary' do
|
||||||
|
let(:rspec_example) { double(example_attrs) }
|
||||||
|
let(:notification) { double(example: rspec_example) }
|
||||||
|
|
||||||
|
context 'when a report file path is set by ALL_FLAKY_RSPEC_REPORT_PATH' do
|
||||||
|
let(:report_file_path) { Rails.root.join('tmp', 'rspec_flaky_report.json') }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_env('ALL_FLAKY_RSPEC_REPORT_PATH', report_file_path)
|
||||||
|
FileUtils.rm(report_file_path) if File.exist?(report_file_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
FileUtils.rm(report_file_path) if File.exist?(report_file_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when FLAKY_RSPEC_GENERATE_REPORT == "false"' do
|
||||||
|
before do
|
||||||
|
stub_env('FLAKY_RSPEC_GENERATE_REPORT', 'false')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not write the report file' do
|
||||||
|
subject.example_passed(notification)
|
||||||
|
|
||||||
|
subject.dump_summary(nil)
|
||||||
|
|
||||||
|
expect(File.exist?(report_file_path)).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when FLAKY_RSPEC_GENERATE_REPORT == "true"' do
|
||||||
|
before do
|
||||||
|
stub_env('FLAKY_RSPEC_GENERATE_REPORT', 'true')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'writes the report file' do
|
||||||
|
subject.example_passed(notification)
|
||||||
|
|
||||||
|
subject.dump_summary(nil)
|
||||||
|
|
||||||
|
expect(File.exist?(report_file_path)).to be(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_report' do
|
||||||
|
it 'transforms the internal hash to a JSON-ready hash' do
|
||||||
|
expect(subject.to_report('abc123' => RspecFlaky::FlakyExample.new(flaky_example_report['abc123'])))
|
||||||
|
.to match(hash_including(flaky_example_report))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -69,6 +69,12 @@ RSpec.configure do |config|
|
||||||
|
|
||||||
config.raise_errors_for_deprecations!
|
config.raise_errors_for_deprecations!
|
||||||
|
|
||||||
|
if ENV['CI']
|
||||||
|
# This includes the first try, i.e. tests will be run 4 times before failing.
|
||||||
|
config.default_retry_count = 4
|
||||||
|
config.reporter.register_listener(RspecFlaky::Listener.new, :example_passed, :dump_summary)
|
||||||
|
end
|
||||||
|
|
||||||
config.before(:suite) do
|
config.before(:suite) do
|
||||||
TestEnv.init
|
TestEnv.init
|
||||||
end
|
end
|
||||||
|
@ -97,12 +103,6 @@ RSpec.configure do |config|
|
||||||
reset_delivered_emails!
|
reset_delivered_emails!
|
||||||
end
|
end
|
||||||
|
|
||||||
if ENV['CI']
|
|
||||||
config.around(:each) do |ex|
|
|
||||||
ex.run_with_retry retry: 2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
config.around(:each, :use_clean_rails_memory_store_caching) do |example|
|
config.around(:each, :use_clean_rails_memory_store_caching) do |example|
|
||||||
caching_store = Rails.cache
|
caching_store = Rails.cache
|
||||||
Rails.cache = ActiveSupport::Cache::MemoryStore.new
|
Rails.cache = ActiveSupport::Cache::MemoryStore.new
|
||||||
|
|
Loading…
Reference in a new issue