Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-01-09 00:10:30 +00:00
parent a0cbad8bf4
commit e1e7e4ae80
23 changed files with 383 additions and 49 deletions

View File

@ -7,6 +7,7 @@ export default {
EditorLite,
},
inheritAttrs: false,
inject: ['projectPath', 'projectNamespace'],
props: {
ciConfigPath: {
type: String,
@ -17,20 +18,15 @@ export default {
required: false,
default: null,
},
projectPath: {
type: String,
required: true,
},
},
methods: {
onEditorReady() {
const editorInstance = this.$refs.editor.getEditor();
const [projectNamespace, projectPath] = this.projectPath.split('/');
editorInstance.use(new CiSchemaExtension());
editorInstance.registerCiSchema({
projectPath,
projectNamespace,
projectPath: this.projectPath,
projectNamespace: this.projectNamespace,
ref: this.commitSha,
});
},

View File

@ -15,12 +15,17 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => {
}
const {
// props
ciConfigPath,
commitSha,
defaultBranch,
newMergeRequestPath,
// `provide/inject` data
lintHelpPagePath,
projectFullPath,
projectPath,
projectNamespace,
ymlHelpPagePath,
} = el?.dataset;
@ -35,6 +40,9 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => {
apolloProvider,
provide: {
lintHelpPagePath,
projectFullPath,
projectPath,
projectNamespace,
ymlHelpPagePath,
},
render(h) {
@ -44,7 +52,6 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => {
commitSha,
defaultBranch,
newMergeRequestPath,
projectPath,
},
});
},

View File

@ -39,11 +39,8 @@ export default {
ValidationSegment,
},
mixins: [glFeatureFlagsMixin()],
inject: ['projectFullPath'],
props: {
projectPath: {
type: String,
required: true,
},
defaultBranch: {
type: String,
required: false,
@ -84,7 +81,7 @@ export default {
query: getBlobContent,
variables() {
return {
projectPath: this.projectPath,
projectPath: this.projectFullPath,
path: this.ciConfigPath,
ref: this.defaultBranch,
};
@ -107,7 +104,7 @@ export default {
},
variables() {
return {
projectPath: this.projectPath,
projectPath: this.projectFullPath,
content: this.contentModel,
};
},
@ -244,7 +241,7 @@ export default {
} = await this.$apollo.mutate({
mutation: commitCiFileMutation,
variables: {
projectPath: this.projectPath,
projectPath: this.projectFullPath,
branch,
startBranch: this.defaultBranch,
message,
@ -318,7 +315,6 @@ export default {
v-model="contentModel"
:ci-config-path="ciConfigPath"
:commit-sha="lastCommitSha"
:project-path="projectPath"
/>
</editor-tab>
<editor-tab

View File

@ -1,7 +1,9 @@
- page_title s_('Pipelines|Pipeline Editor')
#js-pipeline-editor{ data: { "ci-config-path": @project.ci_config_path_or_default,
"project-path" => @project.full_path,
"project-path" => @project.path,
"project-full-path" => @project.full_path,
"project-namespace" => @project.namespace.full_path,
"default-branch" => @project.default_branch,
"commit-sha" => @project.commit ? @project.commit.sha : '',
"new-merge-request-path" => namespace_project_new_merge_request_path,

View File

@ -0,0 +1,8 @@
---
name: usage_data_i_testing_metrics_report_widget_total
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50790
rollout_issue_url:
milestone: '13.8'
type: development
group: group::testing
default_enabled: true

View File

@ -13,7 +13,7 @@ ignorecase: true
swap:
active user: '"billable user"'
active users: '"billable users"'
docs: documentation
docs: '"documentation"'
once that: '"after that"'
once the: '"after the"'
once you: '"after you"'

View File

@ -404,7 +404,7 @@ To make Docker available in the context of the image:
commands are siblings of the runner rather than children of the runner.**
This may have complications and limitations that are unsuitable for your workflow.
Your `config.toml` file should not have an entry like this:
Your `config.toml` file should now have an entry like this:
```toml
[[runners]]

View File

@ -70,6 +70,39 @@ build:
- if: $CI_COMMIT_TAG
```
### Building an image with kaniko behind a proxy
If you use a custom GitLab Runner behind an http(s) proxy, kaniko needs to be set
up accordingly. This means:
- Adding the proxy to `/kaniko/.docker/config.json`
- Passing the `http_proxy` environment variables as build args so the Dockerfile
instructions can use the proxy when building the image.
The previous example can be extended as follows:
```yaml
build:
stage: build
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
script:
- mkdir -p /kaniko/.docker
- |-
KANIKOPROXYBUILDARGS=""
KANIKOCFG="{ \"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}"
if [ "x${http_proxy}" != "x" -o "x${https_proxy}" != "x" ]; then
KANIKOCFG="${KANIKOCFG}, \"proxies\": { \"default\": { \"httpProxy\": \"${http_proxy}\", \"httpsProxy\": \"${https_proxy}\", \"noProxy\": \"${no_proxy}\"}}"
KANIKOPROXYBUILDARGS="--build-arg http_proxy=${http_proxy} --build-arg https_proxy=${https_proxy} --build-arg no_proxy=${no_proxy}"
fi
KANIKOCFG="${KANIKOCFG} }"
echo "${KANIKOCFG}" > /kaniko/.docker/config.json
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile $KANIKOPROXYBUILDARGS --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
only:
- tags
```
## Using a registry with a custom certificate
When trying to push to a Docker registry that uses a certificate that is signed

View File

@ -133,8 +133,8 @@ You might do this if the results of a pipeline (for example, a code build) are r
operation of the pipeline.
[In GitLab 13.7 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/30101),
the variables fields are pre-filled with any global variables defined in the
`.gitlab-ci.yml` file.
all global variables with descriptions defined in the `.gitlab-ci.yml` file are
displayed in the variable fields.
To execute a pipeline manually:

View File

@ -283,6 +283,9 @@ Additional Conan images to use as the basis of your CI file are available in the
Install a Conan package from the Package Registry so you can use it as a
dependency.
WARNING:
Project-level packages [cannot be downloaded currently](https://gitlab.com/gitlab-org/gitlab/-/issues/270129).
Conan packages are often installed as dependencies by using the `conanfile.txt`
file.
@ -384,3 +387,16 @@ The GitLab Conan repository supports the following Conan CLI commands:
packages you have permission to view.
- `conan info`: View the information on a given package from the Package Registry.
- `conan remove`: Delete the package from the Package Registry.
## Troubleshooting Conan packages
### `ERROR: <package> was not found in remote <remote>`
When you attempt to install a Conan package, you might receive a `404` error
like `ERROR: <package> was not found in remote <remote>`.
This issue occurs when you request a download from the project-level Conan API.
The resulting URL is missing is project's `/<id>` and Conan commands, like
`conan install`, fail.
For more information, see [issue 270129](https://gitlab.com/gitlab-org/gitlab/-/issues/270129).

View File

@ -75,6 +75,7 @@ The following table depicts the various user permission levels in a project.
| Manage user-starred metrics dashboards (*7*) | ✓ | ✓ | ✓ | ✓ | ✓ |
| View confidential issues | (*2*) | ✓ | ✓ | ✓ | ✓ |
| Assign issues | | ✓ | ✓ | ✓ | ✓ |
| Assign reviewers | | ✓ | ✓ | ✓ | ✓ |
| Label issues | | ✓ | ✓ | ✓ | ✓ |
| Set issue weight | | ✓ | ✓ | ✓ | ✓ |
| Lock issue threads | | ✓ | ✓ | ✓ | ✓ |

View File

@ -31,6 +31,9 @@ The following quick actions are applicable to descriptions, discussions and thre
| `/assign @user` | ✓ | ✓ | | Assign one user. |
| `/assign @user1 @user2` | ✓ | ✓ | | Assign multiple users. **(STARTER)** |
| `/assign me` | ✓ | ✓ | | Assign yourself. |
| `/assign_reviewer @user` | | ✓ | | Assign one user as a reviewer. |
| `/assign_reviewer @user1 @user2` | | ✓ | | Assign multiple users as reviewers. **(STARTER)** |
| `/assign_reviewer me` | | ✓ | | Assign yourself as a reviewer. |
| `/award :emoji:` | ✓ | ✓ | ✓ | Toggle emoji award. |
| `/child_epic <epic>` | | | ✓ | Add child epic to `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab/-/issues/7330)). **(ULTIMATE)** |
| `/clear_weight` | ✓ | | | Clear weight. **(STARTER)** |
@ -79,7 +82,9 @@ The following quick actions are applicable to descriptions, discussions and thre
| `/title <new title>` | ✓ | ✓ | ✓ | Change title. |
| `/todo` | ✓ | ✓ | ✓ | Add a to-do item. |
| `/unassign @user1 @user2` | ✓ | ✓ | | Remove specific assignees. **(STARTER)** |
| `/unassign` | ✓ | ✓ | | Remove all assignees. |
| `/unassign` | | ✓ | | Remove all assignees. |
| `/unassign_reviewer @user1 @user2` | | ✓ | | Remove specific reviewers. **(STARTER)** |
| `/unassign_reviewer` | | ✓ | | Remove all reviewers. |
| `/unlabel ~label1 ~label2` or `/remove_label ~label1 ~label2` | ✓ | ✓ | ✓ | Remove specified labels. |
| `/unlabel` or `/remove_label` | ✓ | ✓ | ✓ | Remove all labels. |
| `/unlock` | ✓ | ✓ | | Unlock the discussions. |

View File

@ -18,9 +18,7 @@ module BulkImports
configuration: configuration
)
BulkImports::Groups::Pipelines::GroupPipeline.new.run(context)
'BulkImports::EE::Groups::Pipelines::EpicsPipeline'.constantize.new.run(context) if Gitlab.ee?
BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline.new.run(context)
pipelines.each { |pipeline| pipeline.new.run(context) }
entity.finish!
end
@ -28,6 +26,15 @@ module BulkImports
private
attr_reader :entity
def pipelines
[
BulkImports::Groups::Pipelines::GroupPipeline,
BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline
]
end
end
end
end
BulkImports::Importers::GroupImporter.prepend_if_ee('EE::BulkImports::Importers::GroupImporter')

View File

@ -149,6 +149,105 @@ module Gitlab
@execution_message[:approve] = _('Approved the current merge request.')
end
desc do
if quick_action_target.allows_multiple_reviewers?
_('Assign reviewer(s)')
else
_('Assign reviewer')
end
end
explanation do |users|
_('Assigns %{reviewer_users_sentence} as %{reviewer_text}.') % { reviewer_users_sentence: reviewer_users_sentence(users),
reviewer_text: 'reviewer'.pluralize(users.size) }
end
execution_message do |users = nil|
if users.blank?
_("Failed to assign a reviewer because no user was found.")
else
users = [users.first] unless quick_action_target.allows_multiple_reviewers?
_('Assigned %{reviewer_users_sentence} as %{reviewer_text}.') % { reviewer_users_sentence: reviewer_users_sentence(users),
reviewer_text: 'reviewer'.pluralize(users.size) }
end
end
params do
quick_action_target.allows_multiple_reviewers? ? '@user1 @user2' : '@user'
end
types MergeRequest
condition do
Feature.enabled?(:merge_request_reviewers, project, default_enabled: :yaml) &&
current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project)
end
parse_params do |reviewer_param|
extract_users(reviewer_param)
end
command :assign_reviewer do |users|
next if users.empty?
if quick_action_target.allows_multiple_reviewers?
@updates[:reviewer_ids] ||= quick_action_target.reviewers.map(&:id)
@updates[:reviewer_ids] |= users.map(&:id)
else
@updates[:reviewer_ids] = [users.first.id]
end
end
desc do
if quick_action_target.allows_multiple_reviewers?
_('Remove all or specific reviewer(s)')
else
_('Remove reviewer')
end
end
explanation do |users = nil|
reviewers = reviewers_for_removal(users)
_("Removes %{reviewer_text} %{reviewer_references}.") %
{ reviewer_text: 'reviewer'.pluralize(reviewers.size), reviewer_references: reviewers.map(&:to_reference).to_sentence }
end
execution_message do |users = nil|
reviewers = reviewers_for_removal(users)
_("Removed %{reviewer_text} %{reviewer_references}.") %
{ reviewer_text: 'reviewer'.pluralize(reviewers.size), reviewer_references: reviewers.map(&:to_reference).to_sentence }
end
params do
quick_action_target.allows_multiple_reviewers? ? '@user1 @user2' : ''
end
types MergeRequest
condition do
quick_action_target.persisted? &&
Feature.enabled?(:merge_request_reviewers, project, default_enabled: :yaml) &&
quick_action_target.reviewers.any? &&
current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project)
end
parse_params do |unassign_reviewer_param|
# When multiple users are assigned, all will be unassigned if multiple reviewers are no longer allowed
extract_users(unassign_reviewer_param) if quick_action_target.allows_multiple_reviewers?
end
command :unassign_reviewer do |users = nil|
if quick_action_target.allows_multiple_reviewers? && users&.any?
@updates[:reviewer_ids] ||= quick_action_target.reviewers.map(&:id)
@updates[:reviewer_ids] -= users.map(&:id)
else
@updates[:reviewer_ids] = []
end
end
end
def reviewer_users_sentence(users)
if quick_action_target.allows_multiple_reviewers?
users
else
[users.first]
end.map(&:to_reference).to_sentence
end
def reviewers_for_removal(users)
reviewers = quick_action_target.reviewers
if users.present? && quick_action_target.allows_multiple_reviewers?
users
else
reviewers
end
end
def merge_orchestration_service

View File

@ -248,6 +248,11 @@
redis_slot: testing
aggregation: weekly
feature_flag: usage_data_i_testing_test_case_parsed
- name: i_testing_metrics_report_widget_total
category: testing
redis_slot: testing
aggregation: weekly
feature_flag: usage_data_i_testing_metrics_report_widget_total
# Project Management group
- name: g_project_management_issue_title_changed
category: issues_edit

View File

@ -3889,6 +3889,12 @@ msgstr ""
msgid "Assign milestone"
msgstr ""
msgid "Assign reviewer"
msgstr ""
msgid "Assign reviewer(s)"
msgstr ""
msgid "Assign some issues to this milestone."
msgstr ""
@ -3907,6 +3913,9 @@ msgstr ""
msgid "Assigned %{assignee_users_sentence}."
msgstr ""
msgid "Assigned %{reviewer_users_sentence} as %{reviewer_text}."
msgstr ""
msgid "Assigned Issues"
msgstr ""
@ -3954,6 +3963,9 @@ msgstr ""
msgid "Assigns %{assignee_users_sentence}."
msgstr ""
msgid "Assigns %{reviewer_users_sentence} as %{reviewer_text}."
msgstr ""
msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
msgstr ""
@ -11748,6 +11760,9 @@ msgstr ""
msgid "Failed to apply commands."
msgstr ""
msgid "Failed to assign a reviewer because no user was found."
msgstr ""
msgid "Failed to assign a user because no user was found."
msgstr ""
@ -23359,6 +23374,9 @@ msgstr ""
msgid "Remove all or specific label(s)"
msgstr ""
msgid "Remove all or specific reviewer(s)"
msgstr ""
msgid "Remove approver"
msgstr ""
@ -23437,6 +23455,9 @@ msgstr ""
msgid "Remove report"
msgstr ""
msgid "Remove reviewer"
msgstr ""
msgid "Remove secondary node"
msgstr ""
@ -23473,6 +23494,9 @@ msgstr ""
msgid "Removed %{milestone_reference} milestone."
msgstr ""
msgid "Removed %{reviewer_text} %{reviewer_references}."
msgstr ""
msgid "Removed %{type} with id %{id}"
msgstr ""
@ -23518,6 +23542,9 @@ msgstr ""
msgid "Removes %{milestone_reference} milestone."
msgstr ""
msgid "Removes %{reviewer_text} %{reviewer_references}."
msgstr ""
msgid "Removes all labels."
msgstr ""

View File

@ -4,8 +4,7 @@ import {
mockCiYml,
mockCommitSha,
mockProjectPath,
mockNamespace,
mockProjectName,
mockProjectNamespace,
} from '../mock_data';
import TextEditor from '~/pipeline_editor/components/text_editor.vue';
@ -33,10 +32,13 @@ describe('~/pipeline_editor/components/text_editor.vue', () => {
const createComponent = (opts = {}, mountFn = shallowMount) => {
wrapper = mountFn(TextEditor, {
provide: {
projectPath: mockProjectPath,
projectNamespace: mockProjectNamespace,
},
propsData: {
ciConfigPath: mockCiConfigPath,
commitSha: mockCommitSha,
projectPath: mockProjectPath,
},
attrs: {
value: mockCiYml,
@ -77,8 +79,8 @@ describe('~/pipeline_editor/components/text_editor.vue', () => {
expect(mockUse).toHaveBeenCalledTimes(1);
expect(mockRegisterCiSchema).toHaveBeenCalledTimes(1);
expect(mockRegisterCiSchema).toHaveBeenCalledWith({
projectNamespace: mockNamespace,
projectPath: mockProjectName,
projectNamespace: mockProjectNamespace,
projectPath: mockProjectPath,
ref: mockCommitSha,
});
});

View File

@ -5,7 +5,7 @@ import {
mockCiYml,
mockDefaultBranch,
mockLintResponse,
mockProjectPath,
mockProjectFullPath,
} from '../mock_data';
import httpStatus from '~/lib/utils/http_status';
import axios from '~/lib/utils/axios_utils';
@ -32,12 +32,12 @@ describe('~/pipeline_editor/graphql/resolvers', () => {
it('resolves lint data with type names', async () => {
const result = resolvers.Query.blobContent(null, {
projectPath: mockProjectPath,
projectPath: mockProjectFullPath,
path: mockCiConfigPath,
ref: mockDefaultBranch,
});
expect(Api.getRawFile).toHaveBeenCalledWith(mockProjectPath, mockCiConfigPath, {
expect(Api.getRawFile).toHaveBeenCalledWith(mockProjectFullPath, mockCiConfigPath, {
ref: mockDefaultBranch,
});

View File

@ -1,9 +1,9 @@
import { CI_CONFIG_STATUS_VALID } from '~/pipeline_editor/constants';
import { unwrapStagesWithNeeds } from '~/pipelines/components/unwrapping_utils';
export const mockNamespace = 'user1';
export const mockProjectName = 'project1';
export const mockProjectPath = `${mockNamespace}/${mockProjectName}`;
export const mockProjectNamespace = 'user1';
export const mockProjectPath = 'project1';
export const mockProjectFullPath = `${mockProjectNamespace}/${mockProjectPath}`;
export const mockDefaultBranch = 'master';
export const mockNewMergeRequestPath = '/-/merge_requests/new';
export const mockCommitSha = 'aabbccdd';

View File

@ -15,6 +15,8 @@ import {
mockCommitMessage,
mockDefaultBranch,
mockProjectPath,
mockProjectFullPath,
mockProjectNamespace,
mockNewMergeRequestPath,
} from './mock_data';
@ -39,6 +41,15 @@ const MockEditorLite = {
template: '<div/>',
};
const mockProvide = {
projectFullPath: mockProjectFullPath,
projectPath: mockProjectPath,
projectNamespace: mockProjectNamespace,
glFeatures: {
ciConfigVisualizationTab: true,
},
};
describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
let wrapper;
@ -53,11 +64,7 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
lintLoading = false,
options = {},
mountFn = shallowMount,
provide = {
glFeatures: {
ciConfigVisualizationTab: true,
},
},
provide = mockProvide,
} = {}) => {
mockMutate = jest.fn().mockResolvedValue({
data: {
@ -75,7 +82,6 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
ciConfigPath: mockCiConfigPath,
commitSha: mockCommitSha,
defaultBranch: mockDefaultBranch,
projectPath: mockProjectPath,
newMergeRequestPath: mockNewMergeRequestPath,
...props,
},
@ -211,7 +217,12 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
describe('with feature flag off', () => {
beforeEach(() => {
createComponent({ provide: { glFeatures: { ciConfigVisualizationTab: false } } });
createComponent({
provide: {
...mockProvide,
glFeatures: { ciConfigVisualizationTab: false },
},
});
});
it('does not display the tab', () => {
@ -242,7 +253,6 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
it('configures text editor', () => {
expect(findTextEditor().props('commitSha')).toBe(mockCommitSha);
expect(findTextEditor().props('projectPath')).toBe(mockProjectPath);
});
describe('commit form', () => {
@ -251,7 +261,7 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
filePath: mockCiConfigPath,
lastCommitId: mockCommitSha,
message: mockCommitMessage,
projectPath: mockProjectPath,
projectPath: mockProjectFullPath,
startBranch: mockDefaultBranch,
};
@ -435,7 +445,7 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => {
expect(mockCiConfigData).toHaveBeenCalledWith({
content: mockCiYml,
projectPath: mockProjectPath,
projectPath: mockProjectFullPath,
});
});

View File

@ -18,14 +18,14 @@ RSpec.describe BulkImports::Importers::GroupImporter do
subject { described_class.new(bulk_import_entity) }
before do
allow(Gitlab).to receive(:ee?).and_return(false)
allow(BulkImports::Pipeline::Context).to receive(:new).and_return(context)
end
describe '#execute' do
it 'starts the entity and run its pipelines' do
expect(bulk_import_entity).to receive(:start).and_call_original
expect(bulk_import_entity).to receive(:start!).and_call_original
expect_to_run_pipeline BulkImports::Groups::Pipelines::GroupPipeline, context: context
expect_to_run_pipeline('EE::BulkImports::Groups::Pipelines::EpicsPipeline'.constantize, context: context) if Gitlab.ee?
expect_to_run_pipeline BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline, context: context
subject.execute

View File

@ -1262,7 +1262,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
subject { described_class.redis_hll_counters }
let(:categories) { ::Gitlab::UsageDataCounters::HLLRedisCounter.categories }
let(:ineligible_total_categories) { %w[source_code testing ci_secrets_management incident_management_alerts snippets terraform] }
let(:ineligible_total_categories) { %w[source_code ci_secrets_management incident_management_alerts snippets terraform] }
it 'has all known_events' do
expect(subject).to have_key(:redis_hll_counters)

View File

@ -8,6 +8,7 @@ RSpec.describe QuickActions::InterpretService do
let_it_be(:project) { public_project }
let_it_be(:developer) { create(:user) }
let_it_be(:developer2) { create(:user) }
let_it_be(:developer3) { create(:user) }
let_it_be_with_reload(:issue) { create(:issue, project: project) }
let(:milestone) { create(:milestone, project: project, title: '9.10') }
let(:commit) { create(:commit, project: project) }
@ -23,6 +24,7 @@ RSpec.describe QuickActions::InterpretService do
before do
stub_licensed_features(multiple_issue_assignees: false,
multiple_merge_request_reviewers: false,
multiple_merge_request_assignees: false)
end
@ -665,6 +667,20 @@ RSpec.describe QuickActions::InterpretService do
end
end
shared_examples 'assign_reviewer command' do
it 'assigns a reviewer to a single user' do
_, updates, _ = service.execute(content, issuable)
expect(updates).to eq(reviewer_ids: [developer.id])
end
it 'returns the assign reviewer message' do
_, _, message = service.execute(content, issuable)
expect(message).to eq("Assigned #{developer.to_reference} as reviewer.")
end
end
it_behaves_like 'reopen command' do
let(:content) { '/reopen' }
let(:issuable) { issue }
@ -859,6 +875,110 @@ RSpec.describe QuickActions::InterpretService do
let(:issuable) { issue }
end
context 'when the merge_request_reviewers flag is enabled' do
context 'assign_reviewer command with one user' do
it_behaves_like 'assign_reviewer command' do
let(:content) { "/assign_reviewer @#{developer.username}" }
let(:issuable) { merge_request }
end
it_behaves_like 'empty command' do
let(:content) { "/assign_reviewer @#{developer.username}" }
let(:issuable) { issue }
end
end
# CE does not have multiple reviewers
context 'assign_reviewer command with multiple assignees' do
let(:issuable) { merge_request }
it 'assigns only the first reviewer to the merge request' do
content = "/assign_reviewer @#{developer.username} @#{developer2.username}"
_, updates, _ = service.execute(content, issuable)
expect(updates).to eq(reviewer_ids: [developer.id])
end
end
context 'assign_reviewer command with me alias' do
it_behaves_like 'assign_reviewer command' do
let(:content) { '/assign_reviewer me' }
let(:issuable) { merge_request }
end
end
context 'assign_reviewer command with me alias and whitespace' do
it_behaves_like 'assign_reviewer command' do
let(:content) { '/assign_reviewer me ' }
let(:issuable) { merge_request }
end
end
it_behaves_like 'empty command', "Failed to assign a reviewer because no user was found." do
let(:content) { '/assign_reviewer @abcd1234' }
let(:issuable) { merge_request }
end
it_behaves_like 'empty command', "Failed to assign a reviewer because no user was found." do
let(:content) { '/assign_reviewer' }
let(:issuable) { merge_request }
end
describe 'assign_reviewer command' do
let(:content) { "/assign_reviewer @#{developer.username} do it!" }
it 'includes only the user reference' do
_, explanations = service.explain(content, merge_request)
expect(explanations).to eq(["Assigns @#{developer.username} as reviewer."])
end
end
describe 'unassign_reviewer command' do
let(:content) { '/unassign_reviewer' }
let(:merge_request) { create(:merge_request, reviewers: [developer]) }
it 'includes current reviewer reference' do
_, explanations = service.explain(content, merge_request)
expect(explanations).to eq(["Removes reviewer @#{developer.username}."])
end
it 'populates reviewer_ids: [] if content contains /unassign_reviewer' do
_, updates, _ = service.execute(content, merge_request)
expect(updates).to eq(reviewer_ids: [])
end
it 'returns the unassign reviewer message for all the reviewers if content contains /unassign_reviewer' do
merge_request.update!(reviewer_ids: [developer.id, developer2.id])
_, _, message = service.execute(content, merge_request)
expect(message).to eq("Removed reviewers #{developer.to_reference} and #{developer2.to_reference}.")
end
end
end
context 'when the merge_request_reviewers flag is disabled' do
before do
stub_feature_flags(merge_request_reviewers: false)
end
describe 'assign_reviewer command' do
it_behaves_like 'empty command' do
let(:content) { "/assign_reviewer @#{developer.username}" }
let(:issuable) { merge_request }
end
end
describe 'unassign_reviewer command' do
it_behaves_like 'empty command' do
let(:content) { "/unassign_reviewer @#{developer.username}" }
let(:issuable) { merge_request }
end
end
end
context 'unassign command' do
let(:content) { '/unassign' }