Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-05-14 15:10:58 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-05-14 15:10:58 +0300
commit793d974d7c4bd8c9cbd437a9e35087092f4e8bea (patch)
treea88b391ab97bc58f1d1eb665eec7cf64ce072716 /spec
parentc19bb4adbf354562715ba019892f464080eba850 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/features/boards/new_issue_spec.rb93
-rw-r--r--spec/finders/packages/group_or_project_package_finder_spec.rb22
-rw-r--r--spec/finders/packages/maven/package_finder_spec.rb13
-rw-r--r--spec/finders/packages/pypi/package_finder_spec.rb45
-rw-r--r--spec/finders/packages/pypi/packages_finder_spec.rb70
-rw-r--r--spec/frontend/jobs/mock_data.js1
-rw-r--r--spec/frontend/pipelines/graph/mock_data.js2
-rw-r--r--spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_ready_to_merge_spec.js.snap3
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js558
-rw-r--r--spec/graphql/resolvers/design_management/versions_resolver_spec.rb14
-rw-r--r--spec/graphql/types/ci/pipeline_type_spec.rb2
-rw-r--r--spec/graphql/types/design_management/version_type_spec.rb2
-rw-r--r--spec/helpers/webpack_helper_spec.rb36
-rw-r--r--spec/models/ci/job_artifact_spec.rb28
-rw-r--r--spec/models/project_spec.rb26
-rw-r--r--spec/models/wiki_page_spec.rb17
-rw-r--r--spec/policies/project_policy_spec.rb4
-rw-r--r--spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb17
-rw-r--r--spec/requests/api/merge_requests_spec.rb64
-rw-r--r--spec/serializers/job_entity_spec.rb4
-rw-r--r--spec/services/ci/destroy_pipeline_service_spec.rb7
-rw-r--r--spec/services/ci/job_artifacts/destroy_associations_service_spec.rb54
-rw-r--r--spec/services/ci/job_artifacts/destroy_batch_service_spec.rb39
-rw-r--r--spec/services/issues/build_service_spec.rb6
-rw-r--r--spec/services/labels/find_or_create_service_spec.rb29
-rw-r--r--spec/services/merge_requests/update_assignees_service_spec.rb16
-rw-r--r--spec/support/shared_contexts/policies/project_policy_shared_context.rb2
-rw-r--r--spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/policies/project_policy_shared_examples.rb2
-rw-r--r--spec/views/layouts/_head.html.haml_spec.rb6
30 files changed, 706 insertions, 480 deletions
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index 20ae569322c..129d03d17f3 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -10,6 +10,9 @@ RSpec.describe 'Issue Boards new issue', :js do
let_it_be(:list) { create(:list, board: board, label: label, position: 0) }
let_it_be(:user) { create(:user) }
+ let(:board_list_header) { first('[data-testid="board-list-header"]') }
+ let(:project_select_dropdown) { find('[data-testid="project-select-dropdown"]') }
+
context 'authorized user' do
before do
project.add_maintainer(user)
@@ -24,18 +27,18 @@ RSpec.describe 'Issue Boards new issue', :js do
end
it 'displays new issue button' do
- expect(first('.board')).to have_selector('.issue-count-badge-add-button', count: 1)
+ expect(first('.board')).to have_button('New issue', count: 1)
end
it 'does not display new issue button in closed list' do
page.within('.board:nth-child(3)') do
- expect(page).not_to have_selector('.issue-count-badge-add-button')
+ expect(page).not_to have_button('New issue')
end
end
it 'shows form when clicking button' do
page.within(first('.board')) do
- find('.issue-count-badge-add-button').click
+ click_button 'New issue'
expect(page).to have_selector('.board-new-issue-form')
end
@@ -43,7 +46,7 @@ RSpec.describe 'Issue Boards new issue', :js do
it 'hides form when clicking cancel' do
page.within(first('.board')) do
- find('.issue-count-badge-add-button').click
+ click_button 'New issue'
expect(page).to have_selector('.board-new-issue-form')
@@ -55,7 +58,7 @@ RSpec.describe 'Issue Boards new issue', :js do
it 'creates new issue' do
page.within(first('.board')) do
- find('.issue-count-badge-add-button').click
+ click_button 'New issue'
end
page.within(first('.board-new-issue-form')) do
@@ -80,7 +83,7 @@ RSpec.describe 'Issue Boards new issue', :js do
# TODO https://gitlab.com/gitlab-org/gitlab/-/issues/323446
xit 'shows sidebar when creating new issue' do
page.within(first('.board')) do
- find('.issue-count-badge-add-button').click
+ click_button 'New issue'
end
page.within(first('.board-new-issue-form')) do
@@ -95,7 +98,7 @@ RSpec.describe 'Issue Boards new issue', :js do
it 'successfuly loads labels to be added to newly created issue' do
page.within(first('.board')) do
- find('.issue-count-badge-add-button').click
+ click_button 'New issue'
end
page.within(first('.board-new-issue-form')) do
@@ -109,12 +112,12 @@ RSpec.describe 'Issue Boards new issue', :js do
find('.board-card').click
end
- page.within(first('[data-testid="issue-boards-sidebar"]')) do
- find('.labels [data-testid="edit-button"]').click
+ page.within('[data-testid="sidebar-labels"]') do
+ click_button 'Edit'
wait_for_requests
- expect(page).to have_selector('.labels-select-contents-list .dropdown-content li a')
+ expect(page).to have_content 'Label 1'
end
end
end
@@ -126,70 +129,94 @@ RSpec.describe 'Issue Boards new issue', :js do
end
it 'displays new issue button in open list' do
- expect(first('.board')).to have_selector('.issue-count-badge-add-button', count: 1)
+ expect(first('.board')).to have_button('New issue', count: 1)
end
it 'does not display new issue button in label list' do
page.within('.board:nth-child(2)') do
- expect(page).not_to have_selector('.issue-count-badge-add-button')
+ expect(page).not_to have_button('New issue')
end
end
end
context 'group boards' do
let_it_be(:group) { create(:group, :public) }
- let_it_be(:project) { create(:project, :public, namespace: group) }
+ let_it_be(:project) { create(:project, namespace: group, name: "root project") }
+ let_it_be(:subgroup) { create(:group, parent: group) }
+ let_it_be(:subproject1) { create(:project, group: subgroup, name: "sub project1") }
+ let_it_be(:subproject2) { create(:project, group: subgroup, name: "sub project2") }
let_it_be(:group_board) { create(:board, group: group) }
let_it_be(:project_label) { create(:label, project: project, name: 'label') }
let_it_be(:list) { create(:list, board: group_board, label: project_label, position: 0) }
context 'for unauthorized users' do
- context 'when backlog does not exist' do
- before do
- sign_in(user)
- visit group_board_path(group, group_board)
- wait_for_requests
- end
+ before do
+ visit group_board_path(group, group_board)
+ wait_for_requests
+ end
+ context 'when backlog does not exist' do
it 'does not display new issue button in label list' do
page.within('.board.is-draggable') do
- expect(page).not_to have_selector('.issue-count-badge-add-button')
+ expect(page).not_to have_button('New issue')
end
end
end
context 'when backlog list already exists' do
- let!(:backlog_list) { create(:backlog_list, board: group_board) }
-
- before do
- sign_in(user)
- visit group_board_path(group, group_board)
- wait_for_requests
- end
+ let_it_be(:backlog_list) { create(:backlog_list, board: group_board) }
it 'displays new issue button in open list' do
- expect(first('.board')).to have_selector('.issue-count-badge-add-button', count: 1)
+ expect(first('.board')).to have_button('New issue', count: 1)
end
it 'does not display new issue button in label list' do
page.within('.board.is-draggable') do
- expect(page).not_to have_selector('.issue-count-badge-add-button')
+ expect(page).not_to have_button('New issue')
end
end
end
end
context 'for authorized users' do
- it 'display new issue button in label list' do
- project = create(:project, namespace: group)
+ before do
project.add_reporter(user)
+ subproject1.add_reporter(user)
sign_in(user)
visit group_board_path(group, group_board)
wait_for_requests
+ end
+
+ context 'when backlog does not exist' do
+ it 'display new issue button in label list' do
+ expect(board_list_header).to have_button('New issue')
+ end
+ end
+
+ context 'project select dropdown' do
+ let_it_be(:backlog_list) { create(:backlog_list, board: group_board) }
+
+ before do
+ page.within(board_list_header) do
+ click_button 'New issue'
+ end
+
+ project_select_dropdown.click
+
+ wait_for_requests
+ end
+
+ it 'lists a project which is a direct descendant of the top-level group' do
+ expect(project_select_dropdown).to have_button("root project")
+ end
+
+ it 'lists a project that belongs to a subgroup' do
+ expect(project_select_dropdown).to have_button("sub project1")
+ end
- page.within('.board.is-draggable') do
- expect(page).to have_selector('.issue-count-badge-add-button')
+ it "does not list projects to which user doesn't have access" do
+ expect(project_select_dropdown).not_to have_button("sub project2")
end
end
end
diff --git a/spec/finders/packages/group_or_project_package_finder_spec.rb b/spec/finders/packages/group_or_project_package_finder_spec.rb
new file mode 100644
index 00000000000..aaeec8e70d2
--- /dev/null
+++ b/spec/finders/packages/group_or_project_package_finder_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::GroupOrProjectPackageFinder do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+
+ let(:finder) { described_class.new(user, project) }
+
+ describe 'execute' do
+ subject(:run_finder) { finder.execute }
+
+ it { expect { run_finder }.to raise_error(NotImplementedError) }
+ end
+
+ describe 'execute!' do
+ subject(:run_finder) { finder.execute! }
+
+ it { expect { run_finder }.to raise_error(NotImplementedError) }
+ end
+end
diff --git a/spec/finders/packages/maven/package_finder_spec.rb b/spec/finders/packages/maven/package_finder_spec.rb
index d5f521ff895..13c603f1ec4 100644
--- a/spec/finders/packages/maven/package_finder_spec.rb
+++ b/spec/finders/packages/maven/package_finder_spec.rb
@@ -9,10 +9,9 @@ RSpec.describe ::Packages::Maven::PackageFinder do
let_it_be_with_refind(:package) { create(:maven_package, project: project) }
let(:param_path) { nil }
- let(:param_project) { nil }
- let(:param_group) { nil }
+ let(:project_or_group) { nil }
let(:param_order_by_package_file) { false }
- let(:finder) { described_class.new(param_path, user, project: param_project, group: param_group, order_by_package_file: param_order_by_package_file) }
+ let(:finder) { described_class.new(user, project_or_group, path: param_path, order_by_package_file: param_order_by_package_file) }
before do
group.add_developer(user)
@@ -49,13 +48,13 @@ RSpec.describe ::Packages::Maven::PackageFinder do
end
context 'within the project' do
- let(:param_project) { project }
+ let(:project_or_group) { project }
it_behaves_like 'handling valid and invalid paths'
end
context 'within a group' do
- let(:param_group) { group }
+ let(:project_or_group) { group }
it_behaves_like 'handling valid and invalid paths'
end
@@ -77,7 +76,7 @@ RSpec.describe ::Packages::Maven::PackageFinder do
let_it_be(:package2) { create(:maven_package, project: project2, name: package_name, version: nil) }
let_it_be(:package3) { create(:maven_package, project: project3, name: package_name, version: nil) }
- let(:param_group) { group }
+ let(:project_or_group) { group }
let(:param_path) { package_name }
before do
@@ -116,7 +115,7 @@ RSpec.describe ::Packages::Maven::PackageFinder do
it_behaves_like 'Packages::Maven::PackageFinder examples'
it 'uses CTE in the query' do
- sql = described_class.new('some_path', user, group: group).send(:packages_with_path).to_sql
+ sql = described_class.new(user, group, path: package.maven_metadatum.path).send(:packages).to_sql
expect(sql).to include('WITH "maven_metadata_by_path" AS')
end
diff --git a/spec/finders/packages/pypi/package_finder_spec.rb b/spec/finders/packages/pypi/package_finder_spec.rb
new file mode 100644
index 00000000000..7d9eb8a5cd1
--- /dev/null
+++ b/spec/finders/packages/pypi/package_finder_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Pypi::PackageFinder do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:project2) { create(:project, group: group) }
+ let_it_be(:package1) { create(:pypi_package, project: project) }
+ let_it_be(:package2) { create(:pypi_package, project: project) }
+ let_it_be(:package3) { create(:pypi_package, project: project2) }
+
+ let(:package_file) { package2.package_files.first }
+ let(:params) do
+ {
+ filename: package_file.file_name,
+ sha256: package_file.file_sha256
+ }
+ end
+
+ describe 'execute' do
+ subject { described_class.new(user, scope, params).execute }
+
+ context 'within a project' do
+ let(:scope) { project }
+
+ it { is_expected.to eq(package2) }
+ end
+
+ context 'within a group' do
+ let(:scope) { group }
+
+ it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) }
+
+ context 'user with access' do
+ before do
+ project.add_developer(user)
+ end
+
+ it { is_expected.to eq(package2) }
+ end
+ end
+ end
+end
diff --git a/spec/finders/packages/pypi/packages_finder_spec.rb b/spec/finders/packages/pypi/packages_finder_spec.rb
new file mode 100644
index 00000000000..a69c2317261
--- /dev/null
+++ b/spec/finders/packages/pypi/packages_finder_spec.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Pypi::PackagesFinder do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:project2) { create(:project, group: group) }
+ let_it_be(:package1) { create(:pypi_package, project: project) }
+ let_it_be(:package2) { create(:pypi_package, project: project) }
+ let_it_be(:package3) { create(:pypi_package, name: package2.name, project: project) }
+ let_it_be(:package4) { create(:pypi_package, name: package2.name, project: project2) }
+
+ let(:package_name) { package2.name }
+
+ describe 'execute!' do
+ subject { described_class.new(user, scope, package_name: package_name).execute! }
+
+ shared_examples 'when no package is found' do
+ context 'non-existing package' do
+ let(:package_name) { 'none' }
+
+ it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) }
+ end
+ end
+
+ shared_examples 'when package_name param is a non-normalized name' do
+ context 'non-existing package' do
+ let(:package_name) { package2.name.upcase.tr('-', '.') }
+
+ it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) }
+ end
+ end
+
+ context 'within a project' do
+ let(:scope) { project }
+
+ it { is_expected.to contain_exactly(package2, package3) }
+
+ it_behaves_like 'when no package is found'
+ it_behaves_like 'when package_name param is a non-normalized name'
+ end
+
+ context 'within a group' do
+ let(:scope) { group }
+
+ it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) }
+
+ context 'user with access to only one project' do
+ before do
+ project2.add_developer(user)
+ end
+
+ it { is_expected.to contain_exactly(package4) }
+
+ it_behaves_like 'when no package is found'
+ it_behaves_like 'when package_name param is a non-normalized name'
+
+ context ' user with access to multiple projects' do
+ before do
+ project.add_developer(user)
+ end
+
+ it { is_expected.to contain_exactly(package2, package3, package4) }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/frontend/jobs/mock_data.js b/spec/frontend/jobs/mock_data.js
index 7851f633629..57f0b852ff8 100644
--- a/spec/frontend/jobs/mock_data.js
+++ b/spec/frontend/jobs/mock_data.js
@@ -920,6 +920,7 @@ export default {
cancel_path: '/root/ci-mock/-/jobs/4757/cancel',
new_issue_path: '/root/ci-mock/issues/new',
playable: false,
+ complete: true,
created_at: threeWeeksAgo.toISOString(),
updated_at: threeWeeksAgo.toISOString(),
finished_at: threeWeeksAgo.toISOString(),
diff --git a/spec/frontend/pipelines/graph/mock_data.js b/spec/frontend/pipelines/graph/mock_data.js
index 472f2a8b211..28fe3b67e7b 100644
--- a/spec/frontend/pipelines/graph/mock_data.js
+++ b/spec/frontend/pipelines/graph/mock_data.js
@@ -8,6 +8,7 @@ export const mockPipelineResponse = {
__typename: 'Pipeline',
id: 163,
iid: '22',
+ complete: true,
usesNeeds: true,
downstream: null,
upstream: null,
@@ -570,6 +571,7 @@ export const wrappedPipelineReturn = {
__typename: 'Pipeline',
id: 'gid://gitlab/Ci::Pipeline/175',
iid: '38',
+ complete: true,
usesNeeds: true,
downstream: {
__typename: 'PipelineConnection',
diff --git a/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_ready_to_merge_spec.js.snap b/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_ready_to_merge_spec.js.snap
new file mode 100644
index 00000000000..cef1dff3335
--- /dev/null
+++ b/spec/frontend/vue_mr_widget/components/states/__snapshots__/mr_widget_ready_to_merge_spec.js.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ReadyToMerge with a mismatched SHA warns the user to refresh to review 1`] = `"<gl-sprintf-stub message=\\"New changes were added. %{linkStart}Reload the page to review them%{linkEnd}\\"></gl-sprintf-stub>"`;
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
index 7202f327683..85a42946325 100644
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
@@ -1,13 +1,13 @@
-import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { GlSprintf } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
-import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
import simplePoll from '~/lib/utils/simple_poll';
import CommitEdit from '~/vue_merge_request_widget/components/states/commit_edit.vue';
import CommitMessageDropdown from '~/vue_merge_request_widget/components/states/commit_message_dropdown.vue';
import CommitsHeader from '~/vue_merge_request_widget/components/states/commits_header.vue';
import ReadyToMerge from '~/vue_merge_request_widget/components/states/ready_to_merge.vue';
import SquashBeforeMerge from '~/vue_merge_request_widget/components/states/squash_before_merge.vue';
-import { MWPS_MERGE_STRATEGY, MTWPS_MERGE_STRATEGY } from '~/vue_merge_request_widget/constants';
+import { MWPS_MERGE_STRATEGY } from '~/vue_merge_request_widget/constants';
import eventHub from '~/vue_merge_request_widget/event_hub';
jest.mock('~/lib/utils/simple_poll', () =>
@@ -58,11 +58,9 @@ const createTestService = () => ({
poll: jest.fn().mockResolvedValue(),
});
+let wrapper;
const createComponent = (customConfig = {}) => {
- const Component = Vue.extend(ReadyToMerge);
-
- return new Component({
- el: document.createElement('div'),
+ wrapper = shallowMount(ReadyToMerge, {
propsData: {
mr: createTestMr(customConfig),
service: createTestService(),
@@ -71,277 +69,207 @@ const createComponent = (customConfig = {}) => {
};
describe('ReadyToMerge', () => {
- let vm;
-
- beforeEach(() => {
- vm = createComponent();
- });
-
afterEach(() => {
- vm.$destroy();
- });
-
- describe('props', () => {
- it('should have props', () => {
- const { mr, service } = ReadyToMerge.props;
-
- expect(mr.type instanceof Object).toBeTruthy();
- expect(mr.required).toBeTruthy();
-
- expect(service.type instanceof Object).toBeTruthy();
- expect(service.required).toBeTruthy();
- });
- });
-
- describe('data', () => {
- it('should have default data', () => {
- expect(vm.mergeWhenBuildSucceeds).toBeFalsy();
- expect(vm.useCommitMessageWithDescription).toBeFalsy();
- expect(vm.showCommitMessageEditor).toBeFalsy();
- expect(vm.isMakingRequest).toBeFalsy();
- expect(vm.isMergingImmediately).toBeFalsy();
- expect(vm.commitMessage).toBe(vm.mr.commitMessage);
- });
+ wrapper.destroy();
});
describe('computed', () => {
describe('isAutoMergeAvailable', () => {
it('should return true when at least one merge strategy is available', () => {
- vm.mr.availableAutoMergeStrategies = [MWPS_MERGE_STRATEGY];
+ createComponent();
- expect(vm.isAutoMergeAvailable).toBe(true);
+ expect(wrapper.vm.isAutoMergeAvailable).toBe(true);
});
it('should return false when no merge strategies are available', () => {
- vm.mr.availableAutoMergeStrategies = [];
+ createComponent({ mr: { availableAutoMergeStrategies: [] } });
- expect(vm.isAutoMergeAvailable).toBe(false);
+ expect(wrapper.vm.isAutoMergeAvailable).toBe(false);
});
});
describe('status', () => {
it('defaults to success', () => {
- Vue.set(vm.mr, 'pipeline', true);
- Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
+ createComponent({ mr: { pipeline: true, availableAutoMergeStrategies: [] } });
- expect(vm.status).toEqual('success');
+ expect(wrapper.vm.status).toEqual('success');
});
it('returns failed when MR has CI but also has an unknown status', () => {
- Vue.set(vm.mr, 'hasCI', true);
+ createComponent({ mr: { hasCI: true } });
- expect(vm.status).toEqual('failed');
+ expect(wrapper.vm.status).toEqual('failed');
});
it('returns default when MR has no pipeline', () => {
- Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
+ createComponent({ mr: { availableAutoMergeStrategies: [] } });
- expect(vm.status).toEqual('success');
+ expect(wrapper.vm.status).toEqual('success');
});
it('returns pending when pipeline is active', () => {
- Vue.set(vm.mr, 'pipeline', {});
- Vue.set(vm.mr, 'isPipelineActive', true);
+ createComponent({ mr: { pipeline: {}, isPipelineActive: true } });
- expect(vm.status).toEqual('pending');
+ expect(wrapper.vm.status).toEqual('pending');
});
it('returns failed when pipeline is failed', () => {
- Vue.set(vm.mr, 'pipeline', {});
- Vue.set(vm.mr, 'isPipelineFailed', true);
- Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
+ createComponent({
+ mr: { pipeline: {}, isPipelineFailed: true, availableAutoMergeStrategies: [] },
+ });
- expect(vm.status).toEqual('failed');
+ expect(wrapper.vm.status).toEqual('failed');
});
});
describe('mergeButtonVariant', () => {
it('defaults to success class', () => {
- Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
+ createComponent({
+ mr: { availableAutoMergeStrategies: [] },
+ });
- expect(vm.mergeButtonVariant).toEqual('success');
+ expect(wrapper.vm.mergeButtonVariant).toEqual('success');
});
it('returns success class for success status', () => {
- Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
- Vue.set(vm.mr, 'pipeline', true);
+ createComponent({
+ mr: { availableAutoMergeStrategies: [], pipeline: true },
+ });
- expect(vm.mergeButtonVariant).toEqual('success');
+ expect(wrapper.vm.mergeButtonVariant).toEqual('success');
});
it('returns info class for pending status', () => {
- Vue.set(vm.mr, 'availableAutoMergeStrategies', [MTWPS_MERGE_STRATEGY]);
+ createComponent();
- expect(vm.mergeButtonVariant).toEqual('info');
+ expect(wrapper.vm.mergeButtonVariant).toEqual('info');
});
it('returns danger class for failed status', () => {
- vm.mr.hasCI = true;
+ createComponent({ mr: { hasCI: true } });
- expect(vm.mergeButtonVariant).toEqual('danger');
+ expect(wrapper.vm.mergeButtonVariant).toEqual('danger');
});
});
describe('status icon', () => {
it('defaults to tick icon', () => {
- expect(vm.iconClass).toEqual('success');
+ createComponent();
+
+ expect(wrapper.vm.iconClass).toEqual('success');
});
it('shows tick for success status', () => {
- vm.mr.pipeline = true;
+ createComponent({ mr: { pipeline: true } });
- expect(vm.iconClass).toEqual('success');
+ expect(wrapper.vm.iconClass).toEqual('success');
});
it('shows tick for pending status', () => {
- vm.mr.pipeline = {};
- vm.mr.isPipelineActive = true;
+ createComponent({ mr: { pipeline: {}, isPipelineActive: true } });
- expect(vm.iconClass).toEqual('success');
- });
-
- it('shows warning icon for failed status', () => {
- vm.mr.hasCI = true;
-
- expect(vm.iconClass).toEqual('warning');
- });
-
- it('shows warning icon for merge not allowed', () => {
- vm.mr.hasCI = true;
-
- expect(vm.iconClass).toEqual('warning');
+ expect(wrapper.vm.iconClass).toEqual('success');
});
});
describe('mergeButtonText', () => {
it('should return "Merge" when no auto merge strategies are available', () => {
- Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
+ createComponent({ mr: { availableAutoMergeStrategies: [] } });
- expect(vm.mergeButtonText).toEqual('Merge');
+ expect(wrapper.vm.mergeButtonText).toEqual('Merge');
});
- it('should return "Merge in progress"', () => {
- Vue.set(vm, 'isMergingImmediately', true);
+ it('should return "Merge in progress"', async () => {
+ createComponent();
+
+ wrapper.setData({ isMergingImmediately: true });
+
+ await Vue.nextTick();
- expect(vm.mergeButtonText).toEqual('Merge in progress');
+ expect(wrapper.vm.mergeButtonText).toEqual('Merge in progress');
});
it('should return "Merge when pipeline succeeds" when the MWPS auto merge strategy is available', () => {
- Vue.set(vm, 'isMergingImmediately', false);
- Vue.set(vm.mr, 'preferredAutoMergeStrategy', MWPS_MERGE_STRATEGY);
+ createComponent({
+ mr: { isMergingImmediately: false, preferredAutoMergeStrategy: MWPS_MERGE_STRATEGY },
+ });
- expect(vm.mergeButtonText).toEqual('Merge when pipeline succeeds');
+ expect(wrapper.vm.mergeButtonText).toEqual('Merge when pipeline succeeds');
});
});
describe('autoMergeText', () => {
it('should return Merge when pipeline succeeds', () => {
- Vue.set(vm.mr, 'preferredAutoMergeStrategy', MWPS_MERGE_STRATEGY);
+ createComponent({ mr: { preferredAutoMergeStrategy: MWPS_MERGE_STRATEGY } });
- expect(vm.autoMergeText).toEqual('Merge when pipeline succeeds');
+ expect(wrapper.vm.autoMergeText).toEqual('Merge when pipeline succeeds');
});
});
describe('shouldShowMergeImmediatelyDropdown', () => {
it('should return false if no pipeline is active', () => {
- Vue.set(vm.mr, 'isPipelineActive', false);
- Vue.set(vm.mr, 'onlyAllowMergeIfPipelineSucceeds', false);
+ createComponent({
+ mr: { isPipelineActive: false, onlyAllowMergeIfPipelineSucceeds: false },
+ });
- expect(vm.shouldShowMergeImmediatelyDropdown).toBe(false);
+ expect(wrapper.vm.shouldShowMergeImmediatelyDropdown).toBe(false);
});
it('should return false if "Pipelines must succeed" is enabled for the current project', () => {
- Vue.set(vm.mr, 'isPipelineActive', true);
- Vue.set(vm.mr, 'onlyAllowMergeIfPipelineSucceeds', true);
+ createComponent({ mr: { isPipelineActive: true, onlyAllowMergeIfPipelineSucceeds: true } });
- expect(vm.shouldShowMergeImmediatelyDropdown).toBe(false);
- });
-
- it('should return true if the MR\'s pipeline is active and "Pipelines must succeed" is not enabled for the current project', () => {
- Vue.set(vm.mr, 'isPipelineActive', true);
- Vue.set(vm.mr, 'onlyAllowMergeIfPipelineSucceeds', false);
-
- expect(vm.shouldShowMergeImmediatelyDropdown).toBe(true);
+ expect(wrapper.vm.shouldShowMergeImmediatelyDropdown).toBe(false);
});
});
describe('isMergeButtonDisabled', () => {
it('should return false with initial data', () => {
- Vue.set(vm.mr, 'isMergeAllowed', true);
+ createComponent({ mr: { isMergeAllowed: true } });
- expect(vm.isMergeButtonDisabled).toBe(false);
+ expect(wrapper.vm.isMergeButtonDisabled).toBe(false);
});
it('should return true when there is no commit message', () => {
- Vue.set(vm.mr, 'isMergeAllowed', true);
- Vue.set(vm, 'commitMessage', '');
+ createComponent({ mr: { isMergeAllowed: true, commitMessage: '' } });
- expect(vm.isMergeButtonDisabled).toBe(true);
+ expect(wrapper.vm.isMergeButtonDisabled).toBe(true);
});
it('should return true if merge is not allowed', () => {
- Vue.set(vm.mr, 'isMergeAllowed', false);
- Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
- Vue.set(vm.mr, 'onlyAllowMergeIfPipelineSucceeds', true);
+ createComponent({
+ mr: {
+ isMergeAllowed: false,
+ availableAutoMergeStrategies: [],
+ onlyAllowMergeIfPipelineSucceeds: true,
+ },
+ });
- expect(vm.isMergeButtonDisabled).toBe(true);
+ expect(wrapper.vm.isMergeButtonDisabled).toBe(true);
});
- it('should return true when the vm instance is making request', () => {
- Vue.set(vm.mr, 'isMergeAllowed', true);
- Vue.set(vm, 'isMakingRequest', true);
+ it('should return true when the vm instance is making request', async () => {
+ createComponent({ mr: { isMergeAllowed: true } });
- expect(vm.isMergeButtonDisabled).toBe(true);
- });
- });
+ wrapper.setData({ isMakingRequest: true });
- describe('isMergeImmediatelyDangerous', () => {
- it('should always return false in CE', () => {
- expect(vm.isMergeImmediatelyDangerous).toBe(false);
+ await Vue.nextTick();
+
+ expect(wrapper.vm.isMergeButtonDisabled).toBe(true);
});
});
});
describe('methods', () => {
- describe('shouldShowMergeControls', () => {
- it('should return false when an external pipeline is running and required to succeed', () => {
- Vue.set(vm.mr, 'isMergeAllowed', false);
- Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
-
- expect(vm.shouldShowMergeControls).toBe(false);
- });
-
- it('should return true when the build succeeded or build not required to succeed', () => {
- Vue.set(vm.mr, 'isMergeAllowed', true);
- Vue.set(vm.mr, 'availableAutoMergeStrategies', []);
-
- expect(vm.shouldShowMergeControls).toBe(true);
- });
-
- it('should return true when showing the MWPS button and a pipeline is running that needs to be successful', () => {
- Vue.set(vm.mr, 'isMergeAllowed', false);
- Vue.set(vm.mr, 'availableAutoMergeStrategies', [MWPS_MERGE_STRATEGY]);
-
- expect(vm.shouldShowMergeControls).toBe(true);
- });
-
- it('should return true when showing the MWPS button but not required for the pipeline to succeed', () => {
- Vue.set(vm.mr, 'isMergeAllowed', true);
- Vue.set(vm.mr, 'availableAutoMergeStrategies', [MWPS_MERGE_STRATEGY]);
-
- expect(vm.shouldShowMergeControls).toBe(true);
- });
- });
-
describe('updateMergeCommitMessage', () => {
it('should revert flag and change commitMessage', () => {
- expect(vm.commitMessage).toEqual(commitMessage);
- vm.updateMergeCommitMessage(true);
+ createComponent();
+
+ wrapper.vm.updateMergeCommitMessage(true);
- expect(vm.commitMessage).toEqual(commitMessageWithDescription);
- vm.updateMergeCommitMessage(false);
+ expect(wrapper.vm.commitMessage).toEqual(commitMessageWithDescription);
+ wrapper.vm.updateMergeCommitMessage(false);
- expect(vm.commitMessage).toEqual(commitMessage);
+ expect(wrapper.vm.commitMessage).toEqual(commitMessage);
});
});
@@ -356,23 +284,26 @@ describe('ReadyToMerge', () => {
});
it('should handle merge when pipeline succeeds', (done) => {
+ createComponent();
+
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
jest
- .spyOn(vm.service, 'merge')
+ .spyOn(wrapper.vm.service, 'merge')
.mockReturnValue(returnPromise('merge_when_pipeline_succeeds'));
- vm.removeSourceBranch = false;
- vm.handleMergeButtonClick(true);
+ wrapper.setData({ removeSourceBranch: false });
+
+ wrapper.vm.handleMergeButtonClick(true);
setImmediate(() => {
- expect(vm.isMakingRequest).toBeTruthy();
+ expect(wrapper.vm.isMakingRequest).toBeTruthy();
expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
- const params = vm.service.merge.mock.calls[0][0];
+ const params = wrapper.vm.service.merge.mock.calls[0][0];
expect(params).toEqual(
expect.objectContaining({
- sha: vm.mr.sha,
- commit_message: vm.mr.commitMessage,
+ sha: wrapper.vm.mr.sha,
+ commit_message: wrapper.vm.mr.commitMessage,
should_remove_source_branch: false,
auto_merge_strategy: 'merge_when_pipeline_succeeds',
}),
@@ -382,15 +313,17 @@ describe('ReadyToMerge', () => {
});
it('should handle merge failed', (done) => {
+ createComponent();
+
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest.spyOn(vm.service, 'merge').mockReturnValue(returnPromise('failed'));
- vm.handleMergeButtonClick(false, true);
+ jest.spyOn(wrapper.vm.service, 'merge').mockReturnValue(returnPromise('failed'));
+ wrapper.vm.handleMergeButtonClick(false, true);
setImmediate(() => {
- expect(vm.isMakingRequest).toBeTruthy();
+ expect(wrapper.vm.isMakingRequest).toBeTruthy();
expect(eventHub.$emit).toHaveBeenCalledWith('FailedToMerge', undefined);
- const params = vm.service.merge.mock.calls[0][0];
+ const params = wrapper.vm.service.merge.mock.calls[0][0];
expect(params.should_remove_source_branch).toBeTruthy();
expect(params.auto_merge_strategy).toBeUndefined();
@@ -399,15 +332,17 @@ describe('ReadyToMerge', () => {
});
it('should handle merge action accepted case', (done) => {
- jest.spyOn(vm.service, 'merge').mockReturnValue(returnPromise('success'));
- jest.spyOn(vm, 'initiateMergePolling').mockImplementation(() => {});
- vm.handleMergeButtonClick();
+ createComponent();
+
+ jest.spyOn(wrapper.vm.service, 'merge').mockReturnValue(returnPromise('success'));
+ jest.spyOn(wrapper.vm, 'initiateMergePolling').mockImplementation(() => {});
+ wrapper.vm.handleMergeButtonClick();
setImmediate(() => {
- expect(vm.isMakingRequest).toBeTruthy();
- expect(vm.initiateMergePolling).toHaveBeenCalled();
+ expect(wrapper.vm.isMakingRequest).toBeTruthy();
+ expect(wrapper.vm.initiateMergePolling).toHaveBeenCalled();
- const params = vm.service.merge.mock.calls[0][0];
+ const params = wrapper.vm.service.merge.mock.calls[0][0];
expect(params.should_remove_source_branch).toBeTruthy();
expect(params.auto_merge_strategy).toBeUndefined();
@@ -418,128 +353,31 @@ describe('ReadyToMerge', () => {
describe('initiateMergePolling', () => {
it('should call simplePoll', () => {
- vm.initiateMergePolling();
+ createComponent();
+
+ wrapper.vm.initiateMergePolling();
expect(simplePoll).toHaveBeenCalledWith(expect.any(Function), { timeout: 0 });
});
it('should call handleMergePolling', () => {
- jest.spyOn(vm, 'handleMergePolling').mockImplementation(() => {});
-
- vm.initiateMergePolling();
-
- expect(vm.handleMergePolling).toHaveBeenCalled();
- });
- });
-
- describe('handleMergePolling', () => {
- const returnPromise = (state) =>
- new Promise((resolve) => {
- resolve({
- data: {
- state,
- source_branch_exists: true,
- },
- });
- });
-
- beforeEach(() => {
- loadFixtures('merge_requests/merge_request_of_current_user.html');
- });
-
- it('should call start and stop polling when MR merged', (done) => {
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest.spyOn(vm.service, 'poll').mockReturnValue(returnPromise('merged'));
- jest.spyOn(vm, 'initiateRemoveSourceBranchPolling').mockImplementation(() => {});
-
- let cpc = false; // continuePollingCalled
- let spc = false; // stopPollingCalled
-
- vm.handleMergePolling(
- () => {
- cpc = true;
- },
- () => {
- spc = true;
- },
- );
- setImmediate(() => {
- expect(vm.service.poll).toHaveBeenCalled();
- expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
- expect(eventHub.$emit).toHaveBeenCalledWith('FetchActionsContent');
- expect(vm.initiateRemoveSourceBranchPolling).toHaveBeenCalled();
- expect(refreshUserMergeRequestCounts).toHaveBeenCalled();
- expect(cpc).toBeFalsy();
- expect(spc).toBeTruthy();
+ createComponent();
- done();
- });
- });
-
- it('updates status box', (done) => {
- jest.spyOn(vm.service, 'poll').mockReturnValue(returnPromise('merged'));
- jest.spyOn(vm, 'initiateRemoveSourceBranchPolling').mockImplementation(() => {});
-
- vm.handleMergePolling(
- () => {},
- () => {},
- );
-
- setImmediate(() => {
- const statusBox = document.querySelector('.status-box');
-
- expect(statusBox.classList.contains('status-box-mr-merged')).toBeTruthy();
- expect(statusBox.textContent).toContain('Merged');
-
- done();
- });
- });
-
- it('updates merge request count badge', (done) => {
- jest.spyOn(vm.service, 'poll').mockReturnValue(returnPromise('merged'));
- jest.spyOn(vm, 'initiateRemoveSourceBranchPolling').mockImplementation(() => {});
-
- vm.handleMergePolling(
- () => {},
- () => {},
- );
+ jest.spyOn(wrapper.vm, 'handleMergePolling').mockImplementation(() => {});
- setImmediate(() => {
- expect(document.querySelector('.js-merge-counter').textContent).toBe('0');
-
- done();
- });
- });
-
- it('should continue polling until MR is merged', (done) => {
- jest.spyOn(vm.service, 'poll').mockReturnValue(returnPromise('some_other_state'));
- jest.spyOn(vm, 'initiateRemoveSourceBranchPolling').mockImplementation(() => {});
-
- let cpc = false; // continuePollingCalled
- let spc = false; // stopPollingCalled
-
- vm.handleMergePolling(
- () => {
- cpc = true;
- },
- () => {
- spc = true;
- },
- );
- setImmediate(() => {
- expect(cpc).toBeTruthy();
- expect(spc).toBeFalsy();
+ wrapper.vm.initiateMergePolling();
- done();
- });
+ expect(wrapper.vm.handleMergePolling).toHaveBeenCalled();
});
});
describe('initiateRemoveSourceBranchPolling', () => {
it('should emit event and call simplePoll', () => {
+ createComponent();
+
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- vm.initiateRemoveSourceBranchPolling();
+ wrapper.vm.initiateRemoveSourceBranchPolling();
expect(eventHub.$emit).toHaveBeenCalledWith('SetBranchRemoveFlag', [true]);
expect(simplePoll).toHaveBeenCalled();
@@ -557,13 +395,15 @@ describe('ReadyToMerge', () => {
});
it('should call start and stop polling when MR merged', (done) => {
+ createComponent();
+
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- jest.spyOn(vm.service, 'poll').mockReturnValue(returnPromise(false));
+ jest.spyOn(wrapper.vm.service, 'poll').mockReturnValue(returnPromise(false));
let cpc = false; // continuePollingCalled
let spc = false; // stopPollingCalled
- vm.handleRemoveBranchPolling(
+ wrapper.vm.handleRemoveBranchPolling(
() => {
cpc = true;
},
@@ -572,7 +412,7 @@ describe('ReadyToMerge', () => {
},
);
setImmediate(() => {
- expect(vm.service.poll).toHaveBeenCalled();
+ expect(wrapper.vm.service.poll).toHaveBeenCalled();
const args = eventHub.$emit.mock.calls[0];
@@ -590,12 +430,14 @@ describe('ReadyToMerge', () => {
});
it('should continue polling until MR is merged', (done) => {
- jest.spyOn(vm.service, 'poll').mockReturnValue(returnPromise(true));
+ createComponent();
+
+ jest.spyOn(wrapper.vm.service, 'poll').mockReturnValue(returnPromise(true));
let cpc = false; // continuePollingCalled
let spc = false; // stopPollingCalled
- vm.handleRemoveBranchPolling(
+ wrapper.vm.handleRemoveBranchPolling(
() => {
cpc = true;
},
@@ -616,49 +458,26 @@ describe('ReadyToMerge', () => {
describe('Remove source branch checkbox', () => {
describe('when user can merge but cannot delete branch', () => {
it('should be disabled in the rendered output', () => {
- const checkboxElement = vm.$el.querySelector('#remove-source-branch-input');
+ createComponent();
- expect(checkboxElement).toBeNull();
+ expect(wrapper.find('#remove-source-branch-input').exists()).toBe(false);
});
});
describe('when user can merge and can delete branch', () => {
beforeEach(() => {
- vm = createComponent({
+ createComponent({
mr: { canRemoveSourceBranch: true },
});
});
it('isRemoveSourceBranchButtonDisabled should be false', () => {
- expect(vm.isRemoveSourceBranchButtonDisabled).toBe(false);
- });
-
- it('removed source branch should be enabled in rendered output', () => {
- const checkboxElement = vm.$el.querySelector('#remove-source-branch-input');
-
- expect(checkboxElement).not.toBeNull();
+ expect(wrapper.find('#remove-source-branch-input').props('disabled')).toBe(undefined);
});
});
});
describe('render children components', () => {
- let wrapper;
- const localVue = createLocalVue();
-
- const createLocalComponent = (customConfig = {}) => {
- wrapper = shallowMount(localVue.extend(ReadyToMerge), {
- localVue,
- propsData: {
- mr: createTestMr(customConfig),
- service: createTestService(),
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
const findCheckboxElement = () => wrapper.find(SquashBeforeMerge);
const findCommitsHeaderElement = () => wrapper.find(CommitsHeader);
const findCommitEditElements = () => wrapper.findAll(CommitEdit);
@@ -667,7 +486,7 @@ describe('ReadyToMerge', () => {
describe('squash checkbox', () => {
it('should be rendered when squash before merge is enabled and there is more than 1 commit', () => {
- createLocalComponent({
+ createComponent({
mr: { commitsCount: 2, enableSquashBeforeMerge: true },
});
@@ -675,13 +494,13 @@ describe('ReadyToMerge', () => {
});
it('should not be rendered when squash before merge is disabled', () => {
- createLocalComponent({ mr: { commitsCount: 2, enableSquashBeforeMerge: false } });
+ createComponent({ mr: { commitsCount: 2, enableSquashBeforeMerge: false } });
expect(findCheckboxElement().exists()).toBeFalsy();
});
it('should not be rendered when there is only 1 commit', () => {
- createLocalComponent({ mr: { commitsCount: 1, enableSquashBeforeMerge: true } });
+ createComponent({ mr: { commitsCount: 1, enableSquashBeforeMerge: true } });
expect(findCheckboxElement().exists()).toBeFalsy();
});
@@ -695,7 +514,7 @@ describe('ReadyToMerge', () => {
`(
'is $state when squashIsReadonly returns $expectation ',
({ squashState, prop, expectation }) => {
- createLocalComponent({
+ createComponent({
mr: { commitsCount: 2, enableSquashBeforeMerge: true, [squashState]: expectation },
});
@@ -704,7 +523,7 @@ describe('ReadyToMerge', () => {
);
it('is not rendered for "Do not allow" option', () => {
- createLocalComponent({
+ createComponent({
mr: {
commitsCount: 2,
enableSquashBeforeMerge: true,
@@ -720,14 +539,14 @@ describe('ReadyToMerge', () => {
describe('commits count collapsible header', () => {
it('should be rendered when fast-forward is disabled', () => {
- createLocalComponent();
+ createComponent();
expect(findCommitsHeaderElement().exists()).toBeTruthy();
});
describe('when fast-forward is enabled', () => {
it('should be rendered if squash and squash before are enabled and there is more than 1 commit', () => {
- createLocalComponent({
+ createComponent({
mr: {
ffOnlyEnabled: true,
enableSquashBeforeMerge: true,
@@ -740,7 +559,7 @@ describe('ReadyToMerge', () => {
});
it('should not be rendered if squash before merge is disabled', () => {
- createLocalComponent({
+ createComponent({
mr: {
ffOnlyEnabled: true,
enableSquashBeforeMerge: false,
@@ -753,7 +572,7 @@ describe('ReadyToMerge', () => {
});
it('should not be rendered if squash is disabled', () => {
- createLocalComponent({
+ createComponent({
mr: {
ffOnlyEnabled: true,
squash: false,
@@ -766,7 +585,7 @@ describe('ReadyToMerge', () => {
});
it('should not be rendered if commits count is 1', () => {
- createLocalComponent({
+ createComponent({
mr: {
ffOnlyEnabled: true,
squash: true,
@@ -783,7 +602,7 @@ describe('ReadyToMerge', () => {
describe('commits edit components', () => {
describe('when fast-forward merge is enabled', () => {
it('should not be rendered if squash is disabled', () => {
- createLocalComponent({
+ createComponent({
mr: {
ffOnlyEnabled: true,
squash: false,
@@ -796,7 +615,7 @@ describe('ReadyToMerge', () => {
});
it('should not be rendered if squash before merge is disabled', () => {
- createLocalComponent({
+ createComponent({
mr: {
ffOnlyEnabled: true,
squash: true,
@@ -809,7 +628,7 @@ describe('ReadyToMerge', () => {
});
it('should not be rendered if there is only one commit', () => {
- createLocalComponent({
+ createComponent({
mr: {
ffOnlyEnabled: true,
squash: true,
@@ -822,7 +641,7 @@ describe('ReadyToMerge', () => {
});
it('should have one edit component if squash is enabled and there is more than 1 commit', () => {
- createLocalComponent({
+ createComponent({
mr: {
ffOnlyEnabled: true,
squashIsSelected: true,
@@ -837,13 +656,13 @@ describe('ReadyToMerge', () => {
});
it('should have one edit component when squash is disabled', () => {
- createLocalComponent();
+ createComponent();
expect(findCommitEditElements().length).toBe(1);
});
it('should have two edit components when squash is enabled and there is more than 1 commit', () => {
- createLocalComponent({
+ createComponent({
mr: {
commitsCount: 2,
squashIsSelected: true,
@@ -855,7 +674,7 @@ describe('ReadyToMerge', () => {
});
it('should have one edit components when squash is enabled and there is 1 commit only', () => {
- createLocalComponent({
+ createComponent({
mr: {
commitsCount: 1,
squash: true,
@@ -867,13 +686,13 @@ describe('ReadyToMerge', () => {
});
it('should have correct edit merge commit label', () => {
- createLocalComponent();
+ createComponent();
expect(findFirstCommitEditLabel()).toBe('Merge commit message');
});
it('should have correct edit squash commit label', () => {
- createLocalComponent({
+ createComponent({
mr: {
commitsCount: 2,
squashIsSelected: true,
@@ -887,13 +706,13 @@ describe('ReadyToMerge', () => {
describe('commits dropdown', () => {
it('should not be rendered if squash is disabled', () => {
- createLocalComponent();
+ createComponent();
expect(findCommitDropdownElement().exists()).toBeFalsy();
});
it('should be rendered if squash is enabled and there is more than 1 commit', () => {
- createLocalComponent({
+ createComponent({
mr: { enableSquashBeforeMerge: true, squashIsSelected: true, commitsCount: 2 },
});
@@ -902,83 +721,38 @@ describe('ReadyToMerge', () => {
});
});
- describe('Merge controls', () => {
- describe('when allowed to merge', () => {
- beforeEach(() => {
- vm = createComponent({
- mr: { isMergeAllowed: true, canRemoveSourceBranch: true },
- });
- });
-
- it('shows remove source branch checkbox', () => {
- expect(vm.$el.querySelector('.js-remove-source-branch-checkbox')).not.toBeNull();
- });
-
- it('shows modify commit message button', () => {
- expect(vm.$el.querySelector('.js-modify-commit-message-button')).toBeDefined();
- });
-
- it('does not show message about needing to resolve items', () => {
- expect(vm.$el.querySelector('.js-resolve-mr-widget-items-message')).toBeNull();
- });
- });
-
- describe('when not allowed to merge', () => {
- beforeEach(() => {
- vm = createComponent({
- mr: { isMergeAllowed: false },
- });
- });
-
- it('does not show remove source branch checkbox', () => {
- expect(vm.$el.querySelector('.js-remove-source-branch-checkbox')).toBeNull();
- });
-
- it('shows message to resolve all items before being allowed to merge', () => {
- expect(vm.$el.querySelector('.js-resolve-mr-widget-items-message')).toBeDefined();
- });
- });
- });
-
describe('Merge request project settings', () => {
describe('when the merge commit merge method is enabled', () => {
beforeEach(() => {
- vm = createComponent({
+ createComponent({
mr: { ffOnlyEnabled: false },
});
});
it('should not show fast forward message', () => {
- expect(vm.$el.querySelector('.mr-fast-forward-message')).toBeNull();
- });
-
- it('should show "Modify commit message" button', () => {
- expect(vm.$el.querySelector('.js-modify-commit-message-button')).toBeDefined();
+ expect(wrapper.find('.mr-fast-forward-message').exists()).toBe(false);
});
});
describe('when the fast-forward merge method is enabled', () => {
beforeEach(() => {
- vm = createComponent({
+ createComponent({
mr: { ffOnlyEnabled: true },
});
});
it('should show fast forward message', () => {
- expect(vm.$el.querySelector('.mr-fast-forward-message')).toBeDefined();
- });
-
- it('should not show "Modify commit message" button', () => {
- expect(vm.$el.querySelector('.js-modify-commit-message-button')).toBeNull();
+ expect(wrapper.find('.mr-fast-forward-message').exists()).toBe(true);
});
});
});
describe('with a mismatched SHA', () => {
- const findMismatchShaBlock = () => vm.$el.querySelector('.js-sha-mismatch');
+ const findMismatchShaBlock = () => wrapper.find('.js-sha-mismatch');
+ const findMismatchShaTextBlock = () => findMismatchShaBlock().find(GlSprintf);
beforeEach(() => {
- vm = createComponent({
+ createComponent({
mr: {
isSHAMismatch: true,
mergeRequestDiffsPath: '/merge_requests/1/diffs',
@@ -987,17 +761,11 @@ describe('ReadyToMerge', () => {
});
it('displays a warning message', () => {
- expect(findMismatchShaBlock()).toExist();
+ expect(findMismatchShaBlock().exists()).toBe(true);
});
it('warns the user to refresh to review', () => {
- expect(findMismatchShaBlock().textContent.trim()).toBe(
- 'New changes were added. Reload the page to review them',
- );
- });
-
- it('displays link to the diffs tab', () => {
- expect(findMismatchShaBlock().querySelector('a').href).toContain(vm.mr.mergeRequestDiffsPath);
+ expect(findMismatchShaTextBlock().element.outerHTML).toMatchSnapshot();
});
});
});
diff --git a/spec/graphql/resolvers/design_management/versions_resolver_spec.rb b/spec/graphql/resolvers/design_management/versions_resolver_spec.rb
index 23d4d86c79a..2c9c3a47650 100644
--- a/spec/graphql/resolvers/design_management/versions_resolver_spec.rb
+++ b/spec/graphql/resolvers/design_management/versions_resolver_spec.rb
@@ -41,6 +41,20 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do
it 'returns the ordered versions' do
expect(result.to_a).to eq(all_versions)
end
+
+ context 'loading associations' do
+ it 'prevents N+1 queries when loading author' do
+ control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ resolve_versions(object).items.map(&:author)
+ end.count
+
+ create_list(:design_version, 3, issue: issue)
+
+ expect do
+ resolve_versions(object).items.map(&:author)
+ end.not_to exceed_all_query_limit(control_count)
+ end
+ end
end
context 'when constrained' do
diff --git a/spec/graphql/types/ci/pipeline_type_spec.rb b/spec/graphql/types/ci/pipeline_type_spec.rb
index c67e86a7ee1..35d48229fa4 100644
--- a/spec/graphql/types/ci/pipeline_type_spec.rb
+++ b/spec/graphql/types/ci/pipeline_type_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Types::Ci::PipelineType do
it 'contains attributes related to a pipeline' do
expected_fields = %w[
- id iid sha before_sha status detailed_status config_source
+ id iid sha before_sha complete status detailed_status config_source
duration queued_duration
coverage created_at updated_at started_at finished_at committed_at
stages user retryable cancelable jobs source_job job downstream
diff --git a/spec/graphql/types/design_management/version_type_spec.rb b/spec/graphql/types/design_management/version_type_spec.rb
index 017cc1775a1..62335a65fdf 100644
--- a/spec/graphql/types/design_management/version_type_spec.rb
+++ b/spec/graphql/types/design_management/version_type_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe GitlabSchema.types['DesignVersion'] do
it { expect(described_class).to require_graphql_authorizations(:read_design) }
it 'has the expected fields' do
- expected_fields = %i[id sha designs design_at_version designs_at_version]
+ expected_fields = %i[id sha designs design_at_version designs_at_version author created_at]
expect(described_class).to have_graphql_fields(*expected_fields)
end
diff --git a/spec/helpers/webpack_helper_spec.rb b/spec/helpers/webpack_helper_spec.rb
new file mode 100644
index 00000000000..f9386c99dc3
--- /dev/null
+++ b/spec/helpers/webpack_helper_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe WebpackHelper do
+ let(:source) { 'foo.js' }
+ let(:asset_path) { "/assets/webpack/#{source}" }
+
+ describe '#prefetch_link_tag' do
+ it 'returns prefetch link tag' do
+ expect(helper.prefetch_link_tag(source)).to eq("<link rel=\"prefetch\" href=\"/#{source}\">")
+ end
+ end
+
+ describe '#webpack_preload_asset_tag' do
+ before do
+ allow(Gitlab::Webpack::Manifest).to receive(:asset_paths).and_return([asset_path])
+ end
+
+ it 'preloads the resource by default' do
+ expect(helper).to receive(:preload_link_tag).with(asset_path, {}).and_call_original
+
+ output = helper.webpack_preload_asset_tag(source)
+
+ expect(output).to eq("<link rel=\"preload\" href=\"#{asset_path}\" as=\"script\" type=\"text/javascript\">")
+ end
+
+ it 'prefetches the resource if explicitly asked' do
+ expect(helper).to receive(:prefetch_link_tag).with(asset_path).and_call_original
+
+ output = helper.webpack_preload_asset_tag(source, prefetch: true)
+
+ expect(output).to eq("<link rel=\"prefetch\" href=\"#{asset_path}\">")
+ end
+ end
+end
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index cdb123573f1..3c4769764d5 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -602,6 +602,34 @@ RSpec.describe Ci::JobArtifact do
end
end
+ context 'FastDestroyAll' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:job) { create(:ci_build, pipeline: pipeline, project: project) }
+
+ let!(:job_artifact) { create(:ci_job_artifact, :archive, job: job) }
+ let(:subjects) { pipeline.job_artifacts }
+
+ describe '.use_fast_destroy' do
+ it 'performs cascading delete with fast_destroy_all' do
+ expect(Ci::DeletedObject.count).to eq(0)
+ expect(subjects.count).to be > 0
+
+ expect { pipeline.destroy! }.not_to raise_error
+
+ expect(subjects.count).to eq(0)
+ expect(Ci::DeletedObject.count).to be > 0
+ end
+
+ it 'updates project statistics' do
+ expect(ProjectStatistics).to receive(:increment_statistic).once
+ .with(project, :build_artifacts_size, -job_artifact.file.size)
+
+ pipeline.destroy!
+ end
+ end
+ end
+
def file_type_limit_failure_message(type, limit_name)
<<~MSG
The artifact type `#{type}` is missing its counterpart plan limit which is expected to be named `#{limit_name}`.
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 7bb344a4ba3..e5ed8d89145 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -6936,6 +6936,32 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#increment_statistic_value' do
+ let(:project) { build_stubbed(:project) }
+
+ subject(:increment) do
+ project.increment_statistic_value(:build_artifacts_size, -10)
+ end
+
+ it 'increments the value' do
+ expect(ProjectStatistics)
+ .to receive(:increment_statistic)
+ .with(project, :build_artifacts_size, -10)
+
+ increment
+ end
+
+ context 'when the project is scheduled for removal' do
+ let(:project) { build_stubbed(:project, pending_delete: true) }
+
+ it 'does not increment the value' do
+ expect(ProjectStatistics).not_to receive(:increment_statistic)
+
+ increment
+ end
+ end
+ end
+
def finish_job(export_job)
export_job.start
export_job.finish
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index 44014f93444..579a9e664cf 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -620,16 +620,12 @@ RSpec.describe WikiPage do
end
describe "#versions" do
- include_context 'subject is persisted page'
+ let(:subject) { create_wiki_page }
it "returns an array of all commits for the page" do
- 3.times { |i| subject.update(content: "content #{i}") }
-
- expect(subject.versions.count).to eq(4)
- end
-
- it 'returns instances of WikiPageVersion' do
- expect(subject.versions).to all( be_a(Gitlab::Git::WikiPageVersion) )
+ expect do
+ 3.times { |i| subject.update(content: "content #{i}") }
+ end.to change { subject.versions.count }.by(3)
end
end
@@ -777,8 +773,11 @@ RSpec.describe WikiPage do
end
describe '#historical?' do
- include_context 'subject is persisted page'
+ let!(:container) { create(:project) }
+
+ subject { create_wiki_page }
+ let(:wiki) { subject.wiki }
let(:old_version) { subject.versions.last.id }
let(:old_page) { wiki.find_page(subject.title, old_version) }
let(:latest_version) { subject.versions.first.id }
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 540647fb699..cc837e7544c 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -60,7 +60,7 @@ RSpec.describe ProjectPolicy do
end
it 'does not include the issues permissions' do
- expect_disallowed :read_issue, :read_issue_iid, :create_issue, :update_issue, :admin_issue
+ expect_disallowed :read_issue, :read_issue_iid, :create_issue, :update_issue, :admin_issue, :create_incident
end
it 'disables boards and lists permissions' do
@@ -72,7 +72,7 @@ RSpec.describe ProjectPolicy do
it 'does not include the issues permissions' do
create(:jira_service, project: project)
- expect_disallowed :read_issue, :read_issue_iid, :create_issue, :update_issue, :admin_issue
+ expect_disallowed :read_issue, :read_issue_iid, :create_issue, :update_issue, :admin_issue, :create_incident
end
end
end
diff --git a/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb b/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb
index ee0085718b3..9d98498ca8a 100644
--- a/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb
+++ b/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb
@@ -33,6 +33,7 @@ RSpec.describe 'Getting versions related to an issue' do
let(:version_params) { nil }
let(:version_query_fields) { ['edges { node { sha } }'] }
+ let(:edges_path) { %w[project issue designCollection versions edges] }
let(:project) { issue.project }
let(:current_user) { owner }
@@ -50,8 +51,7 @@ RSpec.describe 'Getting versions related to an issue' do
end
def response_values(data = graphql_data, key = 'sha')
- path = %w[project issue designCollection versions edges]
- data.dig(*path).map { |e| e.dig('node', key) }
+ data.dig(*edges_path).map { |e| e.dig('node', key) }
end
before do
@@ -64,6 +64,19 @@ RSpec.describe 'Getting versions related to an issue' do
expect(response_values).to match_array([version_a, version_b, version_c, version_d].map(&:sha))
end
+ context 'with all fields requested' do
+ let(:version_query_fields) do
+ ['edges { node { id sha createdAt author { id } } }']
+ end
+
+ it 'returns correct data' do
+ post_graphql(query, current_user: current_user)
+
+ keys = graphql_data.dig(*edges_path).first['node'].keys
+ expect(keys).to match_array(%w(id sha createdAt author))
+ end
+ end
+
describe 'filter by sha' do
let(:sha) { version_b.sha }
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index e4eac9ee174..a13db1bb414 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -2154,7 +2154,7 @@ RSpec.describe API::MergeRequests do
end
end
- describe 'PUT /projects/:id/merge_reuests/:merge_request_iid' do
+ describe 'PUT /projects/:id/merge_requests/:merge_request_iid' do
it_behaves_like 'issuable update endpoint' do
let(:entity) { merge_request }
end
@@ -2176,6 +2176,68 @@ RSpec.describe API::MergeRequests do
end
end
+ context 'when assignee_id=user2.id' do
+ let(:params) do
+ {
+ assignee_id: user2.id
+ }
+ end
+
+ it 'sets the assignees' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['assignees']).to contain_exactly(
+ a_hash_including('name' => user2.name)
+ )
+ end
+ end
+
+ context 'when only assignee_ids are provided, and the list is empty' do
+ let(:params) do
+ {
+ assignee_ids: []
+ }
+ end
+
+ it 'clears the assignees' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['assignees']).to be_empty
+ end
+ end
+
+ context 'when only assignee_ids are provided, and the list contains the sentinel value' do
+ let(:params) do
+ {
+ assignee_ids: [0]
+ }
+ end
+
+ it 'clears the assignees' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['assignees']).to be_empty
+ end
+ end
+
+ context 'when only assignee_id=0' do
+ let(:params) do
+ {
+ assignee_id: 0
+ }
+ end
+
+ it 'clears the assignees' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['assignees']).to be_empty
+ end
+ end
+
context 'accepts reviewer_ids' do
let(:params) do
{
diff --git a/spec/serializers/job_entity_spec.rb b/spec/serializers/job_entity_spec.rb
index 1cbf1914c0c..f31cfcb8499 100644
--- a/spec/serializers/job_entity_spec.rb
+++ b/spec/serializers/job_entity_spec.rb
@@ -21,6 +21,10 @@ RSpec.describe JobEntity do
subject { entity.as_json }
+ it 'contains complete to indicate if a pipeline is completed' do
+ expect(subject).to include(:complete)
+ end
+
it 'contains paths to job page action' do
expect(subject).to include(:build_path)
end
diff --git a/spec/services/ci/destroy_pipeline_service_spec.rb b/spec/services/ci/destroy_pipeline_service_spec.rb
index f226a129fac..302233cea5a 100644
--- a/spec/services/ci/destroy_pipeline_service_spec.rb
+++ b/spec/services/ci/destroy_pipeline_service_spec.rb
@@ -3,7 +3,8 @@
require 'spec_helper'
RSpec.describe ::Ci::DestroyPipelineService do
- let(:project) { create(:project, :repository) }
+ let_it_be(:project) { create(:project, :repository) }
+
let!(:pipeline) { create(:ci_pipeline, :success, project: project, sha: project.commit.id) }
subject { described_class.new(project, user).execute(pipeline) }
@@ -60,6 +61,10 @@ RSpec.describe ::Ci::DestroyPipelineService do
expect { artifact.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
+
+ it 'inserts deleted objects for object storage files' do
+ expect { subject }.to change { Ci::DeletedObject.count }
+ end
end
end
end
diff --git a/spec/services/ci/job_artifacts/destroy_associations_service_spec.rb b/spec/services/ci/job_artifacts/destroy_associations_service_spec.rb
new file mode 100644
index 00000000000..b1a4741851b
--- /dev/null
+++ b/spec/services/ci/job_artifacts/destroy_associations_service_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::JobArtifacts::DestroyAssociationsService do
+ let(:artifacts) { Ci::JobArtifact.all }
+ let(:service) { described_class.new(artifacts) }
+
+ let_it_be(:artifact, refind: true) do
+ create(:ci_job_artifact)
+ end
+
+ before do
+ artifact.file = fixture_file_upload(Rails.root.join('spec/fixtures/ci_build_artifacts.zip'), 'application/zip')
+ artifact.save!
+ end
+
+ describe '#destroy_records' do
+ it 'removes artifacts without updating statistics' do
+ expect(ProjectStatistics).not_to receive(:increment_statistic)
+
+ expect { service.destroy_records }.to change { Ci::JobArtifact.count }
+ end
+
+ context 'when there are no artifacts' do
+ let(:artifacts) { Ci::JobArtifact.none }
+
+ it 'does not raise error' do
+ expect { service.destroy_records }.not_to raise_error
+ end
+ end
+ end
+
+ describe '#update_statistics' do
+ before do
+ service.destroy_records
+ end
+
+ it 'updates project statistics' do
+ expect(ProjectStatistics).to receive(:increment_statistic).once
+ .with(artifact.project, :build_artifacts_size, -artifact.file.size)
+
+ service.update_statistics
+ end
+
+ context 'when there are no artifacts' do
+ let(:artifacts) { Ci::JobArtifact.none }
+
+ it 'does not raise error' do
+ expect { service.update_statistics }.not_to raise_error
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb b/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb
index 52aaf73d67e..2cedbf93d74 100644
--- a/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb
+++ b/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb
@@ -3,8 +3,6 @@
require 'spec_helper'
RSpec.describe Ci::JobArtifacts::DestroyBatchService do
- include ExclusiveLeaseHelpers
-
let(:artifacts) { Ci::JobArtifact.all }
let(:service) { described_class.new(artifacts, pick_up_at: Time.current) }
@@ -25,14 +23,6 @@ RSpec.describe Ci::JobArtifacts::DestroyBatchService do
expect { subject }.to change { Ci::DeletedObject.count }.by(1)
end
- it 'resets project statistics' do
- expect(ProjectStatistics).to receive(:increment_statistic).once
- .with(artifact.project, :build_artifacts_size, -artifact.file.size)
- .and_call_original
-
- execute
- end
-
it 'does not remove the files' do
expect { execute }.not_to change { artifact.file.exists? }
end
@@ -44,6 +34,29 @@ RSpec.describe Ci::JobArtifacts::DestroyBatchService do
execute
end
+
+ context 'ProjectStatistics' do
+ it 'resets project statistics' do
+ expect(ProjectStatistics).to receive(:increment_statistic).once
+ .with(artifact.project, :build_artifacts_size, -artifact.file.size)
+ .and_call_original
+
+ execute
+ end
+
+ context 'with update_stats: false' do
+ it 'does not update project statistics' do
+ expect(ProjectStatistics).not_to receive(:increment_statistic)
+
+ service.execute(update_stats: false)
+ end
+
+ it 'returns size statistics' do
+ expect(service.execute(update_stats: false)).to match(
+ a_hash_including(statistics_updates: { artifact.project => -artifact.file.size }))
+ end
+ end
+ end
end
context 'when failed to destroy artifact' do
@@ -65,16 +78,12 @@ RSpec.describe Ci::JobArtifacts::DestroyBatchService do
context 'when there are no artifacts' do
let(:artifacts) { Ci::JobArtifact.none }
- before do
- artifact.destroy!
- end
-
it 'does not raise error' do
expect { execute }.not_to raise_error
end
it 'reports the number of destroyed artifacts' do
- is_expected.to eq(destroyed_artifacts_count: 0, status: :success)
+ is_expected.to eq(destroyed_artifacts_count: 0, statistics_updates: {}, status: :success)
end
end
end
diff --git a/spec/services/issues/build_service_spec.rb b/spec/services/issues/build_service_spec.rb
index d0f228fb3d9..3f506ec58b0 100644
--- a/spec/services/issues/build_service_spec.rb
+++ b/spec/services/issues/build_service_spec.rb
@@ -184,9 +184,9 @@ RSpec.describe Issues::BuildService do
end
it 'cannot set invalid type' do
- expect do
- build_issue(issue_type: 'invalid type')
- end.to raise_error(ArgumentError, "'invalid type' is not a valid issue_type")
+ issue = build_issue(issue_type: 'invalid type')
+
+ expect(issue).to be_issue
end
end
end
diff --git a/spec/services/labels/find_or_create_service_spec.rb b/spec/services/labels/find_or_create_service_spec.rb
index aa9eb0e6a0d..3ea2727dc60 100644
--- a/spec/services/labels/find_or_create_service_spec.rb
+++ b/spec/services/labels/find_or_create_service_spec.rb
@@ -25,6 +25,35 @@ RSpec.describe Labels::FindOrCreateService do
project.add_developer(user)
end
+ context 'when existing_labels_by_title is provided' do
+ let(:preloaded_label) { build(:label, title: 'Security') }
+
+ before do
+ params.merge!(
+ existing_labels_by_title: {
+ 'Security' => preloaded_label
+ })
+ end
+
+ context 'when label exists' do
+ it 'returns preloaded label' do
+ expect(service.execute).to eq preloaded_label
+ end
+ end
+
+ context 'when label does not exists' do
+ before do
+ params[:title] = 'Audit'
+ end
+
+ it 'does not generates additional label search' do
+ service.execute
+
+ expect(LabelsFinder).not_to receive(:new)
+ end
+ end
+ end
+
context 'when label does not exist at group level' do
it 'creates a new label at project level' do
expect { service.execute }.to change(project.labels, :count).by(1)
diff --git a/spec/services/merge_requests/update_assignees_service_spec.rb b/spec/services/merge_requests/update_assignees_service_spec.rb
index 113bfb0f31a..076161c9029 100644
--- a/spec/services/merge_requests/update_assignees_service_spec.rb
+++ b/spec/services/merge_requests/update_assignees_service_spec.rb
@@ -36,6 +36,22 @@ RSpec.describe MergeRequests::UpdateAssigneesService do
end
context 'when the parameters are valid' do
+ context 'when using sentinel values' do
+ let(:opts) { { assignee_ids: [0] } }
+
+ it 'removes all assignees' do
+ expect { update_merge_request }.to change(merge_request, :assignees).to([])
+ end
+ end
+
+ context 'the assignee_ids parameter is the empty list' do
+ let(:opts) { { assignee_ids: [] } }
+
+ it 'removes all assignees' do
+ expect { update_merge_request }.to change(merge_request, :assignees).to([])
+ end
+ end
+
it 'updates the MR, and queues the more expensive work for later' do
expect_next(MergeRequests::HandleAssigneesChangeService, project: project, current_user: user) do |service|
expect(service)
diff --git a/spec/support/shared_contexts/policies/project_policy_shared_context.rb b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
index 266c8d5ee84..35dc709b5d9 100644
--- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
@@ -15,7 +15,7 @@ RSpec.shared_context 'ProjectPolicy context' do
let(:base_guest_permissions) do
%i[
- award_emoji create_issue create_merge_request_in create_note
+ award_emoji create_issue create_incident create_merge_request_in create_note
create_project read_issue_board read_issue read_issue_iid read_issue_link
read_label read_issue_board_list read_milestone read_note read_project
read_project_for_iids read_project_member read_release read_snippet
diff --git a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
index 0a040557ffe..cfee26a0d6a 100644
--- a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
@@ -130,8 +130,8 @@ RSpec.shared_examples 'wiki controller actions' do
it_behaves_like 'fetching history', :ok do
let(:allow_read_wiki) { true }
- it 'assigns @page_versions' do
- expect(assigns(:page_versions)).to be_present
+ it 'assigns @commits' do
+ expect(assigns(:commits)).to be_present
end
end
diff --git a/spec/support/shared_examples/policies/project_policy_shared_examples.rb b/spec/support/shared_examples/policies/project_policy_shared_examples.rb
index d05e5eb9120..013c9b61b99 100644
--- a/spec/support/shared_examples/policies/project_policy_shared_examples.rb
+++ b/spec/support/shared_examples/policies/project_policy_shared_examples.rb
@@ -57,7 +57,7 @@ RSpec.shared_examples 'project policies as anonymous' do
context 'when a project has pending invites' do
let(:group) { create(:group, :public) }
let(:project) { create(:project, :public, namespace: group) }
- let(:user_permissions) { [:create_merge_request_in, :create_project, :create_issue, :create_note, :upload_file, :award_emoji] }
+ let(:user_permissions) { [:create_merge_request_in, :create_project, :create_issue, :create_note, :upload_file, :award_emoji, :create_incident] }
let(:anonymous_permissions) { guest_permissions - user_permissions }
let(:current_user) { anonymous }
diff --git a/spec/views/layouts/_head.html.haml_spec.rb b/spec/views/layouts/_head.html.haml_spec.rb
index ef0bd97cbcf..6752bdc8337 100644
--- a/spec/views/layouts/_head.html.haml_spec.rb
+++ b/spec/views/layouts/_head.html.haml_spec.rb
@@ -62,12 +62,6 @@ RSpec.describe 'layouts/_head' do
expect(rendered).to match('<link rel="stylesheet" media="print" href="/stylesheets/highlight/themes/solarised-light.css" />')
end
- it 'preloads Monaco' do
- render
-
- expect(rendered).to match('<link rel="preload" href="/assets/webpack/monaco.chunk.js" as="script" type="text/javascript">')
- end
-
context 'when an asset_host is set and snowplow url is set' do
let(:asset_host) { 'http://test.host' }
let(:snowplow_collector_hostname) { 'www.snow.plow' }