diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-09-04 15:10:22 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-09-04 15:10:22 +0300 |
commit | 25f102516f8d77c397c768baa2e550779cc9c60b (patch) | |
tree | 1dd1bf83ad767d76933708bf948a7a7ccc6ff1ae /spec | |
parent | d246f33754d73408224d68e81e56fc2bd6f79301 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
13 files changed, 335 insertions, 121 deletions
diff --git a/spec/frontend/admin/abuse_report/components/labels_select_spec.js b/spec/frontend/admin/abuse_report/components/labels_select_spec.js index 35d378b7258..a22dcc18e10 100644 --- a/spec/frontend/admin/abuse_report/components/labels_select_spec.js +++ b/spec/frontend/admin/abuse_report/components/labels_select_spec.js @@ -1,17 +1,20 @@ import MockAdapter from 'axios-mock-adapter'; import { GlButton, GlDropdown, GlDropdownItem, GlLoadingIcon } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_OK, HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status'; import LabelsSelect from '~/admin/abuse_report/components/labels_select.vue'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; -import { stubComponent } from 'helpers/stub_component'; +import { stubComponent, RENDER_ALL_SLOTS_TEMPLATE } from 'helpers/stub_component'; import labelsQuery from '~/admin/abuse_report/components/graphql/abuse_report_labels.query.graphql'; -import DropdownValue from '~/sidebar/components/labels/labels_select_widget/dropdown_value.vue'; import DropdownWidget from '~/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue'; +import DropdownValue from '~/sidebar/components/labels/labels_select_widget/dropdown_value.vue'; +import DropdownHeader from '~/sidebar/components/labels/labels_select_widget/dropdown_header.vue'; +import DropdownContentsCreateView from '~/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view.vue'; +import DropdownFooter from '~/sidebar/components/labels/labels_select_widget/dropdown_footer.vue'; import { createAlert } from '~/alert'; import { mockLabelsQueryResponse, mockLabel1, mockLabel2 } from '../mock_data'; @@ -24,16 +27,20 @@ describe('Labels select component', () => { let wrapper; let fakeApollo; - const selectedText = () => wrapper.find('[data-testid="selected-labels"]').text(); + const selectedText = () => wrapper.findByTestId('selected-labels').text(); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); const findEditButton = () => wrapper.findComponent(GlButton); const findDropdown = () => wrapper.findComponent(DropdownWidget); + const findDropdownHeader = () => wrapper.findComponent(DropdownHeader); const findDropdownValue = () => wrapper.findComponent(DropdownValue); + const findCreateView = () => wrapper.findComponent(DropdownContentsCreateView); + const findDropdownFooter = () => wrapper.findComponent(DropdownFooter); const labelsQueryHandlerSuccess = jest.fn().mockResolvedValue(mockLabelsQueryResponse); const labelsQueryHandlerFailure = jest.fn().mockRejectedValue(new Error()); const updatePath = '/admin/abuse_reports/1'; + const listPath = '/admin/abuse_reports'; async function openLabelsDropdown() { findEditButton().vm.$emit('click'); @@ -47,7 +54,7 @@ describe('Labels select component', () => { const createComponent = ({ props = {}, labelsQueryHandler = labelsQueryHandlerSuccess } = {}) => { fakeApollo = createMockApollo([[labelsQuery, labelsQueryHandler]]); - wrapper = shallowMount(LabelsSelect, { + wrapper = shallowMountExtended(LabelsSelect, { apolloProvider: fakeApollo, propsData: { report: { labels: [] }, @@ -56,11 +63,13 @@ describe('Labels select component', () => { }, provide: { updatePath, + listPath, }, stubs: { GlDropdown, GlDropdownItem, DropdownWidget: stubComponent(DropdownWidget, { + template: RENDER_ALL_SLOTS_TEMPLATE, methods: { showDropdown: jest.fn() }, }), }, @@ -102,9 +111,11 @@ describe('Labels select component', () => { expect(findEditButton().props('disabled')).toEqual(false); }); - it('renders fetched labels in DropdownValue', () => { - expect(findDropdownValue().isVisible()).toBe(true); - expect(findDropdownValue().props('selectedLabels')).toEqual([mockLabel1]); + it('renders fetched DropdownValue with the correct props', () => { + const component = findDropdownValue(); + expect(component.isVisible()).toBe(true); + expect(component.props('selectedLabels')).toEqual([mockLabel1]); + expect(component.props('labelsFilterBasePath')).toBe(listPath); }); }); }); @@ -187,6 +198,53 @@ describe('Labels select component', () => { expect(labelsQueryHandlerSuccess).toHaveBeenCalledTimes(2); expect(labelsQueryHandlerSuccess).toHaveBeenCalledWith({ searchTerm: 'Dos' }); }); + + it('does not render DropdownContentsCreateView', () => { + expect(findCreateView().exists()).toBe(false); + }); + + it('renders DropdownFooter', () => { + expect(findDropdownFooter().props('footerCreateLabelTitle')).toEqual('Create label'); + expect(findDropdownFooter().props('footerManageLabelTitle')).toEqual(''); + }); + + describe('when DropdownHeader emits `toggleDropdownContentsCreateView` event', () => { + beforeEach(() => { + findDropdownHeader().vm.$emit('toggleDropdownContentsCreateView'); + }); + + it('renders DropdownContentsCreateView and removes DropdownFooter', () => { + expect(findCreateView().props('workspaceType')).toEqual('abuseReport'); + expect(findDropdownFooter().exists()).toBe(false); + }); + + describe('when DropdownContentsCreateView emits `hideCreateView` event', () => { + it('removes itself', async () => { + findCreateView().vm.$emit('hideCreateView'); + await nextTick(); + + expect(findCreateView().exists()).toBe(false); + }); + }); + + describe('when DropdownContentsCreateView emits `labelCreated` event', () => { + it('selects created label', async () => { + findCreateView().vm.$emit('labelCreated', mockLabel1); + await nextTick(); + + expect(findDropdownValue().props('selectedLabels')).toEqual([mockLabel1]); + }); + }); + }); + + describe('when DropdownFooter emits `toggleDropdownContentsCreateView` event', () => { + it('renders DropdownContentsCreateView', async () => { + findDropdownFooter().vm.$emit('toggleDropdownContentsCreateView'); + await nextTick(); + + expect(findCreateView().props('workspaceType')).toEqual('abuseReport'); + }); + }); }); describe('after edit', () => { diff --git a/spec/frontend/admin/abuse_report/mock_data.js b/spec/frontend/admin/abuse_report/mock_data.js index 4777e5b9263..ee61eabfa66 100644 --- a/spec/frontend/admin/abuse_report/mock_data.js +++ b/spec/frontend/admin/abuse_report/mock_data.js @@ -76,7 +76,7 @@ export const mockAbuseReport = { }; export const mockLabel1 = { - id: 'gid://gitlab/AbuseReportLabel/1', + id: 'gid://gitlab/Admin::AbuseReportLabel/1', title: 'Uno', color: '#F0AD4E', textColor: '#FFFFFF', @@ -84,7 +84,7 @@ export const mockLabel1 = { }; export const mockLabel2 = { - id: 'gid://gitlab/AbuseReportLabel/2', + id: 'gid://gitlab/Admin::AbuseReportLabel/2', title: 'Dos', color: '#F0AD4E', textColor: '#FFFFFF', @@ -93,7 +93,7 @@ export const mockLabel2 = { export const mockLabelsQueryResponse = { data: { - abuseReportLabels: { + labels: { nodes: [mockLabel1, mockLabel2], __typename: 'LabelConnection', }, @@ -111,3 +111,20 @@ export const mockReportQueryResponse = { }, }, }; + +export const mockCreateLabelResponse = { + data: { + labelCreate: { + label: { + id: 'gid://gitlab/Admin::AbuseReportLabel/1', + color: '#ed9121', + description: null, + title: 'abuse report label', + textColor: '#FFFFFF', + __typename: 'Label', + }, + errors: [], + __typename: 'AbuseReportLabelCreatePayload', + }, + }, +}; diff --git a/spec/frontend/environments/edit_environment_spec.js b/spec/frontend/environments/edit_environment_spec.js index b55bbb34c65..9989c946800 100644 --- a/spec/frontend/environments/edit_environment_spec.js +++ b/spec/frontend/environments/edit_environment_spec.js @@ -7,7 +7,6 @@ import EditEnvironment from '~/environments/components/edit_environment.vue'; import { createAlert } from '~/alert'; import { visitUrl } from '~/lib/utils/url_utility'; import getEnvironment from '~/environments/graphql/queries/environment.query.graphql'; -import getEnvironmentWithFluxResource from '~/environments/graphql/queries/environment_with_flux_resource.query.graphql'; import updateEnvironment from '~/environments/graphql/mutations/update_environment.mutation.graphql'; import { __ } from '~/locale'; import createMockApollo from '../__helpers__/mock_apollo_helper'; @@ -44,9 +43,6 @@ describe('~/environments/components/edit.vue', () => { let wrapper; const getEnvironmentQuery = jest.fn().mockResolvedValue({ data: resolvedEnvironment }); - const getEnvironmentWithFluxResourceQuery = jest - .fn() - .mockResolvedValue({ data: resolvedEnvironment }); const updateEnvironmentSuccess = jest .fn() @@ -60,24 +56,17 @@ describe('~/environments/components/edit.vue', () => { const mocks = [ [getEnvironment, getEnvironmentQuery], - [getEnvironmentWithFluxResource, getEnvironmentWithFluxResourceQuery], [updateEnvironment, mutationHandler], ]; return createMockApollo(mocks); }; - const createWrapperWithApollo = async ({ - mutationHandler = updateEnvironmentSuccess, - fluxResourceForEnvironment = false, - } = {}) => { + const createWrapperWithApollo = async ({ mutationHandler = updateEnvironmentSuccess } = {}) => { wrapper = mountExtended(EditEnvironment, { propsData: { environment: {} }, provide: { ...provide, - glFeatures: { - fluxResourceForEnvironment, - }, }, apolloProvider: createMockApolloProvider(mutationHandler), }); @@ -170,11 +159,4 @@ describe('~/environments/components/edit.vue', () => { }); }); }); - - describe('when `fluxResourceForEnvironment` is enabled', () => { - it('calls the `getEnvironmentWithFluxResource` query', () => { - createWrapperWithApollo({ fluxResourceForEnvironment: true }); - expect(getEnvironmentWithFluxResourceQuery).toHaveBeenCalled(); - }); - }); }); diff --git a/spec/frontend/environments/environment_form_spec.js b/spec/frontend/environments/environment_form_spec.js index 1b80b596db7..22dd7437d82 100644 --- a/spec/frontend/environments/environment_form_spec.js +++ b/spec/frontend/environments/environment_form_spec.js @@ -53,11 +53,7 @@ describe('~/environments/components/form.vue', () => { }, }); - const createWrapperWithApollo = ({ - propsData = {}, - fluxResourceForEnvironment = false, - queryResult = null, - } = {}) => { + const createWrapperWithApollo = ({ propsData = {}, queryResult = null } = {}) => { Vue.use(VueApollo); const requestHandlers = [ @@ -83,9 +79,6 @@ describe('~/environments/components/form.vue', () => { return mountExtended(EnvironmentForm, { provide: { ...PROVIDE, - glFeatures: { - fluxResourceForEnvironment, - }, }, propsData: { ...DEFAULT_PROPS, @@ -422,39 +415,30 @@ describe('~/environments/components/form.vue', () => { }); describe('flux resource selector', () => { - it("doesn't render if `fluxResourceForEnvironment` feature flag is disabled", () => { + beforeEach(() => { wrapper = createWrapperWithApollo(); + }); + + it("doesn't render flux resource selector by default", () => { expect(findFluxResourceSelector().exists()).toBe(false); }); - describe('when `fluxResourceForEnvironment` feature flag is enabled', () => { - beforeEach(() => { - wrapper = createWrapperWithApollo({ - fluxResourceForEnvironment: true, - }); + describe('when the agent was selected', () => { + beforeEach(async () => { + await selectAgent(); }); - it("doesn't render flux resource selector by default", () => { + it("doesn't render flux resource selector", () => { expect(findFluxResourceSelector().exists()).toBe(false); }); - describe('when the agent was selected', () => { - beforeEach(async () => { - await selectAgent(); - }); - - it("doesn't render flux resource selector", () => { - expect(findFluxResourceSelector().exists()).toBe(false); - }); - - it('renders the flux resource selector when the namespace is selected', async () => { - await findNamespaceSelector().vm.$emit('select', 'agent'); + it('renders the flux resource selector when the namespace is selected', async () => { + await findNamespaceSelector().vm.$emit('select', 'agent'); - expect(findFluxResourceSelector().props()).toEqual({ - namespace: 'agent', - fluxResourcePath: '', - configuration, - }); + expect(findFluxResourceSelector().props()).toEqual({ + namespace: 'agent', + fluxResourcePath: '', + configuration, }); }); }); @@ -522,7 +506,6 @@ describe('~/environments/components/form.vue', () => { beforeEach(() => { wrapper = createWrapperWithApollo({ propsData: { environment: environmentWithAgentAndNamespace }, - fluxResourceForEnvironment: true, }); }); diff --git a/spec/frontend/environments/new_environment_item_spec.js b/spec/frontend/environments/new_environment_item_spec.js index bfcc4f4ebb6..7ee31bf2c62 100644 --- a/spec/frontend/environments/new_environment_item_spec.js +++ b/spec/frontend/environments/new_environment_item_spec.js @@ -13,7 +13,6 @@ import Deployment from '~/environments/components/deployment.vue'; import DeployBoardWrapper from '~/environments/components/deploy_board_wrapper.vue'; import KubernetesOverview from '~/environments/components/kubernetes_overview.vue'; import getEnvironmentClusterAgent from '~/environments/graphql/queries/environment_cluster_agent.query.graphql'; -import getEnvironmentClusterAgentWithFluxResource from '~/environments/graphql/queries/environment_cluster_agent_with_flux_resource.query.graphql'; import { resolvedEnvironment, rolloutStatus, @@ -27,7 +26,6 @@ Vue.use(VueApollo); describe('~/environments/components/new_environment_item.vue', () => { let wrapper; let queryResponseHandler; - let queryWithFluxResourceResponseHandler; const projectPath = '/1'; @@ -39,27 +37,14 @@ describe('~/environments/components/new_environment_item.vue', () => { environment: { id: '1', kubernetesNamespace: 'default', + fluxResourcePath: fluxResourcePathMock, clusterAgent, }, }, }, }; queryResponseHandler = jest.fn().mockResolvedValue(response); - queryWithFluxResourceResponseHandler = jest.fn().mockResolvedValue({ - data: { - project: { - id: response.data.project.id, - environment: { - ...response.data.project.environment, - fluxResourcePath: fluxResourcePathMock, - }, - }, - }, - }); - return createMockApollo([ - [getEnvironmentClusterAgent, queryResponseHandler], - [getEnvironmentClusterAgentWithFluxResource, queryWithFluxResourceResponseHandler], - ]); + return createMockApollo([[getEnvironmentClusterAgent, queryResponseHandler]]); }; const createWrapper = ({ propsData = {}, provideData = {}, apolloProvider } = {}) => @@ -554,25 +539,6 @@ describe('~/environments/components/new_environment_item.vue', () => { }); }); - it('should request agent data with Flux resource when `fluxResourceForEnvironment` feature flag is enabled', async () => { - wrapper = createWrapper({ - propsData: { environment: resolvedEnvironment }, - provideData: { - glFeatures: { - fluxResourceForEnvironment: true, - }, - }, - apolloProvider: createApolloProvider(agent), - }); - - await expandCollapsedSection(); - - expect(queryWithFluxResourceResponseHandler).toHaveBeenCalledWith({ - environmentName: resolvedEnvironment.name, - projectFullPath: projectPath, - }); - }); - it('should render if the environment has an agent associated', async () => { wrapper = createWrapper({ propsData: { environment: resolvedEnvironment }, @@ -588,14 +554,9 @@ describe('~/environments/components/new_environment_item.vue', () => { }); }); - it('should render with the namespace if `fluxResourceForEnvironment` feature flag is enabled and the environment has an agent associated', async () => { + it('should render with the namespace if the environment has an agent associated', async () => { wrapper = createWrapper({ propsData: { environment: resolvedEnvironment }, - provideData: { - glFeatures: { - fluxResourceForEnvironment: true, - }, - }, apolloProvider: createApolloProvider(agent), }); diff --git a/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view_spec.js b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view_spec.js index 6de113ba1f9..5e2ff73878f 100644 --- a/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view_spec.js +++ b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view_spec.js @@ -5,11 +5,14 @@ import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import { createAlert } from '~/alert'; -import { workspaceLabelsQueries } from '~/sidebar/queries/constants'; +import { workspaceLabelsQueries, workspaceCreateLabelMutation } from '~/sidebar/queries/constants'; import DropdownContentsCreateView from '~/sidebar/components/labels/labels_select_widget/dropdown_contents_create_view.vue'; -import createLabelMutation from '~/sidebar/components/labels/labels_select_widget/graphql/create_label.mutation.graphql'; import { DEFAULT_LABEL_COLOR } from '~/sidebar/components/labels/labels_select_widget/constants'; import { + mockCreateLabelResponse as createAbuseReportLabelSuccessfulResponse, + mockLabelsQueryResponse as abuseReportLabelsQueryResponse, +} from '../../../../admin/abuse_report/mock_data'; +import { mockRegularLabel, mockSuggestedColors, createLabelSuccessfulResponse, @@ -38,6 +41,9 @@ const titleTakenError = { }; const createLabelSuccessHandler = jest.fn().mockResolvedValue(createLabelSuccessfulResponse); +const createAbuseReportLabelSuccessHandler = jest + .fn() + .mockResolvedValue(createAbuseReportLabelSuccessfulResponse); const createLabelUserRecoverableErrorHandler = jest.fn().mockResolvedValue(userRecoverableError); const createLabelDuplicateErrorHandler = jest.fn().mockResolvedValue(titleTakenError); const createLabelErrorHandler = jest.fn().mockRejectedValue('Houston, we have a problem'); @@ -66,6 +72,7 @@ describe('DropdownContentsCreateView', () => { labelsResponse = workspaceLabelsQueryResponse, searchTerm = '', } = {}) => { + const createLabelMutation = workspaceCreateLabelMutation[workspaceType]; const mockApollo = createMockApollo([[createLabelMutation, mutationHandler]]); mockApollo.clients.defaultClient.cache.writeQuery({ query: workspaceLabelsQueries[workspaceType].query, @@ -203,6 +210,22 @@ describe('DropdownContentsCreateView', () => { }); }); + it('calls the correct mutation when workspaceType is `abuseReport`', () => { + createComponent({ + mutationHandler: createAbuseReportLabelSuccessHandler, + labelCreateType: '', + workspaceType: 'abuseReport', + labelsResponse: abuseReportLabelsQueryResponse, + }); + fillLabelAttributes(); + findCreateButton().vm.$emit('click'); + + expect(createAbuseReportLabelSuccessHandler).toHaveBeenCalledWith({ + color: '#009966', + title: 'Test title', + }); + }); + it('calls createAlert is mutation has a user-recoverable error', async () => { createComponent({ mutationHandler: createLabelUserRecoverableErrorHandler }); fillLabelAttributes(); diff --git a/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_footer_spec.js b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_footer_spec.js index ad1edaa6671..7a1131b8cce 100644 --- a/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_footer_spec.js +++ b/spec/frontend/sidebar/components/labels/labels_select_widget/dropdown_footer_spec.js @@ -1,12 +1,11 @@ -import { shallowMount } from '@vue/test-utils'; -import { nextTick } from 'vue'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import DropdownFooter from '~/sidebar/components/labels/labels_select_widget/dropdown_footer.vue'; describe('DropdownFooter', () => { let wrapper; const createComponent = ({ props = {}, injected = {} } = {}) => { - wrapper = shallowMount(DropdownFooter, { + wrapper = shallowMountExtended(DropdownFooter, { propsData: { footerCreateLabelTitle: 'create', footerManageLabelTitle: 'manage', @@ -20,7 +19,8 @@ describe('DropdownFooter', () => { }); }; - const findCreateLabelButton = () => wrapper.find('[data-testid="create-label-button"]'); + const findCreateLabelButton = () => wrapper.findByTestId('create-label-button'); + const findManageLabelsButton = () => wrapper.findByTestId('manage-labels-button'); describe('Labels view', () => { beforeEach(() => { @@ -42,12 +42,37 @@ describe('DropdownFooter', () => { expect(findCreateLabelButton().exists()).toBe(true); }); - it('emits `toggleDropdownContentsCreateView` event on create label button click', async () => { + it('emits `toggleDropdownContentsCreateView` event on create label button click', () => { findCreateLabelButton().trigger('click'); - await nextTick(); expect(wrapper.emitted('toggleDropdownContentsCreateView')).toEqual([[]]); }); }); + + describe('manage labels button', () => { + it('is rendered', () => { + expect(findManageLabelsButton().exists()).toBe(true); + }); + + describe('when footerManageLabelTitle is not given', () => { + beforeEach(() => { + createComponent({ props: { footerManageLabelTitle: undefined } }); + }); + + it('does not render manage labels button', () => { + expect(findManageLabelsButton().exists()).toBe(false); + }); + }); + + describe('when labelsManagePath is not provided', () => { + beforeEach(() => { + createComponent({ injected: { labelsManagePath: '' } }); + }); + + it('does not render manage labels button', () => { + expect(findManageLabelsButton().exists()).toBe(false); + }); + }); + }); }); }); diff --git a/spec/frontend/vue_merge_request_widget/components/widget/app_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/app_spec.js index bf318cd6b88..205824c3edd 100644 --- a/spec/frontend/vue_merge_request_widget/components/widget/app_spec.js +++ b/spec/frontend/vue_merge_request_widget/components/widget/app_spec.js @@ -1,13 +1,23 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import waitForPromises from 'helpers/wait_for_promises'; import App from '~/vue_merge_request_widget/components/widget/app.vue'; +import MrSecurityWidgetCE from '~/vue_merge_request_widget/extensions/security_reports/mr_widget_security_reports.vue'; +import MrTestReportWidget from '~/vue_merge_request_widget/extensions/test_report/index.vue'; +import MrTerraformWidget from '~/vue_merge_request_widget/extensions/terraform/index.vue'; +import MrCodeQualityWidget from '~/vue_merge_request_widget/extensions/code_quality/index.vue'; describe('MR Widget App', () => { let wrapper; - const createComponent = () => { + const createComponent = ({ mr = {} } = {}) => { wrapper = shallowMountExtended(App, { propsData: { - mr: {}, + mr: { + pipeline: { + path: '/path/to/pipeline', + }, + ...mr, + }, }, }); }; @@ -16,4 +26,35 @@ describe('MR Widget App', () => { createComponent(); expect(wrapper.findByTestId('mr-widget-app').exists()).toBe(true); }); + + describe('MRSecurityWidget', () => { + it('mounts MrSecurityWidgetCE', async () => { + createComponent(); + + await waitForPromises(); + + expect(wrapper.findComponent(MrSecurityWidgetCE).exists()).toBe(true); + }); + }); + + describe.each` + widgetName | widget | endpoint + ${'testReportWidget'} | ${MrTestReportWidget} | ${'testResultsPath'} + ${'terraformPlansWidget'} | ${MrTerraformWidget} | ${'terraformReportsPath'} + ${'codeQualityWidget'} | ${MrCodeQualityWidget} | ${'codequalityReportsPath'} + `('$widgetName', ({ widget, endpoint }) => { + it(`is mounted when ${endpoint} is defined`, async () => { + createComponent({ mr: { [endpoint]: `path/to/${endpoint}` } }); + await waitForPromises(); + + expect(wrapper.findComponent(widget).exists()).toBe(true); + }); + + it(`is not mounted when ${endpoint} is not defined`, async () => { + createComponent(); + await waitForPromises(); + + expect(wrapper.findComponent(widget).exists()).toBe(false); + }); + }); }); diff --git a/spec/frontend/vue_shared/components/__snapshots__/source_editor_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/source_editor_spec.js.snap index d60fc4cb043..76deb4d0b36 100644 --- a/spec/frontend/vue_shared/components/__snapshots__/source_editor_spec.js.snap +++ b/spec/frontend/vue_shared/components/__snapshots__/source_editor_spec.js.snap @@ -3,7 +3,7 @@ exports[`Source Editor component rendering matches the snapshot 1`] = ` <div data-editor-loading="" - data-qa-selector="source_editor_container" + data-testid="source-editor-container" id="reference-0" > <pre diff --git a/spec/helpers/admin/abuse_reports_helper_spec.rb b/spec/helpers/admin/abuse_reports_helper_spec.rb index 6a7630dc76a..d6f102682ba 100644 --- a/spec/helpers/admin/abuse_reports_helper_spec.rb +++ b/spec/helpers/admin/abuse_reports_helper_spec.rb @@ -25,10 +25,14 @@ RSpec.describe Admin::AbuseReportsHelper, feature_category: :insider_threat do describe '#abuse_report_data' do let(:report) { build_stubbed(:abuse_report) } - subject(:data) { helper.abuse_report_data(report)[:abuse_report_data] } + subject(:data) { helper.abuse_report_data(report) } it 'has the expected attributes' do - expect(data).to include('user', 'reporter', 'report') + expect(data[:abuse_report_data]).to include('user', 'reporter', 'report') + end + + it 'includes path to abuse reports list page' do + expect(data[:abuse_reports_list_path]).to eq admin_abuse_reports_path end end end diff --git a/spec/requests/api/graphql/mutations/admin/abuse_report_labels/create_spec.rb b/spec/requests/api/graphql/mutations/admin/abuse_report_labels/create_spec.rb new file mode 100644 index 00000000000..2a20d96d9c8 --- /dev/null +++ b/spec/requests/api/graphql/mutations/admin/abuse_report_labels/create_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Mutations::Admin::AbuseReportLabels::Create, feature_category: :insider_threat do + include GraphqlHelpers + + let(:params) do + { + 'title' => 'foo', + 'color' => '#FF0000' + } + end + + let(:mutation) { graphql_mutation(:abuse_report_label_create, params) } + + subject { post_graphql_mutation(mutation, current_user: current_user) } + + def mutation_response + graphql_mutation_response(:abuse_report_label_create) + end + + context 'when the user does not have permission to create a label', :enable_admin_mode do + let_it_be(:current_user) { create(:user) } + + it_behaves_like 'a mutation that returns a top-level access error' + + it 'does not create the label' do + expect { subject }.not_to change { Admin::AbuseReportLabel.count } + end + end + + context 'when the user has permission to create a label', :enable_admin_mode do + let_it_be(:current_user) { create(:admin) } + + it 'creates the label' do + expect { subject }.to change { Admin::AbuseReportLabel.count }.to(1) + + expect(mutation_response).to include('label' => a_hash_including(params)) + end + + context 'when there are errors' do + it 'does not create the label', :aggregate_failures do + create(:abuse_report_label, title: params['title']) + + expect { subject }.not_to change { Label.count } + + expect(mutation_response).to include({ + 'label' => nil, + 'errors' => ['Title has already been taken'] + }) + end + end + end +end diff --git a/spec/services/admin/abuse_report_labels/create_service_spec.rb b/spec/services/admin/abuse_report_labels/create_service_spec.rb new file mode 100644 index 00000000000..168229d6ed9 --- /dev/null +++ b/spec/services/admin/abuse_report_labels/create_service_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Admin::AbuseReportLabels::CreateService, feature_category: :insider_threat do + describe '#execute' do + let(:color) { 'red' } + let(:color_in_hex) { ::Gitlab::Color.of(color) } + let(:params) { { title: 'FancyLabel', color: color } } + + subject(:execute) { described_class.new(params).execute } + + shared_examples 'creates a label with the correct values' do + it 'creates a label with the correct values', :aggregate_failures do + expect { execute }.to change { Admin::AbuseReportLabel.count }.from(0).to(1) + + label = Admin::AbuseReportLabel.last + expect(label.title).to eq params[:title] + expect(label.color).to eq color_in_hex + end + + it 'returns the persisted label' do + result = execute + expect(result).to be_an_instance_of(Admin::AbuseReportLabel) + expect(result.persisted?).to eq true + end + end + + it_behaves_like 'creates a label with the correct values' + + context 'without color param' do + let(:params) { { title: 'FancyLabel' } } + let(:color_in_hex) { ::Gitlab::Color.of(Label::DEFAULT_COLOR) } + + it_behaves_like 'creates a label with the correct values' + end + + context 'with errors' do + let!(:existing_label) { create(:abuse_report_label, title: params[:title]) } + + it 'does not create the label' do + expect { execute }.not_to change { Admin::AbuseReportLabel.count } + end + + it 'returns the label with errors' do + label = execute + expect(label.errors.messages).to include({ title: ["has already been taken"] }) + end + end + end +end diff --git a/spec/services/gpg_keys/destroy_service_spec.rb b/spec/services/gpg_keys/destroy_service_spec.rb index b9aa3e351c9..85c1fc2893b 100644 --- a/spec/services/gpg_keys/destroy_service_spec.rb +++ b/spec/services/gpg_keys/destroy_service_spec.rb @@ -2,14 +2,28 @@ require 'spec_helper' -RSpec.describe GpgKeys::DestroyService do - let(:user) { create(:user) } +RSpec.describe GpgKeys::DestroyService, feature_category: :source_code_management do + let_it_be(:user) { create(:user) } + let_it_be(:gpg_key) { create(:gpg_key) } subject { described_class.new(user) } it 'destroys the GPG key' do - gpg_key = create(:gpg_key) - expect { subject.execute(gpg_key) }.to change(GpgKey, :count).by(-1) end + + it 'nullifies the related signatures in batches' do + stub_const("#{described_class}::BATCH_SIZE", 1) + + first_signature = create(:gpg_signature, gpg_key: gpg_key) + second_signature = create(:gpg_signature, gpg_key: gpg_key) + third_signature = create(:gpg_signature, gpg_key: create(:another_gpg_key)) + + control = ActiveRecord::QueryRecorder.new { subject.execute(gpg_key) } + expect(control.count).to eq(5) + + expect(first_signature.reload.gpg_key).to be_nil + expect(second_signature.reload.gpg_key).to be_nil + expect(third_signature.reload.gpg_key).not_to be_nil + end end |