Commit 24b51f40 authored by Jacques Erasmus's avatar Jacques Erasmus

Merge branch 'ph/32285/fixApprovalsSummary' into 'master'

Fixes approval rules summary text

See merge request gitlab-org/gitlab!70928
parents 1c731e90 26d8a611
...@@ -35,13 +35,17 @@ export default { ...@@ -35,13 +35,17 @@ export default {
} }
if (!this.rulesLeft.length) { if (!this.rulesLeft.length) {
return n__('Requires approval.', 'Requires %d more approvals.', this.approvalsLeft); return n__(
'Requires %d approval from eligible users.',
'Requires %d approvals from eligible users.',
this.approvalsLeft,
);
} }
return sprintf( return sprintf(
n__( n__(
'Requires approval from %{names}.', 'Requires %{count} approval from %{names}.',
'Requires %{count} more approvals from %{names}.', 'Requires %{count} approvals from %{names}.',
this.approvalsLeft, this.approvalsLeft,
), ),
{ {
......
...@@ -4,6 +4,7 @@ import { ...@@ -4,6 +4,7 @@ import {
RULE_TYPE_FALLBACK, RULE_TYPE_FALLBACK,
RULE_TYPE_CODE_OWNER, RULE_TYPE_CODE_OWNER,
RULE_TYPE_REPORT_APPROVER, RULE_TYPE_REPORT_APPROVER,
RULE_TYPE_ANY_APPROVER,
} from 'ee/approvals/constants'; } from 'ee/approvals/constants';
import { __ } from '~/locale'; import { __ } from '~/locale';
...@@ -33,6 +34,8 @@ function getApprovalRuleNamesLeft(data) { ...@@ -33,6 +34,8 @@ function getApprovalRuleNamesLeft(data) {
const rulesLeft = groupBy(data.approval_rules_left, (x) => x.rule_type); const rulesLeft = groupBy(data.approval_rules_left, (x) => x.rule_type);
const anyApprover = rulesLeft[RULE_TYPE_ANY_APPROVER] ? [__('eligible users')] : [];
// Filter out empty names (fallback rule has no name) because the empties would look weird. // Filter out empty names (fallback rule has no name) because the empties would look weird.
const regularRules = (rulesLeft[RULE_TYPE_REGULAR] || []).map((x) => x.name).filter((x) => x); const regularRules = (rulesLeft[RULE_TYPE_REGULAR] || []).map((x) => x.name).filter((x) => x);
...@@ -43,7 +46,7 @@ function getApprovalRuleNamesLeft(data) { ...@@ -43,7 +46,7 @@ function getApprovalRuleNamesLeft(data) {
// As the names of code owner rules are patterns that don't mean much out of context. // As the names of code owner rules are patterns that don't mean much out of context.
const codeOwnerRules = rulesLeft[RULE_TYPE_CODE_OWNER] ? [__('Code Owners')] : []; const codeOwnerRules = rulesLeft[RULE_TYPE_CODE_OWNER] ? [__('Code Owners')] : [];
return [...regularRules, ...reportApprovalRules, ...codeOwnerRules]; return [...anyApprover, ...regularRules, ...reportApprovalRules, ...codeOwnerRules];
} }
/** /**
......
...@@ -77,12 +77,42 @@ class ApprovalState ...@@ -77,12 +77,42 @@ class ApprovalState
# considered approved. # considered approved.
def approvals_left def approvals_left
strong_memoize(:approvals_left) do strong_memoize(:approvals_left) do
wrapped_approval_rules.sum(&:approvals_left) [regular_rules_left + code_owner_rules_left + report_approver_rules_left, any_approver_rules_left].max
end
end
def code_owner_rules_left
strong_memoize(:code_owner_rules_left) do
code_owner_rules.sum(&:approvals_left)
end
end
def report_approver_rules_left
strong_memoize(:report_approver_rules_left) do
report_approver_rules.sum(&:approvals_left)
end
end
def regular_rules_left
strong_memoize(:regular_rules_left) do
regular_approval_rules.sum(&:approvals_left)
end
end
def any_approver_rules_left
strong_memoize(:any_approver_rules_left) do
any_approver_approval_rules.sum(&:approvals_left)
end end
end end
def approval_rules_left def approval_rules_left
wrapped_approval_rules.reject(&:approved?) rules = if any_approver_rules_left <= regular_rules_left + code_owner_rules_left + report_approver_rules_left
wrapped_approval_rules.reject(&:any_approver?)
else
wrapped_approval_rules
end
rules.reject(&:approved?)
end end
def approvers def approvers
...@@ -203,6 +233,18 @@ class ApprovalState ...@@ -203,6 +233,18 @@ class ApprovalState
end end
end end
def regular_approval_rules
strong_memoize(:regular_approval_rules) do
wrapped_approval_rules.select(&:regular?)
end
end
def any_approver_approval_rules
strong_memoize(:any_approver_approval_rules) do
wrapped_approval_rules.select(&:any_approver?)
end
end
def wrapped_rules def wrapped_rules
strong_memoize(:wrapped_rules) do strong_memoize(:wrapped_rules) do
merge_request_rules = merge_request.approval_rules.applicable_to_branch(target_branch) merge_request_rules = merge_request.approval_rules.applicable_to_branch(target_branch)
......
...@@ -42,7 +42,7 @@ RSpec.describe 'Merge request > User sees approval widget', :js do ...@@ -42,7 +42,7 @@ RSpec.describe 'Merge request > User sees approval widget', :js do
it 'the renders the number of required approvals' do it 'the renders the number of required approvals' do
wait_for_requests wait_for_requests
expect(page).to have_content('Requires 3 more approvals.') expect(page).to have_content('Requires 3 approvals from eligible users.')
end end
end end
...@@ -66,7 +66,7 @@ RSpec.describe 'Merge request > User sees approval widget', :js do ...@@ -66,7 +66,7 @@ RSpec.describe 'Merge request > User sees approval widget', :js do
visit project_merge_request_path(project, merge_request) visit project_merge_request_path(project, merge_request)
wait_for_requests wait_for_requests
expect(page).to have_content("Requires approval from #{rule.name}") expect(page).to have_content("Requires 1 approval from #{rule.name}")
click_on 'View eligible approvers' click_on 'View eligible approvers'
wait_for_requests wait_for_requests
...@@ -94,7 +94,7 @@ RSpec.describe 'Merge request > User sees approval widget', :js do ...@@ -94,7 +94,7 @@ RSpec.describe 'Merge request > User sees approval widget', :js do
visit project_merge_request_path(project, merge_request) visit project_merge_request_path(project, merge_request)
wait_for_requests wait_for_requests
expect(page).to have_content("Requires approval from #{rule.name}.") expect(page).to have_content("Requires 1 approval from #{rule.name}.")
click_on 'View eligible approvers' click_on 'View eligible approvers'
wait_for_requests wait_for_requests
...@@ -118,7 +118,7 @@ RSpec.describe 'Merge request > User sees approval widget', :js do ...@@ -118,7 +118,7 @@ RSpec.describe 'Merge request > User sees approval widget', :js do
visit project_merge_request_path(project, merge_request) visit project_merge_request_path(project, merge_request)
wait_for_requests wait_for_requests
expect(page).to have_content("Requires 2 more approvals from #{rule.name} and Code Owners") expect(page).to have_content("Requires 2 approvals from #{rule.name} and Code Owners")
click_on 'View eligible approvers' click_on 'View eligible approvers'
wait_for_requests wait_for_requests
......
...@@ -92,7 +92,7 @@ RSpec.describe 'Merge request > User sets approvers', :js do ...@@ -92,7 +92,7 @@ RSpec.describe 'Merge request > User sets approvers', :js do
click_on("Create merge request") click_on("Create merge request")
wait_for_all_requests wait_for_all_requests
expect(page).to have_content("Requires approval.") expect(page).to have_content("Requires 1 approval from eligible users.")
expect(page).to have_selector("img[alt='#{other_user.name}']") expect(page).to have_selector("img[alt='#{other_user.name}']")
end end
...@@ -165,7 +165,7 @@ RSpec.describe 'Merge request > User sets approvers', :js do ...@@ -165,7 +165,7 @@ RSpec.describe 'Merge request > User sets approvers', :js do
click_on("Save changes") click_on("Save changes")
wait_for_all_requests wait_for_all_requests
expect(page).to have_content("Requires approval.") expect(page).to have_content("Requires 1 approval from eligible users.")
expect(page).to have_selector("img[alt='#{other_user.name}']") expect(page).to have_selector("img[alt='#{other_user.name}']")
end end
end end
...@@ -193,7 +193,7 @@ RSpec.describe 'Merge request > User sets approvers', :js do ...@@ -193,7 +193,7 @@ RSpec.describe 'Merge request > User sets approvers', :js do
click_on("Save changes") click_on("Save changes")
wait_for_all_requests wait_for_all_requests
expect(page).to have_content("Requires approval.") expect(page).to have_content("Requires 1 approval from eligible users.")
expect(page).to have_selector("img[alt='#{user.name}']") expect(page).to have_selector("img[alt='#{user.name}']")
expect(page).to have_selector("img[alt='#{other_user.name}']") expect(page).to have_selector("img[alt='#{other_user.name}']")
end end
...@@ -225,7 +225,7 @@ RSpec.describe 'Merge request > User sets approvers', :js do ...@@ -225,7 +225,7 @@ RSpec.describe 'Merge request > User sets approvers', :js do
expect(page).not_to have_selector(".js-approvers img[alt='#{other_user.name}']") expect(page).not_to have_selector(".js-approvers img[alt='#{other_user.name}']")
expect(page).to have_selector(".js-approvers img[alt='#{approver.name}']") expect(page).to have_selector(".js-approvers img[alt='#{approver.name}']")
expect(page).to have_content("Requires approval.") expect(page).to have_content("Requires 1 approval from eligible users.")
end end
it 'allows changing approvals number' do it 'allows changing approvals number' do
...@@ -237,7 +237,7 @@ RSpec.describe 'Merge request > User sets approvers', :js do ...@@ -237,7 +237,7 @@ RSpec.describe 'Merge request > User sets approvers', :js do
wait_for_requests wait_for_requests
# project setting in the beginning on the show MR page # project setting in the beginning on the show MR page
expect(page).to have_content("Requires 2 more approvals") expect(page).to have_content("Requires 2 approvals from eligible users")
find('.merge-request').click_on 'Edit' find('.merge-request').click_on 'Edit'
open_modal open_modal
...@@ -253,7 +253,7 @@ RSpec.describe 'Merge request > User sets approvers', :js do ...@@ -253,7 +253,7 @@ RSpec.describe 'Merge request > User sets approvers', :js do
wait_for_all_requests wait_for_all_requests
# new MR setting on the show MR page # new MR setting on the show MR page
expect(page).to have_content("Requires 3 more approvals") expect(page).to have_content("Requires 3 approvals from eligible users")
# new MR setting on the edit MR page # new MR setting on the edit MR page
find('.merge-request').click_on 'Edit' find('.merge-request').click_on 'Edit'
......
...@@ -46,6 +46,6 @@ RSpec.describe 'Merge Requests > User resets approvers', :js do ...@@ -46,6 +46,6 @@ RSpec.describe 'Merge Requests > User resets approvers', :js do
wait_for_requests wait_for_requests
expect(page).to have_content 'Requires approval' expect(page).to have_content 'Requires 1 approval'
end end
end end
...@@ -87,7 +87,7 @@ RSpec.describe 'User approves a merge request', :js do ...@@ -87,7 +87,7 @@ RSpec.describe 'User approves a merge request', :js do
page.within('.mr-state-widget') do page.within('.mr-state-widget') do
expect(page).to have_button('Merge', disabled: true) expect(page).to have_button('Merge', disabled: true)
expect(page).not_to have_button('Approve') expect(page).not_to have_button('Approve')
expect(page).to have_content('Requires approval') expect(page).to have_content('Requires 1 approval from eligible users.')
end end
end end
end end
......
...@@ -316,6 +316,22 @@ RSpec.describe ApprovalState do ...@@ -316,6 +316,22 @@ RSpec.describe ApprovalState do
expect(subject.approvals_left).to eq(12) expect(subject.approvals_left).to eq(12)
end end
context 'with any approval rule' do
it 'sums approvals_left from regular rules' do
create_rule(rule_type: :any_approver, approvals_required: 20)
expect(subject.approvals_left).to eq(20)
end
end
context 'with report approver rule' do
it 'sums code_owner_rules_left from report approver rules' do
create_rule(rule_type: :report_approver, approvals_required: 20)
expect(subject.approvals_left).to eq(32)
end
end
context 'when approval feature is unavailable' do context 'when approval feature is unavailable' do
it 'returns 0' do it 'returns 0' do
disable_feature disable_feature
...@@ -605,7 +621,7 @@ RSpec.describe ApprovalState do ...@@ -605,7 +621,7 @@ RSpec.describe ApprovalState do
end end
it 'is not approved' do it 'is not approved' do
expect(subject.approvals_left).to eq(2) expect(subject.approvals_left).to eq(1)
expect(subject.approved?).to eq(false) expect(subject.approved?).to eq(false)
end end
end end
......
...@@ -28973,13 +28973,13 @@ msgstr "" ...@@ -28973,13 +28973,13 @@ msgstr ""
msgid "Requirements can be based on users, stakeholders, system, software, or anything else you find important to capture." msgid "Requirements can be based on users, stakeholders, system, software, or anything else you find important to capture."
msgstr "" msgstr ""
msgid "Requires approval from %{names}." msgid "Requires %d approval from eligible users."
msgid_plural "Requires %{count} more approvals from %{names}." msgid_plural "Requires %d approvals from eligible users."
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "Requires approval." msgid "Requires %{count} approval from %{names}."
msgid_plural "Requires %d more approvals." msgid_plural "Requires %{count} approvals from %{names}."
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
...@@ -40209,6 +40209,9 @@ msgstr "" ...@@ -40209,6 +40209,9 @@ msgstr ""
msgid "element is not a hierarchy" msgid "element is not a hierarchy"
msgstr "" msgstr ""
msgid "eligible users"
msgstr ""
msgid "email '%{email}' is not a verified email." msgid "email '%{email}' is not a verified email."
msgstr "" msgstr ""
......
...@@ -61,9 +61,7 @@ describe('MRWidget approvals summary', () => { ...@@ -61,9 +61,7 @@ describe('MRWidget approvals summary', () => {
it('render message', () => { it('render message', () => {
const names = toNounSeriesText(testRulesLeft()); const names = toNounSeriesText(testRulesLeft());
expect(wrapper.text()).toContain( expect(wrapper.text()).toContain(`Requires ${TEST_APPROVALS_LEFT} approvals from ${names}.`);
`Requires ${TEST_APPROVALS_LEFT} more approvals from ${names}.`,
);
}); });
}); });
...@@ -75,7 +73,9 @@ describe('MRWidget approvals summary', () => { ...@@ -75,7 +73,9 @@ describe('MRWidget approvals summary', () => {
}); });
it('renders message', () => { it('renders message', () => {
expect(wrapper.text()).toContain(`Requires ${TEST_APPROVALS_LEFT} more approvals.`); expect(wrapper.text()).toContain(
`Requires ${TEST_APPROVALS_LEFT} approvals from eligible users`,
);
}); });
}); });
......
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