Add new RSpecFlaky::FlakyExamplesCollection and RSpecFlaky::Config classes
Signed-off-by: Rémy Coutable <remy@rymai.me>
This commit is contained in:
parent
f286d09784
commit
da5aa64f06
|
@ -0,0 +1,21 @@
|
|||
require 'json'
|
||||
|
||||
module RspecFlaky
|
||||
class Config
|
||||
def self.generate_report?
|
||||
ENV['FLAKY_RSPEC_GENERATE_REPORT'] == 'true'
|
||||
end
|
||||
|
||||
def self.suite_flaky_examples_report_path
|
||||
ENV['SUITE_FLAKY_RSPEC_REPORT_PATH'] || Rails.root.join("rspec_flaky/suite-report.json")
|
||||
end
|
||||
|
||||
def self.flaky_examples_report_path
|
||||
ENV['FLAKY_RSPEC_REPORT_PATH'] || Rails.root.join("rspec_flaky/report.json")
|
||||
end
|
||||
|
||||
def self.new_flaky_examples_report_path
|
||||
ENV['NEW_FLAKY_RSPEC_REPORT_PATH'] || Rails.root.join("rspec_flaky/new-report.json")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,37 @@
|
|||
require 'json'
|
||||
|
||||
module RspecFlaky
|
||||
class FlakyExamplesCollection < SimpleDelegator
|
||||
def self.from_json(json)
|
||||
new(JSON.parse(json))
|
||||
end
|
||||
|
||||
def initialize(collection = {})
|
||||
unless collection.is_a?(Hash)
|
||||
raise ArgumentError, "`collection` must be a Hash, #{collection.class} given!"
|
||||
end
|
||||
|
||||
collection_of_flaky_examples =
|
||||
collection.map do |uid, example|
|
||||
[
|
||||
uid,
|
||||
example.is_a?(RspecFlaky::FlakyExample) ? example : RspecFlaky::FlakyExample.new(example)
|
||||
]
|
||||
end
|
||||
|
||||
super(Hash[collection_of_flaky_examples])
|
||||
end
|
||||
|
||||
def to_report
|
||||
Hash[map { |uid, example| [uid, example.to_h] }].deep_symbolize_keys
|
||||
end
|
||||
|
||||
def -(other)
|
||||
unless other.respond_to?(:key)
|
||||
raise ArgumentError, "`other` must respond to `#key?`, #{other.class} does not!"
|
||||
end
|
||||
|
||||
self.class.new(reject { |uid, _| other.key?(uid) })
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,7 +9,7 @@ module RspecFlaky
|
|||
attr_reader :suite_flaky_examples, :flaky_examples
|
||||
|
||||
def initialize(suite_flaky_examples_json = nil)
|
||||
@flaky_examples = {}
|
||||
@flaky_examples = FlakyExamplesCollection.new
|
||||
@suite_flaky_examples = init_suite_flaky_examples(suite_flaky_examples_json)
|
||||
end
|
||||
|
||||
|
@ -25,14 +25,14 @@ module RspecFlaky
|
|||
end
|
||||
|
||||
def dump_summary(_)
|
||||
write_report_file(flaky_examples, flaky_examples_report_path)
|
||||
write_report_file(flaky_examples, RspecFlaky::Config.flaky_examples_report_path)
|
||||
|
||||
new_flaky_examples = _new_flaky_examples
|
||||
new_flaky_examples = flaky_examples - suite_flaky_examples
|
||||
if new_flaky_examples.any?
|
||||
Rails.logger.warn "\nNew flaky examples detected:\n"
|
||||
Rails.logger.warn JSON.pretty_generate(to_report(new_flaky_examples))
|
||||
Rails.logger.warn JSON.pretty_generate(new_flaky_examples.to_report)
|
||||
|
||||
write_report_file(new_flaky_examples, new_flaky_examples_report_path)
|
||||
write_report_file(new_flaky_examples, RspecFlaky::Config.new_flaky_examples_report_path)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -44,45 +44,21 @@ module RspecFlaky
|
|||
|
||||
def init_suite_flaky_examples(suite_flaky_examples_json = nil)
|
||||
unless suite_flaky_examples_json
|
||||
return {} unless File.exist?(suite_flaky_examples_report_path)
|
||||
return {} unless File.exist?(RspecFlaky::Config.suite_flaky_examples_report_path)
|
||||
|
||||
suite_flaky_examples_json = File.read(suite_flaky_examples_report_path)
|
||||
suite_flaky_examples_json = File.read(RspecFlaky::Config.suite_flaky_examples_report_path)
|
||||
end
|
||||
|
||||
suite_flaky_examples = JSON.parse(suite_flaky_examples_json)
|
||||
|
||||
Hash[(suite_flaky_examples || {}).map { |k, ex| [k, FlakyExample.new(ex)] }].freeze
|
||||
FlakyExamplesCollection.from_json(suite_flaky_examples_json)
|
||||
end
|
||||
|
||||
def _new_flaky_examples
|
||||
flaky_examples.reject { |uid, _| already_flaky?(uid) }
|
||||
end
|
||||
|
||||
def already_flaky?(example_uid)
|
||||
suite_flaky_examples.key?(example_uid)
|
||||
end
|
||||
|
||||
def write_report_file(examples, file_path)
|
||||
return unless ENV['FLAKY_RSPEC_GENERATE_REPORT'] == 'true'
|
||||
def write_report_file(examples_collection, file_path)
|
||||
return unless RspecFlaky::Config.generate_report?
|
||||
|
||||
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 suite_flaky_examples_report_path
|
||||
@suite_flaky_examples_report_path ||= ENV['SUITE_FLAKY_RSPEC_REPORT_PATH'] ||
|
||||
Rails.root.join("rspec_flaky/suite-report.json")
|
||||
end
|
||||
|
||||
def flaky_examples_report_path
|
||||
@flaky_examples_report_path ||= ENV['FLAKY_RSPEC_REPORT_PATH'] ||
|
||||
Rails.root.join("rspec_flaky/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")
|
||||
File.write(file_path, JSON.pretty_generate(examples_collection.to_report))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe RspecFlaky::Config, :aggregate_failures do
|
||||
before do
|
||||
# Stub these env variables otherwise specs don't behave the same on the CI
|
||||
stub_env('FLAKY_RSPEC_GENERATE_REPORT', nil)
|
||||
stub_env('SUITE_FLAKY_RSPEC_REPORT_PATH', nil)
|
||||
stub_env('FLAKY_RSPEC_REPORT_PATH', nil)
|
||||
stub_env('NEW_FLAKY_RSPEC_REPORT_PATH', nil)
|
||||
end
|
||||
|
||||
describe '.generate_report?' do
|
||||
context "when ENV['FLAKY_RSPEC_GENERATE_REPORT'] is not set" do
|
||||
it 'returns false' do
|
||||
expect(described_class).not_to be_generate_report
|
||||
end
|
||||
end
|
||||
|
||||
context "when ENV['FLAKY_RSPEC_GENERATE_REPORT'] is set to 'false'" do
|
||||
before do
|
||||
stub_env('FLAKY_RSPEC_GENERATE_REPORT', 'false')
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(described_class).not_to be_generate_report
|
||||
end
|
||||
end
|
||||
|
||||
context "when ENV['FLAKY_RSPEC_GENERATE_REPORT'] is set to 'true'" do
|
||||
before do
|
||||
stub_env('FLAKY_RSPEC_GENERATE_REPORT', 'true')
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
expect(described_class).to be_generate_report
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.suite_flaky_examples_report_path' do
|
||||
context "when ENV['SUITE_FLAKY_RSPEC_REPORT_PATH'] is not set" do
|
||||
it 'returns the default path' do
|
||||
expect(Rails.root).to receive(:join).with('rspec_flaky/suite-report.json')
|
||||
.and_return('root/rspec_flaky/suite-report.json')
|
||||
|
||||
expect(described_class.suite_flaky_examples_report_path).to eq('root/rspec_flaky/suite-report.json')
|
||||
end
|
||||
end
|
||||
|
||||
context "when ENV['SUITE_FLAKY_RSPEC_REPORT_PATH'] is set" do
|
||||
before do
|
||||
stub_env('SUITE_FLAKY_RSPEC_REPORT_PATH', 'foo/suite-report.json')
|
||||
end
|
||||
|
||||
it 'returns the value of the env variable' do
|
||||
expect(described_class.suite_flaky_examples_report_path).to eq('foo/suite-report.json')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.flaky_examples_report_path' do
|
||||
context "when ENV['FLAKY_RSPEC_REPORT_PATH'] is not set" do
|
||||
it 'returns the default path' do
|
||||
expect(Rails.root).to receive(:join).with('rspec_flaky/report.json')
|
||||
.and_return('root/rspec_flaky/report.json')
|
||||
|
||||
expect(described_class.flaky_examples_report_path).to eq('root/rspec_flaky/report.json')
|
||||
end
|
||||
end
|
||||
|
||||
context "when ENV['FLAKY_RSPEC_REPORT_PATH'] is set" do
|
||||
before do
|
||||
stub_env('FLAKY_RSPEC_REPORT_PATH', 'foo/report.json')
|
||||
end
|
||||
|
||||
it 'returns the value of the env variable' do
|
||||
expect(described_class.flaky_examples_report_path).to eq('foo/report.json')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.new_flaky_examples_report_path' do
|
||||
context "when ENV['NEW_FLAKY_RSPEC_REPORT_PATH'] is not set" do
|
||||
it 'returns the default path' do
|
||||
expect(Rails.root).to receive(:join).with('rspec_flaky/new-report.json')
|
||||
.and_return('root/rspec_flaky/new-report.json')
|
||||
|
||||
expect(described_class.new_flaky_examples_report_path).to eq('root/rspec_flaky/new-report.json')
|
||||
end
|
||||
end
|
||||
|
||||
context "when ENV['NEW_FLAKY_RSPEC_REPORT_PATH'] is set" do
|
||||
before do
|
||||
stub_env('NEW_FLAKY_RSPEC_REPORT_PATH', 'foo/new-report.json')
|
||||
end
|
||||
|
||||
it 'returns the value of the env variable' do
|
||||
expect(described_class.new_flaky_examples_report_path).to eq('foo/new-report.json')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,79 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe RspecFlaky::FlakyExamplesCollection, :aggregate_failures do
|
||||
let(:collection_hash) do
|
||||
{
|
||||
a: { example_id: 'spec/foo/bar_spec.rb:2' },
|
||||
b: { example_id: 'spec/foo/baz_spec.rb:3' }
|
||||
}
|
||||
end
|
||||
let(:collection_report) do
|
||||
{
|
||||
a: {
|
||||
example_id: 'spec/foo/bar_spec.rb:2',
|
||||
first_flaky_at: nil,
|
||||
last_flaky_at: nil,
|
||||
last_flaky_job: nil
|
||||
},
|
||||
b: {
|
||||
example_id: 'spec/foo/baz_spec.rb:3',
|
||||
first_flaky_at: nil,
|
||||
last_flaky_at: nil,
|
||||
last_flaky_job: nil
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
describe '.from_json' do
|
||||
it 'accepts a JSON' do
|
||||
collection = described_class.from_json(JSON.pretty_generate(collection_hash))
|
||||
|
||||
expect(collection.to_report).to eq(described_class.new(collection_hash).to_report)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#initialize' do
|
||||
it 'accepts no argument' do
|
||||
expect { described_class.new }.not_to raise_error
|
||||
end
|
||||
|
||||
it 'accepts a hash' do
|
||||
expect { described_class.new(collection_hash) }.not_to raise_error
|
||||
end
|
||||
|
||||
it 'does not accept anything else' do
|
||||
expect { described_class.new([1, 2, 3]) }.to raise_error(ArgumentError, "`collection` must be a Hash, Array given!")
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_report' do
|
||||
it 'calls #to_h on the values' do
|
||||
collection = described_class.new(collection_hash)
|
||||
|
||||
expect(collection.to_report).to eq(collection_report)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#-' do
|
||||
it 'returns only examples that are not present in the given collection' do
|
||||
collection1 = described_class.new(collection_hash)
|
||||
collection2 = described_class.new(
|
||||
a: { example_id: 'spec/foo/bar_spec.rb:2' },
|
||||
c: { example_id: 'spec/bar/baz_spec.rb:4' })
|
||||
|
||||
expect((collection2 - collection1).to_report).to eq(
|
||||
c: {
|
||||
example_id: 'spec/bar/baz_spec.rb:4',
|
||||
first_flaky_at: nil,
|
||||
last_flaky_at: nil,
|
||||
last_flaky_job: nil
|
||||
})
|
||||
end
|
||||
|
||||
it 'fails if the given collection does not respond to `#key?`' do
|
||||
collection = described_class.new(collection_hash)
|
||||
|
||||
expect { collection - [1, 2, 3] }.to raise_error(ArgumentError, "`other` must respond to `#key?`, Array does not!")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -56,7 +56,6 @@ describe RspecFlaky::Listener, :aggregate_failures do
|
|||
|
||||
expect(listener.to_report(listener.suite_flaky_examples))
|
||||
.to eq(expected_suite_flaky_examples)
|
||||
expect(listener.__send__(:_new_flaky_examples)).to eq({})
|
||||
expect(listener.flaky_examples).to eq({})
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue