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>2023-01-25 18:09:25 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-01-25 18:09:25 +0300
commitec558ad8ed732ff6f8a89aa3651eb92c27c50deb (patch)
treee6d0eb5946758007594e8bf6feb5fb9759f48d6c /spec
parent7b1fa4c1a1b784c2f78405dca82e56a009f1e773 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb56
-rw-r--r--spec/factories/ci/bridge.rb8
-rw-r--r--spec/factories/clusters/applications/helm.rb4
-rw-r--r--spec/factories/clusters/clusters.rb1
-rw-r--r--spec/features/work_items/work_item_spec.rb45
-rw-r--r--spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_page_spec.js45
-rw-r--r--spec/frontend/lib/utils/datetime/date_format_utility_spec.js4
-rw-r--r--spec/frontend/projects/commit/components/projects_dropdown_spec.js64
-rw-r--r--spec/frontend/releases/components/evidence_block_spec.js8
-rw-r--r--spec/frontend/vue_merge_request_widget/components/report_widget_container_spec.js27
-rw-r--r--spec/lib/gitlab/ci/status/bridge/factory_spec.rb29
-rw-r--r--spec/models/ci/bridge_spec.rb20
-rw-r--r--spec/models/ci/processable_spec.rb6
-rw-r--r--spec/models/clusters/applications/cilium_spec.rb17
-rw-r--r--spec/models/project_spec.rb23
-rw-r--r--spec/services/ci/retry_job_service_spec.rb66
-rw-r--r--spec/services/projects/create_service_spec.rb2
-rw-r--r--spec/services/projects/destroy_service_spec.rb18
-rw-r--r--spec/services/projects/transfer_service_spec.rb17
-rw-r--r--spec/support/shared_examples/features/work_items_shared_examples.rb109
20 files changed, 380 insertions, 189 deletions
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index 3dc89365530..8fb9623c21a 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
+RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state, feature_category: :continuous_integration do
include ApiHelpers
include HttpIOHelpers
@@ -809,14 +809,48 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
sign_in(user)
end
+ context 'when job is not retryable' do
+ context 'and the job is a bridge' do
+ let(:job) { create(:ci_bridge, :failed, :reached_max_descendant_pipelines_depth, pipeline: pipeline) }
+
+ it 'renders unprocessable_entity' do
+ post_retry
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ end
+ end
+
+ context 'and the job is a build' do
+ let(:job) { create(:ci_build, :deployment_rejected, pipeline: pipeline) }
+
+ it 'renders unprocessable_entity' do
+ post_retry
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ end
+ end
+ end
+
context 'when job is retryable' do
- let(:job) { create(:ci_build, :retryable, pipeline: pipeline) }
+ context 'and the job is a bridge' do
+ let(:job) { create(:ci_bridge, :retryable, pipeline: pipeline) }
- it 'redirects to the retried job page' do
- post_retry
+ it 'responds :ok' do
+ post_retry
- expect(response).to have_gitlab_http_status(:found)
- expect(response).to redirect_to(namespace_project_job_path(id: Ci::Build.last.id))
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'and the job is a build' do
+ let(:job) { create(:ci_build, :retryable, pipeline: pipeline) }
+
+ it 'redirects to the retried job page' do
+ post_retry
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(response).to redirect_to(namespace_project_job_path(id: Ci::Build.last.id))
+ end
end
shared_examples_for 'retried job has the same attributes' do
@@ -847,16 +881,6 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
end
- context 'when job is not retryable' do
- let(:job) { create(:ci_build, pipeline: pipeline) }
-
- it 'renders unprocessable_entity' do
- post_retry
-
- expect(response).to have_gitlab_http_status(:unprocessable_entity)
- end
- end
-
def post_retry
post :retry, params: {
namespace_id: project.namespace,
diff --git a/spec/factories/ci/bridge.rb b/spec/factories/ci/bridge.rb
index 6cbcabca7ab..49ac74f6f86 100644
--- a/spec/factories/ci/bridge.rb
+++ b/spec/factories/ci/bridge.rb
@@ -33,6 +33,14 @@ FactoryBot.define do
end
end
+ trait :retried do
+ retried { true }
+ end
+
+ trait :retryable do
+ success
+ end
+
trait :created do
status { 'created' }
end
diff --git a/spec/factories/clusters/applications/helm.rb b/spec/factories/clusters/applications/helm.rb
index 6a21df943f5..078450ef1e8 100644
--- a/spec/factories/clusters/applications/helm.rb
+++ b/spec/factories/clusters/applications/helm.rb
@@ -125,9 +125,5 @@ FactoryBot.define do
oauth_application factory: :oauth_application
cluster factory: %i(cluster with_installed_helm provided_by_gcp project)
end
-
- factory :clusters_applications_cilium, class: 'Clusters::Applications::Cilium' do
- cluster factory: %i(cluster with_installed_helm provided_by_gcp)
- end
end
end
diff --git a/spec/factories/clusters/clusters.rb b/spec/factories/clusters/clusters.rb
index 72424a3c321..f6af9d04ea5 100644
--- a/spec/factories/clusters/clusters.rb
+++ b/spec/factories/clusters/clusters.rb
@@ -100,7 +100,6 @@ FactoryBot.define do
application_runner factory: %i(clusters_applications_runner installed)
application_jupyter factory: %i(clusters_applications_jupyter installed)
application_knative factory: %i(clusters_applications_knative installed)
- application_cilium factory: %i(clusters_applications_cilium installed)
end
trait :with_domain do
diff --git a/spec/features/work_items/work_item_spec.rb b/spec/features/work_items/work_item_spec.rb
index 577ec060020..1fdf54d801a 100644
--- a/spec/features/work_items/work_item_spec.rb
+++ b/spec/features/work_items/work_item_spec.rb
@@ -5,56 +5,21 @@ require 'spec_helper'
RSpec.describe 'Work item', :js, feature_category: :team_planning do
let_it_be(:project) { create(:project, :public) }
let_it_be(:user) { create(:user) }
- let_it_be(:other_user) { create(:user) }
let_it_be(:work_item) { create(:work_item, project: project) }
context 'for signed in user' do
before do
project.add_developer(user)
- project.add_developer(other_user)
sign_in(user)
visit project_work_items_path(project, work_items_path: work_item.id)
end
- context 'in work item description' do
- it 'shows GFM autocomplete', :aggregate_failures do
- click_button "Edit description"
-
- find('[aria-label="Description"]').send_keys("@#{user.username}")
-
- wait_for_requests
-
- page.within('.atwho-container') do
- expect(page).to have_text(user.name)
- end
- end
-
- it 'shows conflict message when description changes', :aggregate_failures do
- click_button "Edit description"
- scroll_to(find('[aria-label="Description"]'))
-
- # without this for some reason the test fails when running locally
- sleep 1
-
- ::WorkItems::UpdateService.new(
- project: work_item.project,
- current_user: other_user,
- params: { description: "oh no!" }
- ).execute(work_item)
-
- work_item.reload
-
- find('[aria-label="Description"]').send_keys("oh yeah!")
-
- warning = 'Someone edited the description at the same time you did.'
- expect(page.find('[data-testid="work-item-description-conflicts"]')).to have_text(warning)
-
- click_button "Save and overwrite"
-
- expect(page.find('[data-testid="work-item-description"]')).to have_text("oh yeah!")
- end
- end
+ it_behaves_like 'work items status'
+ it_behaves_like 'work items assignees'
+ it_behaves_like 'work items labels'
+ it_behaves_like 'work items comments'
+ it_behaves_like 'work items description'
end
end
diff --git a/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_page_spec.js b/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_page_spec.js
index c12a45b2f41..658b2cc4be3 100644
--- a/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_page_spec.js
+++ b/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_page_spec.js
@@ -36,47 +36,18 @@ describe('SignInPage', () => {
});
it.each`
- jiraConnectOauthEnabled | jiraConnectOauthSelfManagedEnabled | shouldRenderDotCom | shouldRenderMultiversion
- ${false} | ${false} | ${true} | ${false}
- ${false} | ${true} | ${true} | ${false}
- ${true} | ${false} | ${true} | ${false}
- ${true} | ${true} | ${false} | ${true}
+ jiraConnectOauthEnabled | shouldRenderDotCom | shouldRenderMultiversion
+ ${false} | ${true} | ${false}
+ ${false} | ${true} | ${false}
+ ${true} | ${false} | ${true}
+ ${true} | ${false} | ${true}
`(
- 'renders correct component when jiraConnectOauth is $jiraConnectOauthEnabled and jiraConnectOauthSelfManaged is $jiraConnectOauthSelfManagedEnabled',
- ({
- jiraConnectOauthEnabled,
- jiraConnectOauthSelfManagedEnabled,
- shouldRenderDotCom,
- shouldRenderMultiversion,
- }) => {
- createComponent({ jiraConnectOauthEnabled, jiraConnectOauthSelfManagedEnabled });
+ 'renders correct component when jiraConnectOauth is $jiraConnectOauthEnabled',
+ ({ jiraConnectOauthEnabled, shouldRenderDotCom, shouldRenderMultiversion }) => {
+ createComponent({ jiraConnectOauthEnabled });
expect(findSignInGitlabCom().exists()).toBe(shouldRenderDotCom);
expect(findSignInGitabMultiversion().exists()).toBe(shouldRenderMultiversion);
},
);
-
- describe('when jiraConnectOauthSelfManaged is false', () => {
- beforeEach(() => {
- createComponent({ jiraConnectOauthSelfManaged: false, props: { hasSubscriptions: true } });
- });
-
- it('renders SignInGitlabCom with correct props', () => {
- expect(findSignInGitlabCom().props()).toEqual({ hasSubscriptions: true });
- });
-
- describe('when error event is emitted', () => {
- it('emits another error event', () => {
- findSignInGitlabCom().vm.$emit('error');
- expect(wrapper.emitted('error')).toHaveLength(1);
- });
- });
-
- describe('when sign-in-oauth event is emitted', () => {
- it('emits another sign-in-oauth event', () => {
- findSignInGitlabCom().vm.$emit('sign-in-oauth');
- expect(wrapper.emitted('sign-in-oauth')[0]).toEqual([]);
- });
- });
- });
});
diff --git a/spec/frontend/lib/utils/datetime/date_format_utility_spec.js b/spec/frontend/lib/utils/datetime/date_format_utility_spec.js
index a83b0ed9fbe..2c3fa612740 100644
--- a/spec/frontend/lib/utils/datetime/date_format_utility_spec.js
+++ b/spec/frontend/lib/utils/datetime/date_format_utility_spec.js
@@ -82,6 +82,10 @@ describe('date_format_utility.js', () => {
);
});
+ it('defaults to 00:00 if no time is provided', () => {
+ expect(utils.dateAndTimeToISOString(new Date('2021-08-21'))).toBe('2021-08-21T00:00:00.000Z');
+ });
+
it('throws if date in invalid', () => {
expect(() => utils.dateAndTimeToISOString('Invalid date', '10:00')).toThrow(
'Argument should be a Date instance',
diff --git a/spec/frontend/projects/commit/components/projects_dropdown_spec.js b/spec/frontend/projects/commit/components/projects_dropdown_spec.js
index bb20918e0cd..0e213ff388a 100644
--- a/spec/frontend/projects/commit/components/projects_dropdown_spec.js
+++ b/spec/frontend/projects/commit/components/projects_dropdown_spec.js
@@ -1,4 +1,4 @@
-import { GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui';
+import { GlCollapsibleListbox } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
@@ -35,78 +35,23 @@ describe('ProjectsDropdown', () => {
);
};
- const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
- const findSearchBoxByType = () => wrapper.findComponent(GlSearchBoxByType);
- const findDropdownItemByIndex = (index) => wrapper.findAllComponents(GlDropdownItem).at(index);
- const findNoResults = () => wrapper.findByTestId('empty-result-message');
+ const findDropdown = () => wrapper.findComponent(GlCollapsibleListbox);
afterEach(() => {
wrapper.destroy();
spyFetchProjects.mockReset();
});
- describe('No projects found', () => {
- beforeEach(() => {
- createComponent('_non_existent_project_');
- });
-
- it('renders empty results message', () => {
- expect(findNoResults().text()).toBe('No matching results');
- });
-
- it('shows GlSearchBoxByType with default attributes', () => {
- expect(findSearchBoxByType().exists()).toBe(true);
- expect(findSearchBoxByType().vm.$attrs).toMatchObject({
- placeholder: 'Search projects',
- });
- });
- });
-
- describe('Search term is empty', () => {
- beforeEach(() => {
- createComponent('');
- });
-
- it('renders all projects when search term is empty', () => {
- expect(findAllDropdownItems()).toHaveLength(3);
- expect(findDropdownItemByIndex(0).text()).toBe('_project_1_');
- expect(findDropdownItemByIndex(1).text()).toBe('_project_2_');
- expect(findDropdownItemByIndex(2).text()).toBe('_project_3_');
- });
-
- it('should not be selected on the inactive project', () => {
- expect(wrapper.vm.isSelected('_project_1_')).toBe(false);
- });
- });
-
describe('Projects found', () => {
beforeEach(() => {
createComponent('_project_1_', { targetProjectId: '1' });
});
- it('renders only the project searched for', () => {
- expect(findAllDropdownItems()).toHaveLength(1);
- expect(findDropdownItemByIndex(0).text()).toBe('_project_1_');
- });
-
- it('should not display empty results message', () => {
- expect(findNoResults().exists()).toBe(false);
- });
-
- it('should signify this project is selected', () => {
- expect(findDropdownItemByIndex(0).props('isChecked')).toBe(true);
- });
-
- it('should signify the project is not selected', () => {
- expect(wrapper.vm.isSelected('_not_selected_project_')).toBe(false);
- });
-
describe('Custom events', () => {
it('should emit selectProject if a project is clicked', () => {
- findDropdownItemByIndex(0).vm.$emit('click');
+ findDropdown().vm.$emit('select', '1');
expect(wrapper.emitted('selectProject')).toEqual([['1']]);
- expect(wrapper.vm.filterTerm).toBe('_project_1_');
});
});
});
@@ -117,8 +62,7 @@ describe('ProjectsDropdown', () => {
});
it('renders only the project searched for', () => {
- expect(findAllDropdownItems()).toHaveLength(1);
- expect(findDropdownItemByIndex(0).text()).toBe('_project_1_');
+ expect(findDropdown().props('items')).toEqual([{ text: '_project_1_', value: '1' }]);
});
});
});
diff --git a/spec/frontend/releases/components/evidence_block_spec.js b/spec/frontend/releases/components/evidence_block_spec.js
index 6f935215dd7..69443cb7a11 100644
--- a/spec/frontend/releases/components/evidence_block_spec.js
+++ b/spec/frontend/releases/components/evidence_block_spec.js
@@ -40,13 +40,11 @@ describe('Evidence Block', () => {
});
it('renders the correct hover text for the download', () => {
- expect(wrapper.findComponent(GlLink).attributes('title')).toBe('Download evidence JSON');
+ expect(wrapper.findComponent(GlLink).attributes('title')).toBe('Open evidence JSON in new tab');
});
- it('renders the correct file link for download', () => {
- expect(wrapper.findComponent(GlLink).attributes().download).toMatch(
- /v1\.1-evidences-[0-9]+\.json/,
- );
+ it('renders a link that opens in a new tab', () => {
+ expect(wrapper.findComponent(GlLink).attributes().target).toBe('_blank');
});
describe('sha text', () => {
diff --git a/spec/frontend/vue_merge_request_widget/components/report_widget_container_spec.js b/spec/frontend/vue_merge_request_widget/components/report_widget_container_spec.js
new file mode 100644
index 00000000000..2f6002f36e0
--- /dev/null
+++ b/spec/frontend/vue_merge_request_widget/components/report_widget_container_spec.js
@@ -0,0 +1,27 @@
+import { nextTick } from 'vue';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import ReportWidgetContainer from '~/vue_merge_request_widget/components/report_widget_container.vue';
+
+describe('app/assets/javascripts/vue_merge_request_widget/components/report_widget_container.vue', () => {
+ let wrapper;
+
+ const createComponent = ({ slot } = {}) => {
+ wrapper = mountExtended(ReportWidgetContainer, {
+ slots: {
+ default: slot,
+ },
+ });
+ };
+
+ it('hides the container when children has no content', async () => {
+ createComponent({ slot: `<span><b></b></span>` });
+ await nextTick();
+ expect(wrapper.isVisible()).toBe(false);
+ });
+
+ it('shows the container when children have no content', async () => {
+ createComponent({ slot: `<span><b>test</b></span>` });
+ await nextTick();
+ expect(wrapper.isVisible()).toBe(true);
+ });
+});
diff --git a/spec/lib/gitlab/ci/status/bridge/factory_spec.rb b/spec/lib/gitlab/ci/status/bridge/factory_spec.rb
index c13901a4776..eac17cf08fb 100644
--- a/spec/lib/gitlab/ci/status/bridge/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/bridge/factory_spec.rb
@@ -40,7 +40,8 @@ RSpec.describe Gitlab::Ci::Status::Bridge::Factory, feature_category: :continuou
it 'matches correct extended statuses' do
expect(factory.extended_statuses)
- .to eq [Gitlab::Ci::Status::Bridge::Failed]
+ .to eq [Gitlab::Ci::Status::Bridge::Retryable,
+ Gitlab::Ci::Status::Bridge::Failed]
end
it 'fabricates a failed bridge status' do
@@ -54,7 +55,7 @@ RSpec.describe Gitlab::Ci::Status::Bridge::Factory, feature_category: :continuou
expect(status.label).to be_nil
expect(status.status_tooltip).to eq "#{s_('CiStatusText|failed')} - (unknown failure)"
expect(status).not_to have_details
- expect(status).not_to have_action
+ expect(status).to have_action
end
context 'failed with downstream_pipeline_creation_failed' do
@@ -136,6 +137,30 @@ RSpec.describe Gitlab::Ci::Status::Bridge::Factory, feature_category: :continuou
end
end
+ context 'when the bridge is successful and therefore retryable' do
+ let(:bridge) { create(:ci_bridge, :success) }
+
+ it 'matches correct core status' do
+ expect(factory.core_status).to be_a Gitlab::Ci::Status::Success
+ end
+
+ it 'matches correct extended statuses' do
+ expect(factory.extended_statuses)
+ .to eq [Gitlab::Ci::Status::Bridge::Retryable]
+ end
+
+ it 'fabricates a retryable build status' do
+ expect(status).to be_a Gitlab::Ci::Status::Bridge::Retryable
+ end
+
+ it 'fabricates status with correct details' do
+ expect(status.text).to eq s_('CiStatusText|passed')
+ expect(status.icon).to eq 'status_success'
+ expect(status.favicon).to eq 'favicon_status_success'
+ expect(status).to have_action
+ end
+ end
+
private
def create_bridge(*traits)
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index 964b57e709e..7b307de87c7 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -37,8 +37,18 @@ RSpec.describe Ci::Bridge, feature_category: :continuous_integration do
describe '#retryable?' do
let(:bridge) { create(:ci_bridge, :success) }
- it 'returns false' do
- expect(bridge.retryable?).to eq(false)
+ it 'returns true' do
+ expect(bridge.retryable?).to eq(true)
+ end
+
+ context 'without ci_recreate_downstream_pipeline ff' do
+ before do
+ stub_feature_flags(ci_recreate_downstream_pipeline: false)
+ end
+
+ it 'returns false' do
+ expect(bridge.retryable?).to eq(false)
+ end
end
end
@@ -590,4 +600,10 @@ RSpec.describe Ci::Bridge, feature_category: :continuous_integration do
expect(bridge.metadata.partition_id).to eq(ci_testing_partition_id)
end
end
+
+ describe '#deployment_job?' do
+ subject { bridge.deployment_job? }
+
+ it { is_expected.to eq(false) }
+ end
end
diff --git a/spec/models/ci/processable_spec.rb b/spec/models/ci/processable_spec.rb
index 07fac4ee2f7..89ed8ab3fa7 100644
--- a/spec/models/ci/processable_spec.rb
+++ b/spec/models/ci/processable_spec.rb
@@ -287,6 +287,12 @@ RSpec.describe Ci::Processable do
end
end
+ context 'when the processable is a bridge' do
+ subject(:processable) { create(:ci_bridge, pipeline: pipeline) }
+
+ it_behaves_like 'retryable processable'
+ end
+
context 'when the processable is a build' do
subject(:processable) { create(:ci_build, pipeline: pipeline) }
diff --git a/spec/models/clusters/applications/cilium_spec.rb b/spec/models/clusters/applications/cilium_spec.rb
deleted file mode 100644
index 8b01502d5c0..00000000000
--- a/spec/models/clusters/applications/cilium_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Clusters::Applications::Cilium do
- let(:cilium) { create(:clusters_applications_cilium) }
-
- include_examples 'cluster application core specs', :clusters_applications_cilium
- include_examples 'cluster application status specs', :clusters_applications_cilium
- include_examples 'cluster application initial status specs'
-
- describe '#allowed_to_uninstall?' do
- subject { cilium.allowed_to_uninstall? }
-
- it { is_expected.to be false }
- end
-end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 63931f136f8..2ea6c396117 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -920,6 +920,29 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do
end
end
+ describe '#invalidate_personal_projects_count_of_owner' do
+ context 'for personal projects' do
+ let_it_be(:namespace_user) { create(:user) }
+ let_it_be(:project) { create(:project, namespace: namespace_user.namespace) }
+
+ it 'invalidates personal_project_count cache of the the owner of the personal namespace' do
+ expect(Rails.cache).to receive(:delete).with(['users', namespace_user.id, 'personal_projects_count'])
+
+ project.invalidate_personal_projects_count_of_owner
+ end
+ end
+
+ context 'for projects in groups' do
+ let_it_be(:project) { create(:project, namespace: create(:group)) }
+
+ it 'does not invalidates any cache' do
+ expect(Rails.cache).not_to receive(:delete)
+
+ project.invalidate_personal_projects_count_of_owner
+ end
+ end
+ end
+
describe '#default_pipeline_lock' do
let(:project) { build_stubbed(:project) }
diff --git a/spec/services/ci/retry_job_service_spec.rb b/spec/services/ci/retry_job_service_spec.rb
index c3d80f2cb56..10acf032b1a 100644
--- a/spec/services/ci/retry_job_service_spec.rb
+++ b/spec/services/ci/retry_job_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::RetryJobService do
+RSpec.describe Ci::RetryJobService, feature_category: :continuous_integration do
using RSpec::Parameterized::TableSyntax
let_it_be(:reporter) { create(:user) }
let_it_be(:developer) { create(:user) }
@@ -27,6 +27,22 @@ RSpec.describe Ci::RetryJobService do
project.add_reporter(reporter)
end
+ shared_context 'retryable bridge' do
+ let_it_be(:downstream_project) { create(:project, :repository) }
+
+ let_it_be_with_refind(:job) do
+ create(:ci_bridge, :success,
+ pipeline: pipeline, downstream: downstream_project, description: 'a trigger job', ci_stage: stage
+ )
+ end
+
+ let_it_be(:job_to_clone) { job }
+
+ before do
+ job.update!(retried: false)
+ end
+ end
+
shared_context 'retryable build' do
let_it_be_with_reload(:job) do
create(:ci_build, :success, pipeline: pipeline, ci_stage: stage)
@@ -102,6 +118,14 @@ RSpec.describe Ci::RetryJobService do
end
end
+ shared_examples_for 'does not retry the job' do
+ it 'returns :not_retryable and :unprocessable_entity' do
+ expect(subject.message).to be('Job cannot be retried')
+ expect(subject.payload[:reason]).to eq(:not_retryable)
+ expect(subject.payload[:job]).to eq(job)
+ end
+ end
+
shared_examples_for 'retries the job' do
it_behaves_like 'clones the job'
@@ -189,6 +213,20 @@ RSpec.describe Ci::RetryJobService do
expect { service.clone!(create(:ci_build).present) }.to raise_error(TypeError)
end
+ context 'when the job to be cloned is a bridge' do
+ include_context 'retryable bridge'
+
+ it_behaves_like 'clones the job'
+
+ context 'when given variables' do
+ let(:new_job) { service.clone!(job, variables: job_variables_attributes) }
+
+ it 'does not give variables to the new bridge' do
+ expect { new_job }.not_to raise_error
+ end
+ end
+ end
+
context 'when the job to be cloned is a build' do
include_context 'retryable build'
@@ -287,7 +325,33 @@ RSpec.describe Ci::RetryJobService do
subject { service.execute(job) }
+ context 'when the job to be retried is a bridge' do
+ context 'and it is not retryable' do
+ let_it_be(:job) { create(:ci_bridge, :failed, :reached_max_descendant_pipelines_depth) }
+
+ it_behaves_like 'does not retry the job'
+ end
+
+ include_context 'retryable bridge'
+
+ it_behaves_like 'retries the job'
+
+ context 'when given variables' do
+ let(:new_job) { service.clone!(job, variables: job_variables_attributes) }
+
+ it 'does not give variables to the new bridge' do
+ expect { new_job }.not_to raise_error
+ end
+ end
+ end
+
context 'when the job to be retried is a build' do
+ context 'and it is not retryable' do
+ let_it_be(:job) { create(:ci_build, :deployment_rejected, pipeline: pipeline) }
+
+ it_behaves_like 'does not retry the job'
+ end
+
include_context 'retryable build'
it_behaves_like 'retries the job'
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index f85a8eda7ee..47a31b9c09d 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -163,7 +163,7 @@ RSpec.describe Projects::CreateService, '#execute', feature_category: :projects
describe 'after create actions' do
it 'invalidate personal_projects_count caches' do
- expect(user).to receive(:invalidate_personal_projects_count)
+ expect(Rails.cache).to receive(:delete).with(['users', user.id, 'personal_projects_count'])
create_project(user, opts)
end
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index ff2de45661f..0ef0ecc1099 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -151,10 +151,22 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
it_behaves_like 'deleting the project'
- it 'invalidates personal_project_count cache' do
- expect(user).to receive(:invalidate_personal_projects_count)
+ context 'personal projects count cache' do
+ context 'when the executor is the creator of the project itself' do
+ it 'invalidates personal_project_count cache of the the owner of the personal namespace' do
+ expect(user).to receive(:invalidate_personal_projects_count)
- destroy_project(project, user, {})
+ destroy_project(project, user, {})
+ end
+ end
+
+ context 'when the executor is the instance administrator', :enable_admin_mode do
+ it 'invalidates personal_project_count cache of the the owner of the personal namespace' do
+ expect(user).to receive(:invalidate_personal_projects_count)
+
+ destroy_project(project, create(:admin), {})
+ end
+ end
end
context 'with running pipelines' do
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 5171836f917..d62a736fe6a 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -126,6 +126,12 @@ RSpec.describe Projects::TransferService do
expect(project.namespace).to eq(user.namespace)
end
+ it 'invalidates personal_project_count cache of the the owner of the personal namespace' do
+ expect(user).to receive(:invalidate_personal_projects_count)
+
+ execute_transfer
+ end
+
context 'the owner of the namespace does not have a direct membership in the project residing in the group' do
it 'creates a project membership record for the owner of the namespace, with OWNER access level, after the transfer' do
execute_transfer
@@ -161,6 +167,17 @@ RSpec.describe Projects::TransferService do
end
end
+ context 'personal namespace -> group', :enable_admin_mode do
+ let(:executor) { create(:admin) }
+
+ it 'invalidates personal_project_count cache of the the owner of the personal namespace' \
+ 'that previously held the project' do
+ expect(user).to receive(:invalidate_personal_projects_count)
+
+ execute_transfer
+ end
+ end
+
context 'when transfer succeeds' do
before do
group.add_owner(user)
diff --git a/spec/support/shared_examples/features/work_items_shared_examples.rb b/spec/support/shared_examples/features/work_items_shared_examples.rb
new file mode 100644
index 00000000000..fe36b1006b0
--- /dev/null
+++ b/spec/support/shared_examples/features/work_items_shared_examples.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'work items status' do
+ let(:state_selector) { '[data-testid="work-item-state-select"]' }
+
+ it 'sucessfully shows and changes the status of the work item' do
+ expect(find(state_selector)).to have_content 'Open'
+
+ find(state_selector).select("Closed")
+
+ wait_for_requests
+
+ expect(find(state_selector)).to have_content 'Closed'
+ expect(work_item.reload.state).to eq('closed')
+ end
+end
+
+RSpec.shared_examples 'work items comments' do
+ let(:form_selector) { '[data-testid="work-item-add-comment"]' }
+
+ it 'sucessfully creates and shows comments' do
+ click_button 'Add a comment'
+
+ find(form_selector).fill_in(with: "Test comment")
+ click_button "Comment"
+
+ wait_for_requests
+
+ expect(page).to have_content "Test comment"
+ end
+end
+
+RSpec.shared_examples 'work items assignees' do
+ it 'sucessfully assigns the current user by searching' do
+ # The button is only when the mouse is over the input
+ find('[data-testid="work-item-asssignees-input"]').fill_in(with: user.username)
+ wait_for_requests
+
+ # submit and simulate blur to save
+ send_keys(:enter)
+ find("body").click
+
+ wait_for_requests
+
+ expect(work_item.assignees).to include(user)
+ end
+end
+
+RSpec.shared_examples 'work items labels' do
+ it 'sucessfully assigns a label' do
+ label = create(:label, project: work_item.project, title: "testing-label")
+
+ find('[data-testid="work-item-labels-input"]').fill_in(with: label.title)
+ wait_for_requests
+
+ # submit and simulate blur to save
+ send_keys(:enter)
+ find("body").click
+
+ wait_for_requests
+
+ expect(work_item.labels).to include(label)
+ end
+end
+
+RSpec.shared_examples 'work items description' do
+ it 'shows GFM autocomplete', :aggregate_failures do
+ click_button "Edit description"
+
+ find('[aria-label="Description"]').send_keys("@#{user.username}")
+
+ wait_for_requests
+
+ page.within('.atwho-container') do
+ expect(page).to have_text(user.name)
+ end
+ end
+
+ context 'on conflict' do
+ let_it_be(:other_user) { create(:user) }
+ let(:expected_warning) { 'Someone edited the description at the same time you did.' }
+
+ before do
+ project.add_developer(other_user)
+ end
+
+ it 'shows conflict message when description changes', :aggregate_failures do
+ click_button "Edit description"
+
+ wait_for_requests
+
+ ::WorkItems::UpdateService.new(
+ project: work_item.project,
+ current_user: other_user,
+ params: { description: "oh no!" }
+ ).execute(work_item)
+
+ wait_for_requests
+
+ find('[aria-label="Description"]').send_keys("oh yeah!")
+
+ expect(page.find('[data-testid="work-item-description-conflicts"]')).to have_text(expected_warning)
+
+ click_button "Save and overwrite"
+
+ expect(page.find('[data-testid="work-item-description"]')).to have_text("oh yeah!")
+ end
+ end
+end