Commit 0670caf0 authored by Alper Akgun's avatar Alper Akgun

Merge branch 'ph/removeMergeRequestReviewerState' into 'master'

Toggle assignee and reviewer attention requested state

See merge request gitlab-org/gitlab!74176
parents 374bf0c8 0b9fe3bc
...@@ -39,8 +39,8 @@ export default { ...@@ -39,8 +39,8 @@ export default {
assignSelf() { assignSelf() {
this.$emit('assign-self'); this.$emit('assign-self');
}, },
toggleAttentionRequired(data) { toggleAttentionRequested(data) {
this.$emit('toggle-attention-required', data); this.$emit('toggle-attention-requested', data);
}, },
}, },
}; };
...@@ -65,7 +65,7 @@ export default { ...@@ -65,7 +65,7 @@ export default {
v-else v-else
:users="sortedAssigness" :users="sortedAssigness"
:issuable-type="issuableType" :issuable-type="issuableType"
@toggle-attention-required="toggleAttentionRequired" @toggle-attention-requested="toggleAttentionRequested"
/> />
</div> </div>
</div> </div>
......
...@@ -33,8 +33,8 @@ export default { ...@@ -33,8 +33,8 @@ export default {
}, },
}, },
methods: { methods: {
toggleAttentionRequired(data) { toggleAttentionRequested(data) {
this.$emit('toggle-attention-required', data); this.$emit('toggle-attention-requested', data);
}, },
}, },
}; };
...@@ -66,7 +66,7 @@ export default { ...@@ -66,7 +66,7 @@ export default {
:users="users" :users="users"
:issuable-type="issuableType" :issuable-type="issuableType"
class="gl-text-gray-800 gl-mt-2 hide-collapsed" class="gl-text-gray-800 gl-mt-2 hide-collapsed"
@toggle-attention-required="toggleAttentionRequired" @toggle-attention-requested="toggleAttentionRequested"
/> />
</div> </div>
</template> </template>
...@@ -125,8 +125,8 @@ export default { ...@@ -125,8 +125,8 @@ export default {
availability: this.assigneeAvailabilityStatus[username] || '', availability: this.assigneeAvailabilityStatus[username] || '',
})); }));
}, },
toggleAttentionRequired(data) { toggleAttentionRequested(data) {
this.mediator.toggleAttentionRequired('assignee', data); this.mediator.toggleAttentionRequested('assignee', data);
}, },
}, },
}; };
...@@ -155,7 +155,7 @@ export default { ...@@ -155,7 +155,7 @@ export default {
:editable="store.editable" :editable="store.editable"
:issuable-type="issuableType" :issuable-type="issuableType"
@assign-self="assignSelf" @assign-self="assignSelf"
@toggle-attention-required="toggleAttentionRequired" @toggle-attention-requested="toggleAttentionRequested"
/> />
</div> </div>
</template> </template>
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { IssuableType } from '~/issue_show/constants'; import { IssuableType } from '~/issue_show/constants';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import AttentionRequiredToggle from '../attention_required_toggle.vue'; import AttentionRequestedToggle from '../attention_requested_toggle.vue';
import AssigneeAvatarLink from './assignee_avatar_link.vue'; import AssigneeAvatarLink from './assignee_avatar_link.vue';
import UserNameWithStatus from './user_name_with_status.vue'; import UserNameWithStatus from './user_name_with_status.vue';
...@@ -10,7 +10,7 @@ const DEFAULT_RENDER_COUNT = 5; ...@@ -10,7 +10,7 @@ const DEFAULT_RENDER_COUNT = 5;
export default { export default {
components: { components: {
AttentionRequiredToggle, AttentionRequestedToggle,
AssigneeAvatarLink, AssigneeAvatarLink,
UserNameWithStatus, UserNameWithStatus,
}, },
...@@ -82,8 +82,8 @@ export default { ...@@ -82,8 +82,8 @@ export default {
} }
return u?.status?.availability || ''; return u?.status?.availability || '';
}, },
toggleAttentionRequired(data) { toggleAttentionRequested(data) {
this.$emit('toggle-attention-required', data); this.$emit('toggle-attention-requested', data);
}, },
}, },
}; };
...@@ -113,11 +113,11 @@ export default { ...@@ -113,11 +113,11 @@ export default {
}" }"
class="gl-display-inline-block" class="gl-display-inline-block"
> >
<attention-required-toggle <attention-requested-toggle
v-if="showVerticalList && user.can_update_merge_request" v-if="showVerticalList && user.can_update_merge_request"
:user="user" :user="user"
type="assignee" type="assignee"
@toggle-attention-required="toggleAttentionRequired" @toggle-attention-requested="toggleAttentionRequested"
/> />
<assignee-avatar-link <assignee-avatar-link
:user="user" :user="user"
......
...@@ -5,9 +5,9 @@ import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants'; ...@@ -5,9 +5,9 @@ import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
export default { export default {
i18n: { i18n: {
attentionRequiredReviewer: __('Request attention to review'), attentionRequestedReviewer: __('Request attention to review'),
attentionRequiredAssignee: __('Request attention'), attentionRequestedAssignee: __('Request attention'),
removeAttentionRequired: __('Remove attention request'), removeAttentionRequested: __('Remove attention request'),
}, },
components: { components: {
GlButton, GlButton,
...@@ -32,13 +32,13 @@ export default { ...@@ -32,13 +32,13 @@ export default {
}, },
computed: { computed: {
tooltipTitle() { tooltipTitle() {
if (this.user.attention_required) { if (this.user.attention_requested) {
return this.$options.i18n.removeAttentionRequired; return this.$options.i18n.removeAttentionRequested;
} }
return this.type === 'reviewer' return this.type === 'reviewer'
? this.$options.i18n.attentionRequiredReviewer ? this.$options.i18n.attentionRequestedReviewer
: this.$options.i18n.attentionRequiredAssignee; : this.$options.i18n.attentionRequestedAssignee;
}, },
}, },
methods: { methods: {
...@@ -47,7 +47,7 @@ export default { ...@@ -47,7 +47,7 @@ export default {
this.$root.$emit(BV_HIDE_TOOLTIP); this.$root.$emit(BV_HIDE_TOOLTIP);
this.loading = true; this.loading = true;
this.$emit('toggle-attention-required', { this.$emit('toggle-attention-requested', {
user: this.user, user: this.user,
callback: this.toggleAttentionRequiredComplete, callback: this.toggleAttentionRequiredComplete,
}); });
...@@ -63,8 +63,8 @@ export default { ...@@ -63,8 +63,8 @@ export default {
<span v-gl-tooltip.left.viewport="tooltipTitle"> <span v-gl-tooltip.left.viewport="tooltipTitle">
<gl-button <gl-button
:loading="loading" :loading="loading"
:variant="user.attention_required ? 'warning' : 'default'" :variant="user.attention_requested ? 'warning' : 'default'"
:icon="user.attention_required ? 'star' : 'star-o'" :icon="user.attention_requested ? 'star' : 'star-o'"
:aria-label="tooltipTitle" :aria-label="tooltipTitle"
size="small" size="small"
category="tertiary" category="tertiary"
......
...@@ -49,8 +49,8 @@ export default { ...@@ -49,8 +49,8 @@ export default {
requestReview(data) { requestReview(data) {
this.$emit('request-review', data); this.$emit('request-review', data);
}, },
toggleAttentionRequired(data) { toggleAttentionRequested(data) {
this.$emit('toggle-attention-required', data); this.$emit('toggle-attention-requested', data);
}, },
}, },
}; };
...@@ -73,7 +73,7 @@ export default { ...@@ -73,7 +73,7 @@ export default {
:root-path="rootPath" :root-path="rootPath"
:issuable-type="issuableType" :issuable-type="issuableType"
@request-review="requestReview" @request-review="requestReview"
@toggle-attention-required="toggleAttentionRequired" @toggle-attention-requested="toggleAttentionRequested"
/> />
</div> </div>
</div> </div>
......
...@@ -88,8 +88,8 @@ export default { ...@@ -88,8 +88,8 @@ export default {
requestReview(data) { requestReview(data) {
this.mediator.requestReview(data); this.mediator.requestReview(data);
}, },
toggleAttentionRequired(data) { toggleAttentionRequested(data) {
this.mediator.toggleAttentionRequired('reviewer', data); this.mediator.toggleAttentionRequested('reviewer', data);
}, },
}, },
}; };
...@@ -109,7 +109,7 @@ export default { ...@@ -109,7 +109,7 @@ export default {
:editable="store.editable" :editable="store.editable"
:issuable-type="issuableType" :issuable-type="issuableType"
@request-review="requestReview" @request-review="requestReview"
@toggle-attention-required="toggleAttentionRequired" @toggle-attention-requested="toggleAttentionRequested"
/> />
</div> </div>
</template> </template>
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import { GlButton, GlTooltipDirective, GlIcon } from '@gitlab/ui'; import { GlButton, GlTooltipDirective, GlIcon } from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { __, sprintf, s__ } from '~/locale'; import { __, sprintf, s__ } from '~/locale';
import AttentionRequiredToggle from '../attention_required_toggle.vue'; import AttentionRequestedToggle from '../attention_requested_toggle.vue';
import ReviewerAvatarLink from './reviewer_avatar_link.vue'; import ReviewerAvatarLink from './reviewer_avatar_link.vue';
const LOADING_STATE = 'loading'; const LOADING_STATE = 'loading';
...@@ -16,7 +16,7 @@ export default { ...@@ -16,7 +16,7 @@ export default {
GlButton, GlButton,
GlIcon, GlIcon,
ReviewerAvatarLink, ReviewerAvatarLink,
AttentionRequiredToggle, AttentionRequestedToggle,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -80,8 +80,8 @@ export default { ...@@ -80,8 +80,8 @@ export default {
this.loadingStates[userId] = null; this.loadingStates[userId] = null;
} }
}, },
toggleAttentionRequired(data) { toggleAttentionRequested(data) {
this.$emit('toggle-attention-required', data); this.$emit('toggle-attention-requested', data);
}, },
}, },
LOADING_STATE, LOADING_STATE,
...@@ -97,11 +97,11 @@ export default { ...@@ -97,11 +97,11 @@ export default {
:class="{ 'gl-mb-3': index !== users.length - 1 }" :class="{ 'gl-mb-3': index !== users.length - 1 }"
data-testid="reviewer" data-testid="reviewer"
> >
<attention-required-toggle <attention-requested-toggle
v-if="glFeatures.mrAttentionRequests && user.can_update_merge_request" v-if="glFeatures.mrAttentionRequests && user.can_update_merge_request"
:user="user" :user="user"
type="reviewer" type="reviewer"
@toggle-attention-required="toggleAttentionRequired" @toggle-attention-requested="toggleAttentionRequested"
/> />
<reviewer-avatar-link :user="user" :root-path="rootPath" :issuable-type="issuableType"> <reviewer-avatar-link :user="user" :root-path="rootPath" :issuable-type="issuableType">
<div class="gl-ml-3 gl-line-height-normal gl-display-grid"> <div class="gl-ml-3 gl-line-height-normal gl-display-grid">
......
mutation mergeRequestAttentionRequired($projectPath: ID!, $iid: String!, $userId: ID!) {
mergeRequestAttentionRequired(input: { projectPath: $projectPath, iid: $iid, userId: $userId }) {
errors
}
}
mutation mergeRequestToggleAttentionRequested($projectPath: ID!, $iid: String!, $userId: ID!) {
mergeRequestToggleAttentionRequested(
input: { projectPath: $projectPath, iid: $iid, userId: $userId }
) {
errors
}
}
...@@ -5,7 +5,7 @@ import createGqClient, { fetchPolicies } from '~/lib/graphql'; ...@@ -5,7 +5,7 @@ import createGqClient, { fetchPolicies } from '~/lib/graphql';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import reviewerRereviewMutation from '../queries/reviewer_rereview.mutation.graphql'; import reviewerRereviewMutation from '../queries/reviewer_rereview.mutation.graphql';
import sidebarDetailsMRQuery from '../queries/sidebarDetailsMR.query.graphql'; import sidebarDetailsMRQuery from '../queries/sidebarDetailsMR.query.graphql';
import attentionRequiredMutation from '../queries/attention_required.mutation.graphql'; import toggleAttentionRequestedMutation from '../queries/toggle_attention_requested.mutation.graphql';
const queries = { const queries = {
merge_request: sidebarDetailsMRQuery, merge_request: sidebarDetailsMRQuery,
...@@ -92,9 +92,9 @@ export default class SidebarService { ...@@ -92,9 +92,9 @@ export default class SidebarService {
}); });
} }
attentionRequired(userId) { toggleAttentionRequested(userId) {
return gqClient.mutate({ return gqClient.mutate({
mutation: attentionRequiredMutation, mutation: toggleAttentionRequestedMutation,
variables: { variables: {
userId: convertToGraphQLId(TYPE_USER, `${userId}`), userId: convertToGraphQLId(TYPE_USER, `${userId}`),
projectPath: this.fullPath, projectPath: this.fullPath,
......
...@@ -63,30 +63,27 @@ export default class SidebarMediator { ...@@ -63,30 +63,27 @@ export default class SidebarMediator {
.catch(() => callback(userId, false)); .catch(() => callback(userId, false));
} }
async toggleAttentionRequired(type, { user, callback }) { async toggleAttentionRequested(type, { user, callback }) {
try { try {
const isReviewer = type === 'reviewer'; const isReviewer = type === 'reviewer';
const reviewerOrAssignee = isReviewer const reviewerOrAssignee = isReviewer
? this.store.findReviewer(user) ? this.store.findReviewer(user)
: this.store.findAssignee(user); : this.store.findAssignee(user);
if (reviewerOrAssignee.attention_required) { await this.service.toggleAttentionRequested(user.id);
if (reviewerOrAssignee.attention_requested) {
toast( toast(
sprintf(__('Removed attention request from @%{username}'), { sprintf(__('Removed attention request from @%{username}'), {
username: user.username, username: user.username,
}), }),
); );
} else { } else {
await this.service.attentionRequired(user.id);
toast(sprintf(__('Requested attention from @%{username}'), { username: user.username })); toast(sprintf(__('Requested attention from @%{username}'), { username: user.username }));
} }
if (isReviewer) { this.store.updateReviewer(user.id, 'attention_requested');
this.store.updateReviewer(user.id, 'attention_required'); this.store.updateAssignee(user.id, 'attention_requested');
} else {
this.store.updateAssignee(user.id, 'attention_required');
}
callback(); callback();
} catch (error) { } catch (error) {
......
...@@ -2,20 +2,20 @@ ...@@ -2,20 +2,20 @@
module Mutations module Mutations
module MergeRequests module MergeRequests
class AttentionRequired < Base class ToggleAttentionRequested < Base
graphql_name 'MergeRequestAttentionRequired' graphql_name 'MergeRequestToggleAttentionRequested'
argument :user_id, ::Types::GlobalIDType[::User], argument :user_id, ::Types::GlobalIDType[::User],
loads: Types::UserType, loads: Types::UserType,
required: true, required: true,
description: <<~DESC description: <<~DESC
User ID for the user that has their attention requested. User ID for the user to toggle attention requested.
DESC DESC
def resolve(project_path:, iid:, user:) def resolve(project_path:, iid:, user:)
merge_request = authorized_find!(project_path: project_path, iid: iid) merge_request = authorized_find!(project_path: project_path, iid: iid)
result = ::MergeRequests::AttentionRequiredService.new(project: merge_request.project, current_user: current_user, merge_request: merge_request, user: user).execute result = ::MergeRequests::ToggleAttentionRequestedService.new(project: merge_request.project, current_user: current_user, merge_request: merge_request, user: user).execute
{ {
merge_request: merge_request, merge_request: merge_request,
......
...@@ -68,7 +68,7 @@ module Types ...@@ -68,7 +68,7 @@ module Types
mount_mutation Mutations::MergeRequests::SetDraft, calls_gitaly: true mount_mutation Mutations::MergeRequests::SetDraft, calls_gitaly: true
mount_mutation Mutations::MergeRequests::SetAssignees mount_mutation Mutations::MergeRequests::SetAssignees
mount_mutation Mutations::MergeRequests::ReviewerRereview mount_mutation Mutations::MergeRequests::ReviewerRereview
mount_mutation Mutations::MergeRequests::AttentionRequired, feature_flag: :mr_attention_requests mount_mutation Mutations::MergeRequests::ToggleAttentionRequested, feature_flag: :mr_attention_requests
mount_mutation Mutations::Metrics::Dashboard::Annotations::Create mount_mutation Mutations::Metrics::Dashboard::Annotations::Create
mount_mutation Mutations::Metrics::Dashboard::Annotations::Delete mount_mutation Mutations::Metrics::Dashboard::Annotations::Delete
mount_mutation Mutations::Notes::Create::Note, calls_gitaly: true mount_mutation Mutations::Notes::Create::Note, calls_gitaly: true
......
...@@ -24,7 +24,7 @@ module TodosHelper ...@@ -24,7 +24,7 @@ module TodosHelper
when Todo::UNMERGEABLE then 'Could not merge' when Todo::UNMERGEABLE then 'Could not merge'
when Todo::DIRECTLY_ADDRESSED then "directly addressed #{todo_action_subject(todo)} on" when Todo::DIRECTLY_ADDRESSED then "directly addressed #{todo_action_subject(todo)} on"
when Todo::MERGE_TRAIN_REMOVED then "Removed from Merge Train:" when Todo::MERGE_TRAIN_REMOVED then "Removed from Merge Train:"
when Todo::ATTENTION_REQUIRED then 'requested your attention on' when Todo::ATTENTION_REQUESTED then 'requested your attention on'
end end
end end
......
...@@ -7,7 +7,7 @@ module MergeRequestReviewerState ...@@ -7,7 +7,7 @@ module MergeRequestReviewerState
enum state: { enum state: {
unreviewed: 0, unreviewed: 0,
reviewed: 1, reviewed: 1,
attention_required: 2 attention_requested: 2
} }
validates :state, validates :state,
...@@ -18,7 +18,7 @@ module MergeRequestReviewerState ...@@ -18,7 +18,7 @@ module MergeRequestReviewerState
def set_state def set_state
if Feature.enabled?(:mr_attention_requests, self.merge_request&.project, default_enabled: :yaml) if Feature.enabled?(:mr_attention_requests, self.merge_request&.project, default_enabled: :yaml)
self.state = :attention_required self.state = :attention_requested
end end
end end
end end
......
...@@ -1945,7 +1945,7 @@ class MergeRequest < ApplicationRecord ...@@ -1945,7 +1945,7 @@ class MergeRequest < ApplicationRecord
end end
end end
def attention_required_enabled? def attention_requested_enabled?
Feature.enabled?(:mr_attention_requests, project, default_enabled: :yaml) Feature.enabled?(:mr_attention_requests, project, default_enabled: :yaml)
end end
......
...@@ -18,7 +18,7 @@ class Todo < ApplicationRecord ...@@ -18,7 +18,7 @@ class Todo < ApplicationRecord
DIRECTLY_ADDRESSED = 7 DIRECTLY_ADDRESSED = 7
MERGE_TRAIN_REMOVED = 8 # This is an EE-only feature MERGE_TRAIN_REMOVED = 8 # This is an EE-only feature
REVIEW_REQUESTED = 9 REVIEW_REQUESTED = 9
ATTENTION_REQUIRED = 10 ATTENTION_REQUESTED = 10
ACTION_NAMES = { ACTION_NAMES = {
ASSIGNED => :assigned, ASSIGNED => :assigned,
...@@ -30,7 +30,7 @@ class Todo < ApplicationRecord ...@@ -30,7 +30,7 @@ class Todo < ApplicationRecord
UNMERGEABLE => :unmergeable, UNMERGEABLE => :unmergeable,
DIRECTLY_ADDRESSED => :directly_addressed, DIRECTLY_ADDRESSED => :directly_addressed,
MERGE_TRAIN_REMOVED => :merge_train_removed, MERGE_TRAIN_REMOVED => :merge_train_removed,
ATTENTION_REQUIRED => :attention_required ATTENTION_REQUESTED => :attention_requested
}.freeze }.freeze
belongs_to :author, class_name: "User" belongs_to :author, class_name: "User"
...@@ -191,8 +191,8 @@ class Todo < ApplicationRecord ...@@ -191,8 +191,8 @@ class Todo < ApplicationRecord
action == REVIEW_REQUESTED action == REVIEW_REQUESTED
end end
def attention_required? def attention_requested?
action == ATTENTION_REQUIRED action == ATTENTION_REQUESTED
end end
def merge_train_removed? def merge_train_removed?
......
...@@ -20,8 +20,8 @@ class MergeRequestUserEntity < ::API::Entities::UserBasic ...@@ -20,8 +20,8 @@ class MergeRequestUserEntity < ::API::Entities::UserBasic
find_reviewer_or_assignee(user, options)&.reviewed? find_reviewer_or_assignee(user, options)&.reviewed?
end end
expose :attention_required, if: satisfies(:present?, :allows_reviewers?, :attention_required_enabled?) do |user, options| expose :attention_requested, if: satisfies(:present?, :allows_reviewers?, :attention_requested_enabled?) do |user, options|
find_reviewer_or_assignee(user, options)&.attention_required? find_reviewer_or_assignee(user, options)&.attention_requested?
end end
expose :approved, if: satisfies(:present?) do |user, options| expose :approved, if: satisfies(:present?) do |user, options|
......
# frozen_string_literal: true # frozen_string_literal: true
module MergeRequests module MergeRequests
class AttentionRequiredService < MergeRequests::BaseService class ToggleAttentionRequestedService < MergeRequests::BaseService
attr_accessor :merge_request, :user attr_accessor :merge_request, :user
def initialize(project:, current_user:, merge_request:, user:) def initialize(project:, current_user:, merge_request:, user:)
...@@ -15,10 +15,12 @@ module MergeRequests ...@@ -15,10 +15,12 @@ module MergeRequests
return error("Invalid permissions") unless can?(current_user, :update_merge_request, merge_request) return error("Invalid permissions") unless can?(current_user, :update_merge_request, merge_request)
if reviewer || assignee if reviewer || assignee
reviewer&.update(state: :attention_required) update_state(reviewer)
assignee&.update(state: :attention_required) update_state(assignee)
notity_user if reviewer&.attention_requested? || assignee&.attention_requested?
notity_user
end
success success
else else
...@@ -29,7 +31,7 @@ module MergeRequests ...@@ -29,7 +31,7 @@ module MergeRequests
private private
def notity_user def notity_user
todo_service.create_attention_required_todo(merge_request, current_user, user) todo_service.create_attention_requested_todo(merge_request, current_user, user)
end end
def assignee def assignee
...@@ -39,5 +41,9 @@ module MergeRequests ...@@ -39,5 +41,9 @@ module MergeRequests
def reviewer def reviewer
merge_request.find_reviewer(user) merge_request.find_reviewer(user)
end end
def update_state(reviewer_or_assignee)
reviewer_or_assignee&.update(state: reviewer_or_assignee&.attention_requested? ? :reviewed : :attention_requested)
end
end end
end end
...@@ -217,8 +217,8 @@ class TodoService ...@@ -217,8 +217,8 @@ class TodoService
create_todos(reviewers, attributes) create_todos(reviewers, attributes)
end end
def create_attention_required_todo(target, author, users) def create_attention_requested_todo(target, author, users)
attributes = attributes_for_todo(target.project, target, author, Todo::ATTENTION_REQUIRED) attributes = attributes_for_todo(target.project, target, author, Todo::ATTENTION_REQUESTED)
create_todos(users, attributes) create_todos(users, attributes)
end end
......
...@@ -3313,29 +3313,6 @@ Input type: `MergeRequestAcceptInput` ...@@ -3313,29 +3313,6 @@ Input type: `MergeRequestAcceptInput`
| <a id="mutationmergerequestaccepterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | <a id="mutationmergerequestaccepterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationmergerequestacceptmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request after mutation. | | <a id="mutationmergerequestacceptmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request after mutation. |
### `Mutation.mergeRequestAttentionRequired`
Available only when feature flag `mr_attention_requests` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
Input type: `MergeRequestAttentionRequiredInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationmergerequestattentionrequiredclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationmergerequestattentionrequirediid"></a>`iid` | [`String!`](#string) | IID of the merge request to mutate. |
| <a id="mutationmergerequestattentionrequiredprojectpath"></a>`projectPath` | [`ID!`](#id) | Project the merge request to mutate is in. |
| <a id="mutationmergerequestattentionrequireduserid"></a>`userId` | [`UserID!`](#userid) | User ID for the user that has their attention requested. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationmergerequestattentionrequiredclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationmergerequestattentionrequirederrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationmergerequestattentionrequiredmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request after mutation. |
### `Mutation.mergeRequestCreate` ### `Mutation.mergeRequestCreate`
Input type: `MergeRequestCreateInput` Input type: `MergeRequestCreateInput`
...@@ -3509,6 +3486,29 @@ Input type: `MergeRequestSetSubscriptionInput` ...@@ -3509,6 +3486,29 @@ Input type: `MergeRequestSetSubscriptionInput`
| <a id="mutationmergerequestsetsubscriptionerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | <a id="mutationmergerequestsetsubscriptionerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationmergerequestsetsubscriptionmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request after mutation. | | <a id="mutationmergerequestsetsubscriptionmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request after mutation. |
### `Mutation.mergeRequestToggleAttentionRequested`
Available only when feature flag `mr_attention_requests` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
Input type: `MergeRequestToggleAttentionRequestedInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationmergerequesttoggleattentionrequestedclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationmergerequesttoggleattentionrequestediid"></a>`iid` | [`String!`](#string) | IID of the merge request to mutate. |
| <a id="mutationmergerequesttoggleattentionrequestedprojectpath"></a>`projectPath` | [`ID!`](#id) | Project the merge request to mutate is in. |
| <a id="mutationmergerequesttoggleattentionrequesteduserid"></a>`userId` | [`UserID!`](#userid) | User ID for the user to toggle attention requested. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationmergerequesttoggleattentionrequestedclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationmergerequesttoggleattentionrequestederrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationmergerequesttoggleattentionrequestedmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request after mutation. |
### `Mutation.mergeRequestUpdate` ### `Mutation.mergeRequestUpdate`
Update attributes of a merge request. Update attributes of a merge request.
...@@ -16474,7 +16474,7 @@ State of a review of a GitLab merge request. ...@@ -16474,7 +16474,7 @@ State of a review of a GitLab merge request.
| Value | Description | | Value | Description |
| ----- | ----------- | | ----- | ----------- |
| <a id="mergerequestreviewstateattention_required"></a>`ATTENTION_REQUIRED` | The merge request is attention_required. | | <a id="mergerequestreviewstateattention_requested"></a>`ATTENTION_REQUESTED` | The merge request is attention_requested. |
| <a id="mergerequestreviewstatereviewed"></a>`REVIEWED` | The merge request is reviewed. | | <a id="mergerequestreviewstatereviewed"></a>`REVIEWED` | The merge request is reviewed. |
| <a id="mergerequestreviewstateunreviewed"></a>`UNREVIEWED` | The merge request is unreviewed. | | <a id="mergerequestreviewstateunreviewed"></a>`UNREVIEWED` | The merge request is unreviewed. |
import { GlButton } from '@gitlab/ui'; import { GlButton } from '@gitlab/ui';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import AttentionRequiredToggle from '~/sidebar/components/attention_required_toggle.vue'; import AttentionRequestedToggle from '~/sidebar/components/attention_requested_toggle.vue';
let wrapper; let wrapper;
function factory(propsData = {}) { function factory(propsData = {}) {
wrapper = mount(AttentionRequiredToggle, { propsData }); wrapper = mount(AttentionRequestedToggle, { propsData });
} }
const findToggle = () => wrapper.findComponent(GlButton); const findToggle = () => wrapper.findComponent(GlButton);
...@@ -16,52 +16,52 @@ describe('Attention require toggle', () => { ...@@ -16,52 +16,52 @@ describe('Attention require toggle', () => {
}); });
it('renders button', () => { it('renders button', () => {
factory({ type: 'reviewer', user: { attention_required: false } }); factory({ type: 'reviewer', user: { attention_requested: false } });
expect(findToggle().exists()).toBe(true); expect(findToggle().exists()).toBe(true);
}); });
it.each` it.each`
attentionRequired | icon attentionRequested | icon
${true} | ${'star'} ${true} | ${'star'}
${false} | ${'star-o'} ${false} | ${'star-o'}
`( `(
'renders $icon icon when attention_required is $attentionRequired', 'renders $icon icon when attention_requested is $attentionRequested',
({ attentionRequired, icon }) => { ({ attentionRequested, icon }) => {
factory({ type: 'reviewer', user: { attention_required: attentionRequired } }); factory({ type: 'reviewer', user: { attention_requested: attentionRequested } });
expect(findToggle().props('icon')).toBe(icon); expect(findToggle().props('icon')).toBe(icon);
}, },
); );
it.each` it.each`
attentionRequired | variant attentionRequested | variant
${true} | ${'warning'} ${true} | ${'warning'}
${false} | ${'default'} ${false} | ${'default'}
`( `(
'renders button with variant $variant when attention_required is $attentionRequired', 'renders button with variant $variant when attention_requested is $attentionRequested',
({ attentionRequired, variant }) => { ({ attentionRequested, variant }) => {
factory({ type: 'reviewer', user: { attention_required: attentionRequired } }); factory({ type: 'reviewer', user: { attention_requested: attentionRequested } });
expect(findToggle().props('variant')).toBe(variant); expect(findToggle().props('variant')).toBe(variant);
}, },
); );
it('emits toggle-attention-required on click', async () => { it('emits toggle-attention-requested on click', async () => {
factory({ type: 'reviewer', user: { attention_required: true } }); factory({ type: 'reviewer', user: { attention_requested: true } });
await findToggle().trigger('click'); await findToggle().trigger('click');
expect(wrapper.emitted('toggle-attention-required')[0]).toEqual([ expect(wrapper.emitted('toggle-attention-requested')[0]).toEqual([
{ {
user: { attention_required: true }, user: { attention_requested: true },
callback: expect.anything(), callback: expect.anything(),
}, },
]); ]);
}); });
it('sets loading on click', async () => { it('sets loading on click', async () => {
factory({ type: 'reviewer', user: { attention_required: true } }); factory({ type: 'reviewer', user: { attention_requested: true } });
await findToggle().trigger('click'); await findToggle().trigger('click');
...@@ -69,14 +69,14 @@ describe('Attention require toggle', () => { ...@@ -69,14 +69,14 @@ describe('Attention require toggle', () => {
}); });
it.each` it.each`
type | attentionRequired | tooltip type | attentionRequested | tooltip
${'reviewer'} | ${true} | ${AttentionRequiredToggle.i18n.removeAttentionRequired} ${'reviewer'} | ${true} | ${AttentionRequestedToggle.i18n.removeAttentionRequested}
${'reviewer'} | ${false} | ${AttentionRequiredToggle.i18n.attentionRequiredReviewer} ${'reviewer'} | ${false} | ${AttentionRequestedToggle.i18n.attentionRequestedReviewer}
${'assignee'} | ${false} | ${AttentionRequiredToggle.i18n.attentionRequiredAssignee} ${'assignee'} | ${false} | ${AttentionRequestedToggle.i18n.attentionRequestedAssignee}
`( `(
'sets tooltip as $tooltip when attention_required is $attentionRequired and type is $type', 'sets tooltip as $tooltip when attention_requested is $attentionRequested and type is $type',
({ type, attentionRequired, tooltip }) => { ({ type, attentionRequested, tooltip }) => {
factory({ type, user: { attention_required: attentionRequired } }); factory({ type, user: { attention_requested: attentionRequested } });
expect(findToggle().attributes('aria-label')).toBe(tooltip); expect(findToggle().attributes('aria-label')).toBe(tooltip);
}, },
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import AttentionRequiredToggle from '~/sidebar/components/attention_required_toggle.vue'; import AttentionRequestedToggle from '~/sidebar/components/attention_requested_toggle.vue';
import ReviewerAvatarLink from '~/sidebar/components/reviewers/reviewer_avatar_link.vue'; import ReviewerAvatarLink from '~/sidebar/components/reviewers/reviewer_avatar_link.vue';
import UncollapsedReviewerList from '~/sidebar/components/reviewers/uncollapsed_reviewer_list.vue'; import UncollapsedReviewerList from '~/sidebar/components/reviewers/uncollapsed_reviewer_list.vue';
import userDataMock from '../../user_data_mock'; import userDataMock from '../../user_data_mock';
...@@ -121,11 +121,11 @@ describe('UncollapsedReviewerList component', () => { ...@@ -121,11 +121,11 @@ describe('UncollapsedReviewerList component', () => {
expect(wrapper.findAll('[data-testid="re-request-button"]').length).toBe(0); expect(wrapper.findAll('[data-testid="re-request-button"]').length).toBe(0);
}); });
it('emits toggle-attention-required', () => { it('emits toggle-attention-requested', () => {
createComponent({ users: [userDataMock()] }, { mrAttentionRequests: true }); createComponent({ users: [userDataMock()] }, { mrAttentionRequests: true });
wrapper.find(AttentionRequiredToggle).vm.$emit('toggle-attention-required', 'data'); wrapper.find(AttentionRequestedToggle).vm.$emit('toggle-attention-requested', 'data');
expect(wrapper.emitted('toggle-attention-required')[0]).toEqual(['data']); expect(wrapper.emitted('toggle-attention-requested')[0]).toEqual(['data']);
}); });
}); });
...@@ -119,19 +119,19 @@ describe('Sidebar mediator', () => { ...@@ -119,19 +119,19 @@ describe('Sidebar mediator', () => {
}); });
}); });
describe('toggleAttentionRequired', () => { describe('toggleAttentionRequested', () => {
let attentionRequiredService; let attentionRequiredService;
beforeEach(() => { beforeEach(() => {
attentionRequiredService = jest attentionRequiredService = jest
.spyOn(mediator.service, 'attentionRequired') .spyOn(mediator.service, 'toggleAttentionRequested')
.mockResolvedValue(); .mockResolvedValue();
}); });
it('calls attentionRequired service method', async () => { it('calls attentionRequired service method', async () => {
mediator.store.reviewers = [{ id: 1, attention_required: false, username: 'root' }]; mediator.store.reviewers = [{ id: 1, attention_requested: false, username: 'root' }];
await mediator.toggleAttentionRequired('reviewer', { await mediator.toggleAttentionRequested('reviewer', {
user: { id: 1, username: 'root' }, user: { id: 1, username: 'root' },
callback: jest.fn(), callback: jest.fn(),
}); });
...@@ -145,23 +145,23 @@ describe('Sidebar mediator', () => { ...@@ -145,23 +145,23 @@ describe('Sidebar mediator', () => {
`('finds $type', ({ type, method }) => { `('finds $type', ({ type, method }) => {
const methodSpy = jest.spyOn(mediator.store, method); const methodSpy = jest.spyOn(mediator.store, method);
mediator.toggleAttentionRequired(type, { user: { id: 1 }, callback: jest.fn() }); mediator.toggleAttentionRequested(type, { user: { id: 1 }, callback: jest.fn() });
expect(methodSpy).toHaveBeenCalledWith({ id: 1 }); expect(methodSpy).toHaveBeenCalledWith({ id: 1 });
}); });
it.each` it.each`
attentionRequired | toastMessage attentionRequested | toastMessage
${true} | ${'Removed attention request from @root'} ${true} | ${'Removed attention request from @root'}
${false} | ${'Requested attention from @root'} ${false} | ${'Requested attention from @root'}
`( `(
'it creates toast $toastMessage when attention_required is $attentionRequired', 'it creates toast $toastMessage when attention_requested is $attentionRequested',
async ({ attentionRequired, toastMessage }) => { async ({ attentionRequested, toastMessage }) => {
mediator.store.reviewers = [ mediator.store.reviewers = [
{ id: 1, attention_required: attentionRequired, username: 'root' }, { id: 1, attention_requested: attentionRequested, username: 'root' },
]; ];
await mediator.toggleAttentionRequired('reviewer', { await mediator.toggleAttentionRequested('reviewer', {
user: { id: 1, username: 'root' }, user: { id: 1, username: 'root' },
callback: jest.fn(), callback: jest.fn(),
}); });
......
...@@ -13,9 +13,9 @@ RSpec.describe GitlabSchema.types['MergeRequestReviewState'] do ...@@ -13,9 +13,9 @@ RSpec.describe GitlabSchema.types['MergeRequestReviewState'] do
description: 'The merge request is unreviewed.', description: 'The merge request is unreviewed.',
value: 'unreviewed' value: 'unreviewed'
), ),
'ATTENTION_REQUIRED' => have_attributes( 'ATTENTION_REQUESTED' => have_attributes(
description: 'The merge request is attention_required.', description: 'The merge request is attention_requested.',
value: 'attention_required' value: 'attention_requested'
) )
) )
end end
......
...@@ -78,7 +78,7 @@ RSpec.describe GitlabSchema.types['UserMergeRequestInteraction'] do ...@@ -78,7 +78,7 @@ RSpec.describe GitlabSchema.types['UserMergeRequestInteraction'] do
merge_request.reviewers << user merge_request.reviewers << user
end end
it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['ATTENTION_REQUIRED'].value) } it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['ATTENTION_REQUESTED'].value) }
it 'implies not reviewed' do it 'implies not reviewed' do
expect(resolve(:reviewed)).to be false expect(resolve(:reviewed)).to be false
......
...@@ -61,7 +61,7 @@ RSpec.describe ::Users::MergeRequestInteraction do ...@@ -61,7 +61,7 @@ RSpec.describe ::Users::MergeRequestInteraction do
merge_request.reviewers << user merge_request.reviewers << user
end end
it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['ATTENTION_REQUIRED'].value) } it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['ATTENTION_REQUESTED'].value) }
it 'implies not reviewed' do it 'implies not reviewed' do
expect(interaction).not_to be_reviewed expect(interaction).not_to be_reviewed
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Setting attention required for reviewer' do RSpec.describe 'Toggle attention requested for reviewer' do
include GraphqlHelpers include GraphqlHelpers
let(:current_user) { create(:user) } let(:current_user) { create(:user) }
...@@ -16,7 +16,7 @@ RSpec.describe 'Setting attention required for reviewer' do ...@@ -16,7 +16,7 @@ RSpec.describe 'Setting attention required for reviewer' do
project_path: project.full_path, project_path: project.full_path,
iid: merge_request.iid.to_s iid: merge_request.iid.to_s
} }
graphql_mutation(:merge_request_attention_required, variables.merge(input), graphql_mutation(:merge_request_toggle_attention_requested, variables.merge(input),
<<-QL.strip_heredoc <<-QL.strip_heredoc
clientMutationId clientMutationId
errors errors
...@@ -25,7 +25,7 @@ RSpec.describe 'Setting attention required for reviewer' do ...@@ -25,7 +25,7 @@ RSpec.describe 'Setting attention required for reviewer' do
end end
def mutation_response def mutation_response
graphql_mutation_response(:merge_request_attention_required) graphql_mutation_response(:merge_request_toggle_attention_requested)
end end
def mutation_errors def mutation_errors
......
...@@ -347,7 +347,7 @@ RSpec.describe 'getting merge request information nested in a project' do ...@@ -347,7 +347,7 @@ RSpec.describe 'getting merge request information nested in a project' do
expect(interaction_data).to contain_exactly a_hash_including( expect(interaction_data).to contain_exactly a_hash_including(
'canMerge' => false, 'canMerge' => false,
'canUpdate' => can_update, 'canUpdate' => can_update,
'reviewState' => attention_required, 'reviewState' => attention_requested,
'reviewed' => false, 'reviewed' => false,
'approved' => false 'approved' => false
) )
...@@ -380,8 +380,8 @@ RSpec.describe 'getting merge request information nested in a project' do ...@@ -380,8 +380,8 @@ RSpec.describe 'getting merge request information nested in a project' do
describe 'scalability' do describe 'scalability' do
let_it_be(:other_users) { create_list(:user, 3) } let_it_be(:other_users) { create_list(:user, 3) }
let(:attention_required) do let(:attention_requested) do
{ 'reviewState' => 'ATTENTION_REQUIRED' } { 'reviewState' => 'ATTENTION_REQUESTED' }
end end
let(:reviewed) do let(:reviewed) do
...@@ -413,9 +413,9 @@ RSpec.describe 'getting merge request information nested in a project' do ...@@ -413,9 +413,9 @@ RSpec.describe 'getting merge request information nested in a project' do
expect { post_graphql(query) }.not_to exceed_query_limit(baseline) expect { post_graphql(query) }.not_to exceed_query_limit(baseline)
expect(interaction_data).to contain_exactly( expect(interaction_data).to contain_exactly(
include(attention_required), include(attention_requested),
include(attention_required), include(attention_requested),
include(attention_required), include(attention_requested),
include(reviewed) include(reviewed)
) )
end end
...@@ -444,7 +444,7 @@ RSpec.describe 'getting merge request information nested in a project' do ...@@ -444,7 +444,7 @@ RSpec.describe 'getting merge request information nested in a project' do
it_behaves_like 'when requesting information about MR interactions' do it_behaves_like 'when requesting information about MR interactions' do
let(:field) { :reviewers } let(:field) { :reviewers }
let(:attention_required) { 'ATTENTION_REQUIRED' } let(:attention_requested) { 'ATTENTION_REQUESTED' }
let(:can_update) { false } let(:can_update) { false }
def assign_user(user) def assign_user(user)
...@@ -454,7 +454,7 @@ RSpec.describe 'getting merge request information nested in a project' do ...@@ -454,7 +454,7 @@ RSpec.describe 'getting merge request information nested in a project' do
it_behaves_like 'when requesting information about MR interactions' do it_behaves_like 'when requesting information about MR interactions' do
let(:field) { :assignees } let(:field) { :assignees }
let(:attention_required) { nil } let(:attention_requested) { nil }
let(:can_update) { true } # assignees can update MRs let(:can_update) { true } # assignees can update MRs
def assign_user(user) def assign_user(user)
......
...@@ -19,7 +19,7 @@ RSpec.describe MergeRequestUserEntity do ...@@ -19,7 +19,7 @@ RSpec.describe MergeRequestUserEntity do
is_expected.to include( is_expected.to include(
:id, :name, :username, :state, :avatar_url, :web_url, :id, :name, :username, :state, :avatar_url, :web_url,
:can_merge, :can_update_merge_request, :reviewed, :approved, :can_merge, :can_update_merge_request, :reviewed, :approved,
:attention_required :attention_requested
) )
end end
...@@ -57,8 +57,8 @@ RSpec.describe MergeRequestUserEntity do ...@@ -57,8 +57,8 @@ RSpec.describe MergeRequestUserEntity do
end end
end end
context 'attention_required' do context 'attention_requested' do
it { is_expected.to include(attention_required: true ) } it { is_expected.to include(attention_requested: true ) }
end end
describe 'performance' do describe 'performance' do
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe MergeRequests::AttentionRequiredService do RSpec.describe MergeRequests::ToggleAttentionRequestedService do
let(:current_user) { create(:user) } let(:current_user) { create(:user) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:assignee_user) { create(:user) } let(:assignee_user) { create(:user) }
...@@ -39,6 +39,10 @@ RSpec.describe MergeRequests::AttentionRequiredService do ...@@ -39,6 +39,10 @@ RSpec.describe MergeRequests::AttentionRequiredService do
end end
context 'reviewer exists' do context 'reviewer exists' do
before do
reviewer.update!(state: :reviewed)
end
it 'returns success' do it 'returns success' do
expect(result[:status]).to eq :success expect(result[:status]).to eq :success
end end
...@@ -47,11 +51,11 @@ RSpec.describe MergeRequests::AttentionRequiredService do ...@@ -47,11 +51,11 @@ RSpec.describe MergeRequests::AttentionRequiredService do
service.execute service.execute
reviewer.reload reviewer.reload
expect(reviewer.state).to eq 'attention_required' expect(reviewer.state).to eq 'attention_requested'
end end
it 'creates a new todo for the reviewer' do it 'creates a new todo for the reviewer' do
expect(todo_service).to receive(:create_attention_required_todo).with(merge_request, current_user, user) expect(todo_service).to receive(:create_attention_requested_todo).with(merge_request, current_user, user)
service.execute service.execute
end end
...@@ -60,6 +64,10 @@ RSpec.describe MergeRequests::AttentionRequiredService do ...@@ -60,6 +64,10 @@ RSpec.describe MergeRequests::AttentionRequiredService do
context 'assignee exists' do context 'assignee exists' do
let(:service) { described_class.new(project: project, current_user: current_user, merge_request: merge_request, user: assignee_user) } let(:service) { described_class.new(project: project, current_user: current_user, merge_request: merge_request, user: assignee_user) }
before do
assignee.update!(state: :reviewed)
end
it 'returns success' do it 'returns success' do
expect(result[:status]).to eq :success expect(result[:status]).to eq :success
end end
...@@ -68,11 +76,11 @@ RSpec.describe MergeRequests::AttentionRequiredService do ...@@ -68,11 +76,11 @@ RSpec.describe MergeRequests::AttentionRequiredService do
service.execute service.execute
assignee.reload assignee.reload
expect(assignee.state).to eq 'attention_required' expect(assignee.state).to eq 'attention_requested'
end end
it 'creates a new todo for the reviewer' do it 'creates a new todo for the reviewer' do
expect(todo_service).to receive(:create_attention_required_todo).with(merge_request, current_user, assignee_user) expect(todo_service).to receive(:create_attention_requested_todo).with(merge_request, current_user, assignee_user)
service.execute service.execute
end end
...@@ -83,13 +91,37 @@ RSpec.describe MergeRequests::AttentionRequiredService do ...@@ -83,13 +91,37 @@ RSpec.describe MergeRequests::AttentionRequiredService do
let(:service) { described_class.new(project: project, current_user: current_user, merge_request: merge_request, user: user) } let(:service) { described_class.new(project: project, current_user: current_user, merge_request: merge_request, user: user) }
let(:assignee) { merge_request.find_assignee(user) } let(:assignee) { merge_request.find_assignee(user) }
before do
reviewer.update!(state: :reviewed)
assignee.update!(state: :reviewed)
end
it 'updates reviewers and assignees state' do it 'updates reviewers and assignees state' do
service.execute service.execute
reviewer.reload reviewer.reload
assignee.reload assignee.reload
expect(reviewer.state).to eq 'attention_required' expect(reviewer.state).to eq 'attention_requested'
expect(assignee.state).to eq 'attention_required' expect(assignee.state).to eq 'attention_requested'
end
end
context 'state is attention_requested' do
before do
reviewer.update!(state: :attention_requested)
end
it 'toggles state to reviewed' do
service.execute
reviewer.reload
expect(reviewer.state).to eq "reviewed"
end
it 'does not create a new todo for the reviewer' do
expect(todo_service).not_to receive(:create_attention_requested_todo).with(merge_request, current_user, assignee_user)
service.execute
end end
end end
end end
......
...@@ -1218,14 +1218,14 @@ RSpec.describe TodoService do ...@@ -1218,14 +1218,14 @@ RSpec.describe TodoService do
end end
end end
describe '#create_attention_required_todo' do describe '#create_attention_requested_todo' do
let(:target) { create(:merge_request, author: author, source_project: project) } let(:target) { create(:merge_request, author: author, source_project: project) }
let(:user) { create(:user) } let(:user) { create(:user) }
it 'creates a todo for user' do it 'creates a todo for user' do
service.create_attention_required_todo(target, author, user) service.create_attention_requested_todo(target, author, user)
should_create_todo(user: user, target: target, action: Todo::ATTENTION_REQUIRED) should_create_todo(user: user, target: target, action: Todo::ATTENTION_REQUESTED)
end end
end end
......
...@@ -10,6 +10,6 @@ RSpec.shared_examples 'having reviewer state' do ...@@ -10,6 +10,6 @@ RSpec.shared_examples 'having reviewer state' do
end end
describe 'mr_attention_requests feature flag is enabled' do describe 'mr_attention_requests feature flag is enabled' do
it { is_expected.to have_attributes(state: 'attention_required') } it { is_expected.to have_attributes(state: 'attention_requested') }
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