summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/finders/pipelines_finder_spec.rb13
-rw-r--r--spec/frontend/create_cluster/gke_cluster/components/gke_network_dropdown_spec.js143
-rw-r--r--spec/frontend/create_cluster/gke_cluster/components/gke_subnetwork_dropdown_spec.js113
-rw-r--r--spec/graphql/types/project_type_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/policy/refs_spec.rb28
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb53
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml2
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/models/ci/pipeline_spec.rb110
-rw-r--r--spec/services/ci/create_pipeline_service/custom_config_content_spec.rb29
-rw-r--r--spec/services/suggestions/apply_service_spec.rb31
-rw-r--r--spec/views/projects/edit.html.haml_spec.rb27
12 files changed, 548 insertions, 4 deletions
diff --git a/spec/finders/pipelines_finder_spec.rb b/spec/finders/pipelines_finder_spec.rb
index c8a4ea799c3..1dbf9491118 100644
--- a/spec/finders/pipelines_finder_spec.rb
+++ b/spec/finders/pipelines_finder_spec.rb
@@ -64,6 +64,19 @@ describe PipelinesFinder do
end
end
+ context 'when project has child pipelines' do
+ let!(:parent_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:child_pipeline) { create(:ci_pipeline, project: project, source: :parent_pipeline) }
+
+ let!(:pipeline_source) do
+ create(:ci_sources_pipeline, pipeline: child_pipeline, source_pipeline: parent_pipeline)
+ end
+
+ it 'filters out child pipelines and show only the parents' do
+ is_expected.to eq([parent_pipeline])
+ end
+ end
+
HasStatus::AVAILABLE_STATUSES.each do |target|
context "when status is #{target}" do
let(:params) { { status: target } }
diff --git a/spec/frontend/create_cluster/gke_cluster/components/gke_network_dropdown_spec.js b/spec/frontend/create_cluster/gke_cluster/components/gke_network_dropdown_spec.js
new file mode 100644
index 00000000000..1df583af711
--- /dev/null
+++ b/spec/frontend/create_cluster/gke_cluster/components/gke_network_dropdown_spec.js
@@ -0,0 +1,143 @@
+import Vuex from 'vuex';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import GkeNetworkDropdown from '~/create_cluster/gke_cluster/components/gke_network_dropdown.vue';
+import ClusterFormDropdown from '~/create_cluster/components/cluster_form_dropdown.vue';
+import createClusterDropdownState from '~/create_cluster/store/cluster_dropdown/state';
+
+const localVue = createLocalVue();
+
+localVue.use(Vuex);
+
+describe('GkeNetworkDropdown', () => {
+ let wrapper;
+ let store;
+ const defaultProps = { fieldName: 'field-name' };
+ const selectedNetwork = { selfLink: '123456' };
+ const projectId = '6789';
+ const region = 'east-1';
+ const setNetwork = jest.fn();
+ const setSubnetwork = jest.fn();
+ const fetchSubnetworks = jest.fn();
+
+ const buildStore = ({ clusterDropdownState } = {}) =>
+ new Vuex.Store({
+ state: {
+ selectedNetwork,
+ },
+ actions: {
+ setNetwork,
+ setSubnetwork,
+ },
+ getters: {
+ hasZone: () => false,
+ region: () => region,
+ projectId: () => projectId,
+ },
+ modules: {
+ networks: {
+ namespaced: true,
+ state: {
+ ...createClusterDropdownState(),
+ ...(clusterDropdownState || {}),
+ },
+ },
+ subnetworks: {
+ namespaced: true,
+ actions: {
+ fetchItems: fetchSubnetworks,
+ },
+ },
+ },
+ });
+
+ const buildWrapper = (propsData = defaultProps) =>
+ shallowMount(GkeNetworkDropdown, {
+ propsData,
+ store,
+ localVue,
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('sets correct field-name', () => {
+ const fieldName = 'field-name';
+
+ store = buildStore();
+ wrapper = buildWrapper({ fieldName });
+
+ expect(wrapper.find(ClusterFormDropdown).props('fieldName')).toBe(fieldName);
+ });
+
+ it('sets selected network as the dropdown value', () => {
+ store = buildStore();
+ wrapper = buildWrapper();
+
+ expect(wrapper.find(ClusterFormDropdown).props('value')).toBe(selectedNetwork);
+ });
+
+ it('maps networks store items to the dropdown items property', () => {
+ const items = [{ name: 'network' }];
+
+ store = buildStore({ clusterDropdownState: { items } });
+ wrapper = buildWrapper();
+
+ expect(wrapper.find(ClusterFormDropdown).props('items')).toBe(items);
+ });
+
+ describe('when network dropdown store is loading items', () => {
+ it('sets network dropdown as loading', () => {
+ store = buildStore({ clusterDropdownState: { isLoadingItems: true } });
+ wrapper = buildWrapper();
+
+ expect(wrapper.find(ClusterFormDropdown).props('loading')).toBe(true);
+ });
+ });
+
+ describe('when there is no selected zone', () => {
+ it('disables the network dropdown', () => {
+ store = buildStore();
+ wrapper = buildWrapper();
+
+ expect(wrapper.find(ClusterFormDropdown).props('disabled')).toBe(true);
+ });
+ });
+
+ describe('when an error occurs while loading networks', () => {
+ it('sets the network dropdown as having errors', () => {
+ store = buildStore({ clusterDropdownState: { loadingItemsError: new Error() } });
+ wrapper = buildWrapper();
+
+ expect(wrapper.find(ClusterFormDropdown).props('hasErrors')).toBe(true);
+ });
+ });
+
+ describe('when dropdown emits input event', () => {
+ beforeEach(() => {
+ store = buildStore();
+ wrapper = buildWrapper();
+ wrapper.find(ClusterFormDropdown).vm.$emit('input', selectedNetwork);
+ });
+
+ it('cleans selected subnetwork', () => {
+ expect(setSubnetwork).toHaveBeenCalledWith(expect.anything(), '', undefined);
+ });
+
+ it('dispatches the setNetwork action', () => {
+ expect(setNetwork).toHaveBeenCalledWith(expect.anything(), selectedNetwork, undefined);
+ });
+
+ it('fetches subnetworks for the selected project, region, and network', () => {
+ expect(fetchSubnetworks).toHaveBeenCalledWith(
+ expect.anything(),
+ {
+ project: projectId,
+ region,
+ network: selectedNetwork.selfLink,
+ },
+ undefined,
+ );
+ });
+ });
+});
diff --git a/spec/frontend/create_cluster/gke_cluster/components/gke_subnetwork_dropdown_spec.js b/spec/frontend/create_cluster/gke_cluster/components/gke_subnetwork_dropdown_spec.js
new file mode 100644
index 00000000000..a1dc3960fe9
--- /dev/null
+++ b/spec/frontend/create_cluster/gke_cluster/components/gke_subnetwork_dropdown_spec.js
@@ -0,0 +1,113 @@
+import Vuex from 'vuex';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import GkeSubnetworkDropdown from '~/create_cluster/gke_cluster/components/gke_subnetwork_dropdown.vue';
+import ClusterFormDropdown from '~/create_cluster/components/cluster_form_dropdown.vue';
+import createClusterDropdownState from '~/create_cluster/store/cluster_dropdown/state';
+
+const localVue = createLocalVue();
+
+localVue.use(Vuex);
+
+describe('GkeSubnetworkDropdown', () => {
+ let wrapper;
+ let store;
+ const defaultProps = { fieldName: 'field-name' };
+ const selectedSubnetwork = '123456';
+ const setSubnetwork = jest.fn();
+
+ const buildStore = ({ clusterDropdownState } = {}) =>
+ new Vuex.Store({
+ state: {
+ selectedSubnetwork,
+ },
+ actions: {
+ setSubnetwork,
+ },
+ getters: {
+ hasNetwork: () => false,
+ },
+ modules: {
+ subnetworks: {
+ namespaced: true,
+ state: {
+ ...createClusterDropdownState(),
+ ...(clusterDropdownState || {}),
+ },
+ },
+ },
+ });
+
+ const buildWrapper = (propsData = defaultProps) =>
+ shallowMount(GkeSubnetworkDropdown, {
+ propsData,
+ store,
+ localVue,
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('sets correct field-name', () => {
+ const fieldName = 'field-name';
+
+ store = buildStore();
+ wrapper = buildWrapper({ fieldName });
+
+ expect(wrapper.find(ClusterFormDropdown).props('fieldName')).toBe(fieldName);
+ });
+
+ it('sets selected subnetwork as the dropdown value', () => {
+ store = buildStore();
+ wrapper = buildWrapper();
+
+ expect(wrapper.find(ClusterFormDropdown).props('value')).toBe(selectedSubnetwork);
+ });
+
+ it('maps subnetworks store items to the dropdown items property', () => {
+ const items = [{ name: 'subnetwork' }];
+
+ store = buildStore({ clusterDropdownState: { items } });
+ wrapper = buildWrapper();
+
+ expect(wrapper.find(ClusterFormDropdown).props('items')).toBe(items);
+ });
+
+ describe('when subnetwork dropdown store is loading items', () => {
+ it('sets subnetwork dropdown as loading', () => {
+ store = buildStore({ clusterDropdownState: { isLoadingItems: true } });
+ wrapper = buildWrapper();
+
+ expect(wrapper.find(ClusterFormDropdown).props('loading')).toBe(true);
+ });
+ });
+
+ describe('when there is no selected network', () => {
+ it('disables the subnetwork dropdown', () => {
+ store = buildStore();
+ wrapper = buildWrapper();
+
+ expect(wrapper.find(ClusterFormDropdown).props('disabled')).toBe(true);
+ });
+ });
+
+ describe('when an error occurs while loading subnetworks', () => {
+ it('sets the subnetwork dropdown as having errors', () => {
+ store = buildStore({ clusterDropdownState: { loadingItemsError: new Error() } });
+ wrapper = buildWrapper();
+
+ expect(wrapper.find(ClusterFormDropdown).props('hasErrors')).toBe(true);
+ });
+ });
+
+ describe('when dropdown emits input event', () => {
+ it('dispatches the setSubnetwork action', () => {
+ store = buildStore();
+ wrapper = buildWrapper();
+
+ wrapper.find(ClusterFormDropdown).vm.$emit('input', selectedSubnetwork);
+
+ expect(setSubnetwork).toHaveBeenCalledWith(expect.anything(), selectedSubnetwork, undefined);
+ });
+ });
+});
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index 27fa98bbde4..7bed1f72e0b 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -23,7 +23,7 @@ describe GitlabSchema.types['Project'] do
only_allow_merge_if_all_discussions_are_resolved printing_merge_request_link_enabled
namespace group statistics repository merge_requests merge_request issues
issue pipelines removeSourceBranchAfterMerge sentryDetailedError snippets
- grafanaIntegration autocloseReferencedIssues
+ grafanaIntegration autocloseReferencedIssues suggestion_commit_message
]
is_expected.to include_graphql_fields(*expected_fields)
diff --git a/spec/lib/gitlab/ci/build/policy/refs_spec.rb b/spec/lib/gitlab/ci/build/policy/refs_spec.rb
index 8fc1e0a4e88..c32fdc5c72e 100644
--- a/spec/lib/gitlab/ci/build/policy/refs_spec.rb
+++ b/spec/lib/gitlab/ci/build/policy/refs_spec.rb
@@ -98,6 +98,34 @@ describe Gitlab::Ci::Build::Policy::Refs do
.not_to be_satisfied_by(pipeline)
end
end
+
+ context 'when source is pipeline' do
+ let(:pipeline) { build_stubbed(:ci_pipeline, source: :pipeline) }
+
+ it 'is satisfied with only: pipelines' do
+ expect(described_class.new(%w[pipelines]))
+ .to be_satisfied_by(pipeline)
+ end
+
+ it 'is satisfied with only: pipeline' do
+ expect(described_class.new(%w[pipeline]))
+ .to be_satisfied_by(pipeline)
+ end
+ end
+
+ context 'when source is parent_pipeline' do
+ let(:pipeline) { build_stubbed(:ci_pipeline, source: :parent_pipeline) }
+
+ it 'is satisfied with only: parent_pipelines' do
+ expect(described_class.new(%w[parent_pipelines]))
+ .to be_satisfied_by(pipeline)
+ end
+
+ it 'is satisfied with only: parent_pipeline' do
+ expect(described_class.new(%w[parent_pipeline]))
+ .to be_satisfied_by(pipeline)
+ end
+ end
end
context 'when matching a ref by a regular expression' do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
index aaea044595f..4c4359ad5d2 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
@@ -15,6 +15,42 @@ describe Gitlab::Ci::Pipeline::Chain::Config::Content do
stub_feature_flags(ci_root_config_content: false)
end
+ context 'when bridge job is passed in as parameter' do
+ let(:ci_config_path) { nil }
+ let(:bridge) { create(:ci_bridge) }
+
+ before do
+ command.bridge = bridge
+ end
+
+ context 'when bridge job has downstream yaml' do
+ before do
+ allow(bridge).to receive(:yaml_for_downstream).and_return('the-yaml')
+ end
+
+ it 'returns the content already available in command' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'bridge_source'
+ expect(command.config_content).to eq 'the-yaml'
+ end
+ end
+
+ context 'when bridge job does not have downstream yaml' do
+ before do
+ allow(bridge).to receive(:yaml_for_downstream).and_return(nil)
+ end
+
+ it 'returns the next available source' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'auto_devops_source'
+ template = Gitlab::Template::GitlabCiYmlTemplate.find('Beta/Auto-DevOps')
+ expect(command.config_content).to eq(template.content)
+ end
+ end
+ end
+
context 'when config is defined in a custom path in the repository' do
let(:ci_config_path) { 'path/to/config.yml' }
@@ -135,6 +171,23 @@ describe Gitlab::Ci::Pipeline::Chain::Config::Content do
end
end
+ context 'when bridge job is passed in as parameter' do
+ let(:ci_config_path) { nil }
+ let(:bridge) { create(:ci_bridge) }
+
+ before do
+ command.bridge = bridge
+ allow(bridge).to receive(:yaml_for_downstream).and_return('the-yaml')
+ end
+
+ it 'returns the content already available in command' do
+ subject.perform!
+
+ expect(pipeline.config_source).to eq 'bridge_source'
+ expect(command.config_content).to eq 'the-yaml'
+ end
+ end
+
context 'when config is defined in a custom path in the repository' do
let(:ci_config_path) { 'path/to/config.yml' }
let(:config_content_result) do
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index a45ad514da2..07439880beb 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -201,6 +201,8 @@ ci_pipelines:
- sourced_pipelines
- triggered_by_pipeline
- triggered_pipelines
+- child_pipelines
+- parent_pipeline
- downstream_bridges
- job_artifacts
- vulnerabilities_occurrence_pipelines
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index adb49c8c7e7..e5014eeca09 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -535,6 +535,7 @@ Project:
- merge_requests_disable_committers_approval
- require_password_to_approve
- autoclose_referenced_issues
+- suggestion_commit_message
ProjectTracingSetting:
- external_url
Author:
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index b30e88532e1..ce01765bb8c 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -2716,4 +2716,114 @@ describe Ci::Pipeline, :mailer do
end
end
end
+
+ describe '#parent_pipeline' do
+ let(:project) { create(:project) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+
+ context 'when pipeline is triggered by a pipeline from the same project' do
+ let(:upstream_pipeline) { create(:ci_pipeline, project: pipeline.project) }
+
+ before do
+ create(:ci_sources_pipeline,
+ source_pipeline: upstream_pipeline,
+ source_project: project,
+ pipeline: pipeline,
+ project: project)
+ end
+
+ it 'returns the parent pipeline' do
+ expect(pipeline.parent_pipeline).to eq(upstream_pipeline)
+ end
+
+ it 'is child' do
+ expect(pipeline).to be_child
+ end
+ end
+
+ context 'when pipeline is triggered by a pipeline from another project' do
+ let(:upstream_pipeline) { create(:ci_pipeline) }
+
+ before do
+ create(:ci_sources_pipeline,
+ source_pipeline: upstream_pipeline,
+ source_project: upstream_pipeline.project,
+ pipeline: pipeline,
+ project: project)
+ end
+
+ it 'returns nil' do
+ expect(pipeline.parent_pipeline).to be_nil
+ end
+
+ it 'is not child' do
+ expect(pipeline).not_to be_child
+ end
+ end
+
+ context 'when pipeline is not triggered by a pipeline' do
+ it 'returns nil' do
+ expect(pipeline.parent_pipeline).to be_nil
+ end
+
+ it 'is not child' do
+ expect(pipeline).not_to be_child
+ end
+ end
+ end
+
+ describe '#child_pipelines' do
+ let(:project) { create(:project) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+
+ context 'when pipeline triggered other pipelines on same project' do
+ let(:downstream_pipeline) { create(:ci_pipeline, project: pipeline.project) }
+
+ before do
+ create(:ci_sources_pipeline,
+ source_pipeline: pipeline,
+ source_project: pipeline.project,
+ pipeline: downstream_pipeline,
+ project: pipeline.project)
+ end
+
+ it 'returns the child pipelines' do
+ expect(pipeline.child_pipelines).to eq [downstream_pipeline]
+ end
+
+ it 'is parent' do
+ expect(pipeline).to be_parent
+ end
+ end
+
+ context 'when pipeline triggered other pipelines on another project' do
+ let(:downstream_pipeline) { create(:ci_pipeline) }
+
+ before do
+ create(:ci_sources_pipeline,
+ source_pipeline: pipeline,
+ source_project: pipeline.project,
+ pipeline: downstream_pipeline,
+ project: downstream_pipeline.project)
+ end
+
+ it 'returns empty array' do
+ expect(pipeline.child_pipelines).to be_empty
+ end
+
+ it 'is not parent' do
+ expect(pipeline).not_to be_parent
+ end
+ end
+
+ context 'when pipeline did not trigger any pipelines' do
+ it 'returns empty array' do
+ expect(pipeline.child_pipelines).to be_empty
+ end
+
+ it 'is not parent' do
+ expect(pipeline).not_to be_parent
+ end
+ end
+ end
end
diff --git a/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb b/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb
new file mode 100644
index 00000000000..33cd6e164b0
--- /dev/null
+++ b/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Ci::CreatePipelineService do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:admin) }
+ let(:ref) { 'refs/heads/master' }
+ let(:service) { described_class.new(project, user, { ref: ref }) }
+
+ context 'custom config content' do
+ let(:bridge) do
+ double(:bridge, yaml_for_downstream: <<~YML
+ rspec:
+ script: rspec
+ custom:
+ script: custom
+ YML
+ )
+ end
+
+ subject { service.execute(:push, bridge: bridge) }
+
+ it 'creates a pipeline using the content passed in as param' do
+ expect(subject).to be_persisted
+ expect(subject.builds.map(&:name)).to eq %w[rspec custom]
+ expect(subject.config_source).to eq 'bridge_source'
+ end
+ end
+end
diff --git a/spec/services/suggestions/apply_service_spec.rb b/spec/services/suggestions/apply_service_spec.rb
index bdbcb0fdb07..84529af7187 100644
--- a/spec/services/suggestions/apply_service_spec.rb
+++ b/spec/services/suggestions/apply_service_spec.rb
@@ -48,10 +48,34 @@ describe Suggestions::ApplyService do
expect(commit.committer_email).to eq(user.commit_email)
expect(commit.author_name).to eq(user.name)
end
+
+ context 'when a custom suggestion commit message' do
+ before do
+ project.update!(suggestion_commit_message: message)
+
+ apply(suggestion)
+ end
+
+ context 'is not specified' do
+ let(:message) { nil }
+
+ it 'sets default commit message' do
+ expect(project.repository.commit.message).to eq("Apply suggestion to files/ruby/popen.rb")
+ end
+ end
+
+ context 'is specified' do
+ let(:message) { 'refactor: %{project_path} %{project_name} %{file_path} %{branch_name} %{username} %{user_full_name}' }
+
+ it 'sets custom commit message' do
+ expect(project.repository.commit.message).to eq("refactor: project-1 Project_1 files/ruby/popen.rb master test.user Test User")
+ end
+ end
+ end
end
- let(:project) { create(:project, :repository) }
- let(:user) { create(:user, :commit_email) }
+ let(:project) { create(:project, :repository, path: 'project-1', name: 'Project_1') }
+ let(:user) { create(:user, :commit_email, name: 'Test User', username: 'test.user') }
let(:position) { build_position }
@@ -113,7 +137,8 @@ describe Suggestions::ApplyService do
context 'non-fork project' do
let(:merge_request) do
create(:merge_request, source_project: project,
- target_project: project)
+ target_project: project,
+ source_branch: 'master')
end
before do
diff --git a/spec/views/projects/edit.html.haml_spec.rb b/spec/views/projects/edit.html.haml_spec.rb
index 8005b549838..e95dec56a2d 100644
--- a/spec/views/projects/edit.html.haml_spec.rb
+++ b/spec/views/projects/edit.html.haml_spec.rb
@@ -28,6 +28,33 @@ describe 'projects/edit' do
end
end
+ context 'merge suggestions settings' do
+ it 'displays all possible variables' do
+ render
+
+ expect(rendered).to have_content('%{project_path}')
+ expect(rendered).to have_content('%{project_name}')
+ expect(rendered).to have_content('%{file_path}')
+ expect(rendered).to have_content('%{branch_name}')
+ expect(rendered).to have_content('%{username}')
+ expect(rendered).to have_content('%{user_full_name}')
+ end
+
+ it 'displays a placeholder if none is set' do
+ render
+
+ expect(rendered).to have_field('project[suggestion_commit_message]', placeholder: 'Apply suggestion to %{file_path}')
+ end
+
+ it 'displays the user entered value' do
+ project.update!(suggestion_commit_message: 'refactor: changed %{file_path}')
+
+ render
+
+ expect(rendered).to have_field('project[suggestion_commit_message]', with: 'refactor: changed %{file_path}')
+ end
+ end
+
context 'forking' do
before do
assign(:project, project)