Commit c535d2cc authored by Rémy Coutable's avatar Rémy Coutable Committed by Albert Salim

Suggest to use match_array instead of match for array expectation

parent 5e820b9d
# frozen_string_literal: true
require_relative '../../tooling/danger/specs'
module Danger
class Specs < ::Danger::Plugin
# Put the helper code somewhere it can be tested
include Tooling::Danger::Specs
end
end
......@@ -32,11 +32,12 @@ request specs (and/or feature specs). Please add request specs under
See https://gitlab.com/groups/gitlab-org/-/epics/5076 for information.
MSG
has_app_changes = helper.all_changed_files.grep(%r{\A(app|lib|db/(geo/)?(post_)?migrate)/}).any?
has_ee_app_changes = helper.all_changed_files.grep(%r{\Aee/(app|lib|db/(geo/)?(post_)?migrate)/}).any?
spec_changes = helper.all_changed_files.grep(%r{\Aspec/})
all_changed_files = helper.all_changed_files
has_app_changes = all_changed_files.grep(%r{\A(app|lib|db/(geo/)?(post_)?migrate)/}).any?
has_ee_app_changes = all_changed_files.grep(%r{\Aee/(app|lib|db/(geo/)?(post_)?migrate)/}).any?
spec_changes = specs.changed_specs_files(ee: :exclude)
has_spec_changes = spec_changes.any?
has_ee_spec_changes = helper.all_changed_files.grep(%r{\Aee/spec/}).any?
has_ee_spec_changes = specs.changed_specs_files(ee: :only).any?
new_specs_needed = (gitlab.mr_labels & NO_SPECS_LABELS).empty?
if (has_app_changes || has_ee_app_changes) && !(has_spec_changes || has_ee_spec_changes) && new_specs_needed
......@@ -49,6 +50,10 @@ if has_ee_app_changes && has_spec_changes && !(has_app_changes || has_ee_spec_ch
end
# Forbidding a new file addition under `/spec/controllers` or `/ee/spec/controllers`
if git.added_files.grep(%r{^(ee/)?spec/controllers/}).any?
if project_helper.changes.added.files.grep(%r{^(ee/)?spec/controllers/}).any?
warn CONTROLLER_SPEC_DEPRECATION_MESSAGE
end
specs.changed_specs_files.each do |filename|
specs.add_suggestions_for_match_with_array(filename)
end
......@@ -280,4 +280,17 @@ RSpec.describe Tooling::Danger::ProjectHelper do
is_expected.to eq('gitlab-foss')
end
end
describe '#file_lines' do
let(:filename) { 'spec/foo_spec.rb' }
let(:file_spy) { spy }
it 'returns the chomped file lines' do
expect(project_helper).to receive(:read_file).with(filename).and_return(file_spy)
project_helper.file_lines(filename)
expect(file_spy).to have_received(:lines).with(chomp: true)
end
end
end
# frozen_string_literal: true
require 'rspec-parameterized'
require 'gitlab-dangerfiles'
require 'danger'
require 'danger/plugins/helper'
require 'gitlab/dangerfiles/spec_helper'
require_relative '../../../tooling/danger/specs'
require_relative '../../../tooling/danger/project_helper'
RSpec.describe Tooling::Danger::Specs do
include_context "with dangerfile"
let(:fake_danger) { DangerSpecHelper.fake_danger.include(described_class) }
let(:fake_project_helper) { double('fake-project-helper', helper: fake_helper).tap { |h| h.class.include(Tooling::Danger::ProjectHelper) } }
let(:file_lines) do
[
" describe 'foo' do",
" expect(foo).to match(['bar'])",
" end",
" expect(foo).to match(['bar'])", # same line as line 1 above, we expect two different suggestions
" ",
" expect(foo).to match ['bar']",
" expect(foo).to eq(['bar'])",
" expect(foo).to eq ['bar']",
" expect(foo).to(match(['bar']))",
" expect(foo).to(eq(['bar']))",
" foo.eq(['bar'])"
]
end
let(:matching_lines) do
[
"+ expect(foo).to match(['bar'])",
"+ expect(foo).to match(['bar'])",
"+ expect(foo).to match ['bar']",
"+ expect(foo).to eq(['bar'])",
"+ expect(foo).to eq ['bar']",
"+ expect(foo).to(match(['bar']))",
"+ expect(foo).to(eq(['bar']))"
]
end
subject(:specs) { fake_danger.new(helper: fake_helper) }
before do
allow(specs).to receive(:project_helper).and_return(fake_project_helper)
end
describe '#add_suggestions_for_match_with_array' do
let(:filename) { 'spec/foo_spec.rb' }
before do
expect(specs).to receive(:added_line_matching_match_with_array).and_return(matching_lines)
allow(specs.project_helper).to receive(:file_lines).and_return(file_lines)
end
it 'adds suggestions at the correct lines' do
expect(specs).to receive(:markdown).with(format(described_class::SUGGEST_MR_COMMENT, suggested_line: " expect(foo).to match_array(['bar'])"), file: filename, line: 2)
expect(specs).to receive(:markdown).with(format(described_class::SUGGEST_MR_COMMENT, suggested_line: " expect(foo).to match_array(['bar'])"), file: filename, line: 4)
expect(specs).to receive(:markdown).with(format(described_class::SUGGEST_MR_COMMENT, suggested_line: " expect(foo).to match_array ['bar']"), file: filename, line: 6)
expect(specs).to receive(:markdown).with(format(described_class::SUGGEST_MR_COMMENT, suggested_line: " expect(foo).to match_array(['bar'])"), file: filename, line: 7)
expect(specs).to receive(:markdown).with(format(described_class::SUGGEST_MR_COMMENT, suggested_line: " expect(foo).to match_array ['bar']"), file: filename, line: 8)
expect(specs).to receive(:markdown).with(format(described_class::SUGGEST_MR_COMMENT, suggested_line: " expect(foo).to(match_array(['bar']))"), file: filename, line: 9)
expect(specs).to receive(:markdown).with(format(described_class::SUGGEST_MR_COMMENT, suggested_line: " expect(foo).to(match_array(['bar']))"), file: filename, line: 10)
specs.add_suggestions_for_match_with_array(filename)
end
end
describe '#changed_specs_files' do
let(:base_expected_files) { %w[spec/foo_spec.rb ee/spec/foo_spec.rb spec/bar_spec.rb ee/spec/bar_spec.rb spec/zab_spec.rb ee/spec/zab_spec.rb] }
before do
all_changed_files = %w[
app/workers/a.rb
app/workers/b.rb
app/workers/e.rb
spec/foo_spec.rb
ee/spec/foo_spec.rb
spec/bar_spec.rb
ee/spec/bar_spec.rb
spec/zab_spec.rb
ee/spec/zab_spec.rb
]
allow(specs.helper).to receive(:all_changed_files).and_return(all_changed_files)
end
it 'returns added, modified, and renamed_after files by default' do
expect(specs.changed_specs_files).to match_array(base_expected_files)
end
context 'with include_ee: :exclude' do
it 'returns spec files without EE-specific files' do
expect(specs.changed_specs_files(ee: :exclude)).not_to include(%w[ee/spec/foo_spec.rb ee/spec/bar_spec.rb ee/spec/zab_spec.rb])
end
end
context 'with include_ee: :only' do
it 'returns EE-specific spec files only' do
expect(specs.changed_specs_files(ee: :only)).to match_array(%w[ee/spec/foo_spec.rb ee/spec/bar_spec.rb ee/spec/zab_spec.rb])
end
end
end
describe '#added_line_matching_match_with_array' do
let(:filename) { 'spec/foo_spec.rb' }
let(:changed_lines) do
[
" expect(foo).to match(['bar'])",
" expect(foo).to match(['bar'])",
" expect(foo).to match ['bar']",
" expect(foo).to eq(['bar'])",
" expect(foo).to eq ['bar']",
"- expect(foo).to match(['bar'])",
"- expect(foo).to match(['bar'])",
"- expect(foo).to match ['bar']",
"- expect(foo).to eq(['bar'])",
"- expect(foo).to eq ['bar']"
] + matching_lines
end
before do
allow(specs.helper).to receive(:changed_lines).with(filename).and_return(changed_lines)
end
it 'returns added, modified, and renamed_after files by default' do
expect(specs.added_line_matching_match_with_array(filename)).to match_array(matching_lines)
end
end
end
......@@ -175,8 +175,16 @@ module Tooling
ee? ? 'gitlab' : 'gitlab-foss'
end
def file_lines(filename)
read_file(filename).lines(chomp: true)
end
private
def read_file(filename)
File.read(filename)
end
def ee?
# Support former project name for `dev` and support local Danger run
%w[gitlab gitlab-ee].include?(ENV['CI_PROJECT_NAME']) || Dir.exist?(File.expand_path('../../../ee', __dir__))
......
# frozen_string_literal: true
module Tooling
module Danger
module Specs
SPEC_FILES_REGEX = 'spec/'
EE_PREFIX = 'ee/'
MATCH_WITH_ARRAY_REGEX = /(?<to>to\(?\s*)(?<matcher>match|eq)(?<expectation>[( ]?\[)/.freeze
SUGGEST_MR_COMMENT = <<~SUGGEST_COMMENT
```suggestion
%<suggested_line>s
```
If order of the result is not important, please consider using `match_array` to avoid flakiness.
SUGGEST_COMMENT
def changed_specs_files(ee: :include)
changed_files = helper.all_changed_files
folder_prefix =
case ee
when :include
"(#{EE_PREFIX})?"
when :only
EE_PREFIX
when :exclude
nil
end
changed_files.grep(%r{\A#{folder_prefix}#{SPEC_FILES_REGEX}})
end
def add_suggestions_for_match_with_array(filename)
added_lines = added_line_matching_match_with_array(filename)
return if added_lines.empty?
spec_file_lines = project_helper.file_lines(filename)
added_lines.each_with_object([]) do |added_line, processed_line_numbers|
line_number = find_line_number(spec_file_lines, added_line.delete_prefix('+'), exclude_indexes: processed_line_numbers)
processed_line_numbers << line_number
markdown(format(SUGGEST_MR_COMMENT, suggested_line: spec_file_lines[line_number].gsub(MATCH_WITH_ARRAY_REGEX, '\k<to>match_array\k<expectation>')), file: filename, line: line_number.succ)
end
end
def added_line_matching_match_with_array(filename)
helper.changed_lines(filename).grep(/\A\+ /).grep(MATCH_WITH_ARRAY_REGEX)
end
private
def find_line_number(file_lines, searched_line, exclude_indexes: [])
file_lines.each_with_index do |file_line, index|
next if exclude_indexes.include?(index)
break index if file_line == searched_line
end
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment