diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-25 18:09:25 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-01-25 18:09:25 +0300 |
commit | ec558ad8ed732ff6f8a89aa3651eb92c27c50deb (patch) | |
tree | e6d0eb5946758007594e8bf6feb5fb9759f48d6c /spec | |
parent | 7b1fa4c1a1b784c2f78405dca82e56a009f1e773 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
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 |