Commit d02ef2db authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Prevent assignment of groups using quick actions

Previously, we were expanding group mentions into their members. This
also removes groups from the assignment autocomplete.
parent 13718448
......@@ -34,6 +34,7 @@ export function membersBeforeSave(members) {
: '';
return {
type: member.type,
username: member.username,
avatarTag: autoCompleteAvatar.length === 1 ? txtAvatar : imgAvatar,
title: sanitize(title),
......@@ -275,9 +276,11 @@ class GfmAutoComplete {
return $.fn.atwho.default.callbacks.filter(query, data, searchKey);
}
if (command === MEMBER_COMMAND.ASSIGN) {
if (command === MEMBER_COMMAND.ASSIGN || command === MEMBER_COMMAND.REASSIGN) {
// Only include members which are not assigned to Issuable currently
return data.filter(member => !assignees.includes(member.search));
return data.filter(
member => member.type === 'User' && !assignees.includes(member.search),
);
} else if (command === MEMBER_COMMAND.UNASSIGN) {
// Only include members which are assigned to Issuable currently
return data.filter(member => assignees.includes(member.search));
......
......@@ -63,8 +63,13 @@ const autoCompleteMap = {
SidebarMediator.singleton?.store?.assignees?.map(assignee => assignee.username) || [];
}
if (doesCurrentLineStartWith('/assign', fullText, selectionStart)) {
return this.members.filter(member => !this.assignees.includes(member.username));
if (
doesCurrentLineStartWith('/assign', fullText, selectionStart) ||
doesCurrentLineStartWith('/reassign', fullText, selectionStart)
) {
return this.members.filter(
member => member.type === 'User' && !this.assignees.includes(member.username),
);
}
if (doesCurrentLineStartWith('/unassign', fullText, selectionStart)) {
......
......@@ -69,7 +69,7 @@ module QuickActions
def extract_users(params)
return [] if params.nil?
users = extract_references(params, :user)
users = extract_references(params, :mentioned_user)
if users.empty?
users =
......
---
title: Prevent assignment of groups using quick actions
merge_request: 42810
author:
type: fixed
......@@ -3,30 +3,49 @@
require 'spec_helper'
RSpec.describe 'GFM autocomplete EE', :js do
let(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
let(:another_user) { create(:user, name: 'another user', username: 'another.user') }
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
let_it_be(:user) { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
let_it_be(:another_user) { create(:user, name: 'another user', username: 'another.user') }
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
before do
let_it_be(:group) { create(:group) }
before_all do
project.add_maintainer(user)
project.add_developer(another_user)
group.add_developer(user)
end
context 'assignees' do
let(:issue_assignee) { create(:issue, project: project) }
let(:issue_assignee) { create(:issue, project: project, assignees: [user]) }
describe 'when tribute_autocomplete feature flag is off' do
before do
stub_feature_flags(tribute_autocomplete: false)
issue_assignee.update!(assignees: [user])
sign_in(user)
visit project_issue_path(project, issue_assignee)
wait_for_requests
end
it 'lists users who are currently not assigned to the issue when using /reassign' do
note = find('#note-body')
page.within '.timeline-content-form' do
note.native.send_keys('/reas')
end
find('.atwho-view li', text: '/reassign')
note.native.send_keys(:tab)
wait_for_requests
expect(find('#at-view-users .atwho-view-ul')).not_to have_content(user.username)
expect(find('#at-view-users .atwho-view-ul')).not_to have_content(group.name)
expect(find('#at-view-users .atwho-view-ul')).to have_content(another_user.username)
end
it 'only lists users who are currently assigned to the issue when using /unassign' do
note = find('#note-body')
page.within '.timeline-content-form' do
......@@ -48,14 +67,29 @@ RSpec.describe 'GFM autocomplete EE', :js do
before do
stub_feature_flags(tribute_autocomplete: true)
issue_assignee.update!(assignees: [user])
sign_in(user)
visit project_issue_path(project, issue_assignee)
wait_for_requests
end
it 'lists users who are currently not assigned to the issue when using /reassign' do
note = find('#note-body')
page.within '.timeline-content-form' do
note.native.send_keys('/reas')
end
find('.atwho-view li', text: '/reassign')
note.native.send_keys(:tab)
note.native.send_keys(:right)
wait_for_requests
expect(find('.tribute-container ul', visible: true)).not_to have_content(user.username)
expect(find('.tribute-container ul', visible: true)).not_to have_content(group.name)
expect(find('.tribute-container ul', visible: true)).to have_content(another_user.username)
end
it 'only lists users who are currently assigned to the issue when using /unassign' do
note = find('#note-body')
page.within '.timeline-content-form' do
......
......@@ -295,18 +295,21 @@ RSpec.describe 'GFM autocomplete', :js do
end
context 'assignees' do
let(:issue_assignee) { create(:issue, project: project) }
let(:unassigned_user) { create(:user) }
let_it_be(:issue_assignee) { create(:issue, project: project, assignees: [user]) }
let_it_be(:unassigned_user) { create(:user) }
before do
issue_assignee.update(assignees: [user])
let_it_be(:group) { create(:group) }
before_all do
project.add_maintainer(unassigned_user)
group.add_developer(user)
end
it 'lists users who are currently not assigned to the issue when using /assign' do
visit project_issue_path(project, issue_assignee)
wait_for_requests
note = find('#note-body')
page.within '.timeline-content-form' do
note.native.send_keys('/as')
......@@ -318,6 +321,7 @@ RSpec.describe 'GFM autocomplete', :js do
wait_for_requests
expect(find('#at-view-users .atwho-view-ul')).not_to have_content(user.username)
expect(find('#at-view-users .atwho-view-ul')).not_to have_content(group.name)
expect(find('#at-view-users .atwho-view-ul')).to have_content(unassigned_user.username)
end
......@@ -330,6 +334,7 @@ RSpec.describe 'GFM autocomplete', :js do
textarea.native.send_keys(:tab)
expect(find('#at-view-users .atwho-view-ul')).to have_content(unassigned_user.username)
expect(find('#at-view-users .atwho-view-ul')).not_to have_content(group.name)
expect(find('#at-view-users .atwho-view-ul')).to have_content(user.username)
end
end
......@@ -644,18 +649,21 @@ RSpec.describe 'GFM autocomplete', :js do
end
context 'assignees' do
let(:issue_assignee) { create(:issue, project: project) }
let(:unassigned_user) { create(:user) }
let_it_be(:issue_assignee) { create(:issue, project: project, assignees: [user]) }
let_it_be(:unassigned_user) { create(:user) }
before do
issue_assignee.update(assignees: [user])
let_it_be(:group) { create(:group) }
before_all do
project.add_maintainer(unassigned_user)
group.add_developer(user)
end
it 'lists users who are currently not assigned to the issue when using /assign' do
visit project_issue_path(project, issue_assignee)
wait_for_requests
note = find('#note-body')
page.within '.timeline-content-form' do
note.native.send_keys('/as')
......@@ -668,12 +676,15 @@ RSpec.describe 'GFM autocomplete', :js do
wait_for_requests
expect(find('.tribute-container ul', visible: true)).not_to have_content(user.username)
expect(find('.tribute-container ul', visible: true)).not_to have_content(group.name)
expect(find('.tribute-container ul', visible: true)).to have_content(unassigned_user.username)
end
it 'lists users who are currently not assigned to the issue when using /assign on the second line' do
visit project_issue_path(project, issue_assignee)
wait_for_requests
note = find('#note-body')
page.within '.timeline-content-form' do
note.native.send_keys('/assign @user2')
......
......@@ -278,6 +278,7 @@ describe('GfmAutoComplete', () => {
it('should set the text avatar if avatar_url is null', () => {
expect(membersBeforeSave([{ ...mockGroup, avatar_url: null }])).toEqual([
{
type: 'Group',
username: 'my-group',
avatarTag: '<div class="avatar rect-avatar center avatar-inline s26">M</div>',
title: 'My Group (2)',
......@@ -290,6 +291,7 @@ describe('GfmAutoComplete', () => {
it('should set the image avatar if avatar_url is given', () => {
expect(membersBeforeSave([mockGroup])).toEqual([
{
type: 'Group',
username: 'my-group',
avatarTag:
'<img src="./group.jpg" alt="my-group" class="avatar rect-avatar avatar-inline center s26"/>',
......@@ -303,6 +305,7 @@ describe('GfmAutoComplete', () => {
it('should set mentions disabled icon if mentionsDisabled is set', () => {
expect(membersBeforeSave([{ ...mockGroup, mentionsDisabled: true }])).toEqual([
{
type: 'Group',
username: 'my-group',
avatarTag:
'<img src="./group.jpg" alt="my-group" class="avatar rect-avatar avatar-inline center s26"/>',
......@@ -321,6 +324,7 @@ describe('GfmAutoComplete', () => {
]),
).toEqual([
{
type: 'User',
username: 'my-user',
avatarTag:
'<img src="./users.jpg" alt="my-user" class="avatar avatar-inline center s26"/>',
......
......@@ -834,6 +834,19 @@ RSpec.describe QuickActions::InterpretService do
let(:issuable) { issue }
end
context 'assigning to a group' do
let_it_be(:group) { create(:group, :public) }
before_all do
group.add_developer(create(:user))
end
it_behaves_like 'empty command', "Failed to assign a user because no user was found." do
let(:content) { "/assign #{group.to_reference}" }
let(:issuable) { issue }
end
end
context 'unassign command' do
let(:content) { '/unassign' }
......
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