diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-03-07 12:08:26 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-03-07 12:08:26 +0300 |
commit | 47da68850624438b5b5b9ff9d648b1e83282c446 (patch) | |
tree | 95f31cd727324ad14cc64e5e5d8b41830cecefe8 /spec | |
parent | ed922e61f47716d82396490b0a19d794c997b9c5 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
29 files changed, 375 insertions, 78 deletions
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb index 530b4616765..2a21bde5436 100644 --- a/spec/factories/notes.rb +++ b/spec/factories/notes.rb @@ -12,6 +12,7 @@ FactoryBot.define do factory :note_on_commit, traits: [:on_commit] factory :note_on_issue, traits: [:on_issue], aliases: [:votable_note] + factory :note_on_work_item, traits: [:on_work_item] factory :note_on_merge_request, traits: [:on_merge_request] factory :note_on_project_snippet, traits: [:on_project_snippet] factory :note_on_personal_snippet, traits: [:on_personal_snippet] @@ -122,6 +123,10 @@ FactoryBot.define do noteable { association(:issue, project: project) } end + trait :on_work_item do + noteable { association(:work_item, project: project) } + end + trait :on_merge_request do noteable { association(:merge_request, source_project: project) } end diff --git a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js index a15c78cc456..11a16ade9ed 100644 --- a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js +++ b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js @@ -30,7 +30,7 @@ import { INTEGRATION_INACTIVE_PAYLOAD_TEST_ERROR, DELETE_INTEGRATION_ERROR, } from '~/alerts_settings/utils/error_messages'; -import { createAlert, VARIANT_SUCCESS } from '~/flash'; +import { createAlert, VARIANT_SUCCESS } from '~/alert'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_FORBIDDEN, HTTP_STATUS_UNPROCESSABLE_ENTITY } from '~/lib/utils/http_status'; import { @@ -48,7 +48,7 @@ import { } from './mocks/apollo_mock'; import mockIntegrations from './mocks/integrations.json'; -jest.mock('~/flash'); +jest.mock('~/alert'); describe('AlertsSettingsWrapper', () => { let wrapper; @@ -478,7 +478,7 @@ describe('AlertsSettingsWrapper', () => { expect(destroyIntegrationHandler).toHaveBeenCalled(); }); - it('displays flash if mutation had a recoverable error', async () => { + it('displays alert if mutation had a recoverable error', async () => { createComponentWithApollo({ destroyHandler: jest.fn().mockResolvedValue(destroyIntegrationResponseWithErrors), }); @@ -489,7 +489,7 @@ describe('AlertsSettingsWrapper', () => { expect(createAlert).toHaveBeenCalledWith({ message: 'Houston, we have a problem' }); }); - it('displays flash if mutation had a non-recoverable error', async () => { + it('displays alert if mutation had a non-recoverable error', async () => { createComponentWithApollo({ destroyHandler: jest.fn().mockRejectedValue('Error'), }); diff --git a/spec/frontend/artifacts/components/artifacts_table_row_details_spec.js b/spec/frontend/artifacts/components/artifacts_table_row_details_spec.js index 87bf1be51c6..eced6d3f3ba 100644 --- a/spec/frontend/artifacts/components/artifacts_table_row_details_spec.js +++ b/spec/frontend/artifacts/components/artifacts_table_row_details_spec.js @@ -10,9 +10,9 @@ import createMockApollo from 'helpers/mock_apollo_helper'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import destroyArtifactMutation from '~/artifacts/graphql/mutations/destroy_artifact.mutation.graphql'; import { I18N_DESTROY_ERROR, I18N_MODAL_TITLE } from '~/artifacts/constants'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; -jest.mock('~/flash'); +jest.mock('~/alert'); const { artifacts } = getJobArtifactsResponse.data.project.jobs.nodes[0]; const refetchArtifacts = jest.fn(); @@ -88,7 +88,7 @@ describe('ArtifactsTableRowDetails component', () => { }); }); - it('displays a flash message and refetches artifacts when the mutation fails', async () => { + it('displays an alert message and refetches artifacts when the mutation fails', async () => { createComponent({ destroyArtifactMutation: jest.fn().mockRejectedValue(new Error('Error!')), }); diff --git a/spec/frontend/artifacts/components/job_artifacts_table_spec.js b/spec/frontend/artifacts/components/job_artifacts_table_spec.js index 9919ea5ad2a..790b082c10b 100644 --- a/spec/frontend/artifacts/components/job_artifacts_table_spec.js +++ b/spec/frontend/artifacts/components/job_artifacts_table_spec.js @@ -19,9 +19,9 @@ import { INITIAL_CURRENT_PAGE, } from '~/artifacts/constants'; import { totalArtifactsSizeForJob } from '~/artifacts/utils'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; -jest.mock('~/flash'); +jest.mock('~/alert'); Vue.use(VueApollo); diff --git a/spec/frontend/error_tracking/components/error_details_spec.js b/spec/frontend/error_tracking/components/error_details_spec.js index 9d6e46be8c4..3bfade12d27 100644 --- a/spec/frontend/error_tracking/components/error_details_spec.js +++ b/spec/frontend/error_tracking/components/error_details_spec.js @@ -18,11 +18,11 @@ import { trackErrorDetailsViewsOptions, trackErrorStatusUpdateOptions, } from '~/error_tracking/utils'; -import { createAlert, VARIANT_WARNING } from '~/flash'; +import { createAlert, VARIANT_WARNING } from '~/alert'; import { __ } from '~/locale'; import Tracking from '~/tracking'; -jest.mock('~/flash'); +jest.mock('~/alert'); Vue.use(Vuex); @@ -148,7 +148,7 @@ describe('ErrorDetails', () => { expect(mocks.$apollo.queries.error.stopPolling).not.toHaveBeenCalled(); }); - it('when timeout is hit and no apollo result, stops loading and shows flash', async () => { + it('when timeout is hit and no apollo result, stops loading and shows alert', async () => { Date.now.mockReturnValue(endTime + 1); wrapper.vm.onNoApolloResult(); diff --git a/spec/frontend/incidents_settings/components/incidents_settings_service_spec.js b/spec/frontend/incidents_settings/components/incidents_settings_service_spec.js index 1d1b285c1b6..c5c29b4bb19 100644 --- a/spec/frontend/incidents_settings/components/incidents_settings_service_spec.js +++ b/spec/frontend/incidents_settings/components/incidents_settings_service_spec.js @@ -1,12 +1,12 @@ import AxiosMockAdapter from 'axios-mock-adapter'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { ERROR_MSG } from '~/incidents_settings/constants'; import IncidentsSettingsService from '~/incidents_settings/incidents_settings_service'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status'; import { refreshCurrentPage } from '~/lib/utils/url_utility'; -jest.mock('~/flash'); +jest.mock('~/alert'); jest.mock('~/lib/utils/url_utility'); describe('IncidentsSettingsService', () => { @@ -33,7 +33,7 @@ describe('IncidentsSettingsService', () => { }); }); - it('should display a flash message on update error', () => { + it('should display an alert message on update error', () => { mock.onPatch().reply(HTTP_STATUS_BAD_REQUEST); return service.updateSettings({}).then(() => { diff --git a/spec/frontend/issues/show/components/header_actions_spec.js b/spec/frontend/issues/show/components/header_actions_spec.js index 54d8cceb523..00e6a6f8e90 100644 --- a/spec/frontend/issues/show/components/header_actions_spec.js +++ b/spec/frontend/issues/show/components/header_actions_spec.js @@ -4,7 +4,7 @@ import { shallowMount } from '@vue/test-utils'; import Vuex from 'vuex'; import { mockTracking } from 'helpers/tracking_helper'; import { createAlert, VARIANT_SUCCESS } from '~/flash'; -import { IssueType, STATUS_CLOSED, STATUS_OPEN, TYPE_INCIDENT } from '~/issues/constants'; +import { STATUS_CLOSED, STATUS_OPEN, TYPE_INCIDENT, TYPE_ISSUE } from '~/issues/constants'; import DeleteIssueModal from '~/issues/show/components/delete_issue_modal.vue'; import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue'; import HeaderActions from '~/issues/show/components/header_actions.vue'; @@ -36,7 +36,7 @@ describe('HeaderActions component', () => { iid: '32', isIssueAuthor: true, issuePath: 'gitlab-org/gitlab-test/-/issues/1', - issueType: IssueType.Issue, + issueType: TYPE_ISSUE, newIssuePath: 'gitlab-org/gitlab-test/-/issues/new', projectPath: 'gitlab-org/gitlab-test', reportAbusePath: '-/abuse_reports/add_category', @@ -118,7 +118,7 @@ describe('HeaderActions component', () => { describe.each` issueType - ${IssueType.Issue} + ${TYPE_ISSUE} ${TYPE_INCIDENT} `('when issue type is $issueType', ({ issueType }) => { describe('close/reopen button', () => { diff --git a/spec/frontend/releases/components/app_index_spec.js b/spec/frontend/releases/components/app_index_spec.js index ef3bd5ca873..7a0e9fb7326 100644 --- a/spec/frontend/releases/components/app_index_spec.js +++ b/spec/frontend/releases/components/app_index_spec.js @@ -6,7 +6,7 @@ import createMockApollo from 'helpers/mock_apollo_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; import allReleasesQuery from '~/releases/graphql/queries/all_releases.query.graphql'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { historyPushState } from '~/lib/utils/common_utils'; import { sprintf, __ } from '~/locale'; import ReleasesIndexApp from '~/releases/components/app_index.vue'; @@ -20,7 +20,7 @@ import { deleteReleaseSessionKey } from '~/releases/util'; Vue.use(VueApollo); -jest.mock('~/flash'); +jest.mock('~/alert'); let mockQueryParams; jest.mock('~/lib/utils/common_utils', () => ({ @@ -114,7 +114,7 @@ describe('app_index.vue', () => { const toDescription = (bool) => (bool ? 'does' : 'does not'); describe.each` - description | singleResponseFn | fullResponseFn | loadingIndicator | emptyState | flashMessage | releaseCount | pagination + description | singleResponseFn | fullResponseFn | loadingIndicator | emptyState | alertMessage | releaseCount | pagination ${'both requests loading'} | ${getInProgressResponse} | ${getInProgressResponse} | ${true} | ${false} | ${false} | ${0} | ${false} ${'both requests failed'} | ${getErrorResponse} | ${getErrorResponse} | ${false} | ${false} | ${true} | ${0} | ${false} ${'both requests loaded'} | ${getSingleRequestLoadedResponse} | ${getFullRequestLoadedResponse} | ${false} | ${false} | ${false} | ${2} | ${true} @@ -134,7 +134,7 @@ describe('app_index.vue', () => { fullResponseFn, loadingIndicator, emptyState, - flashMessage, + alertMessage, releaseCount, pagination, }) => { @@ -154,9 +154,9 @@ describe('app_index.vue', () => { expect(findEmptyState().exists()).toBe(emptyState); }); - it(`${toDescription(flashMessage)} show a flash message`, async () => { + it(`${toDescription(alertMessage)} show a flash message`, async () => { await waitForPromises(); - if (flashMessage) { + if (alertMessage) { expect(createAlert).toHaveBeenCalledWith({ message: ReleasesIndexApp.i18n.errorMessage, captureError: true, diff --git a/spec/frontend/releases/components/app_show_spec.js b/spec/frontend/releases/components/app_show_spec.js index efe72e8000a..d8e9ff8c59d 100644 --- a/spec/frontend/releases/components/app_show_spec.js +++ b/spec/frontend/releases/components/app_show_spec.js @@ -4,14 +4,14 @@ import VueApollo from 'vue-apollo'; import oneReleaseQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release.query.graphql.json'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { popCreateReleaseNotification } from '~/releases/release_notification_service'; import ReleaseShowApp from '~/releases/components/app_show.vue'; import ReleaseBlock from '~/releases/components/release_block.vue'; import ReleaseSkeletonLoader from '~/releases/components/release_skeleton_loader.vue'; import oneReleaseQuery from '~/releases/graphql/queries/one_release.query.graphql'; -jest.mock('~/flash'); +jest.mock('~/alert'); jest.mock('~/releases/release_notification_service'); Vue.use(VueApollo); @@ -54,13 +54,13 @@ describe('Release show component', () => { }; const expectNoFlash = () => { - it('does not show a flash message', () => { + it('does not show an alert message', () => { expect(createAlert).not.toHaveBeenCalled(); }); }; const expectFlashWithMessage = (message) => { - it(`shows a flash message that reads "${message}"`, () => { + it(`shows an alert message that reads "${message}"`, () => { expect(createAlert).toHaveBeenCalledWith({ message, captureError: true, @@ -152,7 +152,7 @@ describe('Release show component', () => { beforeEach(async () => { // As we return a release as `null`, Apollo also throws an error to the console // about the missing field. We need to suppress console.error in order to check - // that flash message was called + // that alert message was called // eslint-disable-next-line no-console console.error = jest.fn(); diff --git a/spec/frontend/releases/release_notification_service_spec.js b/spec/frontend/releases/release_notification_service_spec.js index 2344d4b929a..a90bfa3dcbd 100644 --- a/spec/frontend/releases/release_notification_service_spec.js +++ b/spec/frontend/releases/release_notification_service_spec.js @@ -2,9 +2,9 @@ import { popCreateReleaseNotification, putCreateReleaseNotification, } from '~/releases/release_notification_service'; -import { createAlert, VARIANT_SUCCESS } from '~/flash'; +import { createAlert, VARIANT_SUCCESS } from '~/alert'; -jest.mock('~/flash'); +jest.mock('~/alert'); describe('~/releases/release_notification_service', () => { const projectPath = 'test-project-path'; @@ -35,7 +35,7 @@ describe('~/releases/release_notification_service', () => { expect(item).toBe(null); }); - it('should create a flash message', () => { + it('should create an alert message', () => { expect(createAlert).toHaveBeenCalledTimes(1); expect(createAlert).toHaveBeenCalledWith({ message: `Release ${releaseName} has been successfully created.`, @@ -49,7 +49,7 @@ describe('~/releases/release_notification_service', () => { popCreateReleaseNotification(projectPath); }); - it('should not create a flash message', () => { + it('should not create an alert message', () => { expect(createAlert).toHaveBeenCalledTimes(0); }); }); diff --git a/spec/frontend/releases/stores/modules/detail/actions_spec.js b/spec/frontend/releases/stores/modules/detail/actions_spec.js index ca3b2d5f734..2fca3396a1f 100644 --- a/spec/frontend/releases/stores/modules/detail/actions_spec.js +++ b/spec/frontend/releases/stores/modules/detail/actions_spec.js @@ -2,7 +2,7 @@ import { cloneDeep } from 'lodash'; import originalOneReleaseForEditingQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release_for_editing.query.graphql.json'; import testAction from 'helpers/vuex_action_helper'; import { getTag } from '~/api/tags_api'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { redirectTo } from '~/lib/utils/url_utility'; import { s__ } from '~/locale'; import { ASSET_LINK_TYPE } from '~/releases/constants'; @@ -21,7 +21,7 @@ import { jest.mock('~/api/tags_api'); -jest.mock('~/flash'); +jest.mock('~/alert'); jest.mock('~/releases/release_notification_service'); @@ -154,7 +154,7 @@ describe('Release edit/new actions', () => { ]); }); - it(`shows a flash message`, () => { + it(`shows an alert message`, () => { return actions.fetchRelease({ commit: jest.fn(), state, rootState: state }).then(() => { expect(createAlert).toHaveBeenCalledTimes(1); expect(createAlert).toHaveBeenCalledWith({ @@ -380,7 +380,7 @@ describe('Release edit/new actions', () => { ]); }); - it(`shows a flash message`, () => { + it(`shows an alert message`, () => { return actions .createRelease({ commit: jest.fn(), dispatch: jest.fn(), state, getters: {} }) .then(() => { @@ -406,7 +406,7 @@ describe('Release edit/new actions', () => { ]); }); - it(`shows a flash message`, () => { + it(`shows an alert message`, () => { return actions .createRelease({ commit: jest.fn(), dispatch: jest.fn(), state, getters: {} }) .then(() => { @@ -538,7 +538,7 @@ describe('Release edit/new actions', () => { expect(commit.mock.calls).toEqual([[types.RECEIVE_SAVE_RELEASE_ERROR, error]]); }); - it('shows a flash message', async () => { + it('shows an alert message', async () => { await actions.updateRelease({ commit, dispatch, state, getters }); expect(createAlert).toHaveBeenCalledTimes(1); @@ -558,7 +558,7 @@ describe('Release edit/new actions', () => { ]); }); - it('shows a flash message', async () => { + it('shows an alert message', async () => { await actions.updateRelease({ commit, dispatch, state, getters }); expect(createAlert).toHaveBeenCalledTimes(1); @@ -711,7 +711,7 @@ describe('Release edit/new actions', () => { expect(commit.mock.calls).toContainEqual([types.RECEIVE_SAVE_RELEASE_ERROR, error]); }); - it('shows a flash message', async () => { + it('shows an alert message', async () => { await actions.deleteRelease({ commit, dispatch, state, getters }); expect(createAlert).toHaveBeenCalledTimes(1); @@ -747,7 +747,7 @@ describe('Release edit/new actions', () => { ]); }); - it('shows a flash message', async () => { + it('shows an alert message', async () => { await actions.deleteRelease({ commit, dispatch, state, getters }); expect(createAlert).toHaveBeenCalledTimes(1); @@ -778,7 +778,7 @@ describe('Release edit/new actions', () => { expect(getTag).toHaveBeenCalledWith(state.projectId, tagName); }); - it('creates a flash on error', async () => { + it('creates an alert on error', async () => { error = new Error(); getTag.mockRejectedValue(error); diff --git a/spec/frontend/repository/commits_service_spec.js b/spec/frontend/repository/commits_service_spec.js index e56975d021a..22ef552c2f9 100644 --- a/spec/frontend/repository/commits_service_spec.js +++ b/spec/frontend/repository/commits_service_spec.js @@ -2,11 +2,11 @@ import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; import { loadCommits, isRequested, resetRequestedCommits } from '~/repository/commits_service'; import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { I18N_COMMIT_DATA_FETCH_ERROR } from '~/repository/constants'; import { refWithSpecialCharMock } from './mock_data'; -jest.mock('~/flash'); +jest.mock('~/alert'); describe('commits service', () => { let mock; diff --git a/spec/frontend/repository/components/fork_info_spec.js b/spec/frontend/repository/components/fork_info_spec.js index f327a8cfae7..4b86d9425bc 100644 --- a/spec/frontend/repository/components/fork_info_spec.js +++ b/spec/frontend/repository/components/fork_info_spec.js @@ -4,13 +4,13 @@ import { GlSkeletonLoader, GlIcon, GlLink, GlSprintf } from '@gitlab/ui'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import ForkInfo, { i18n } from '~/repository/components/fork_info.vue'; import forkDetailsQuery from '~/repository/queries/fork_details.query.graphql'; import { propsForkInfo } from '../mock_data'; -jest.mock('~/flash'); +jest.mock('~/alert'); describe('ForkInfo component', () => { let wrapper; diff --git a/spec/frontend/repository/components/new_directory_modal_spec.js b/spec/frontend/repository/components/new_directory_modal_spec.js index 4e5c9a685c4..06e3dcbe9ea 100644 --- a/spec/frontend/repository/components/new_directory_modal_spec.js +++ b/spec/frontend/repository/components/new_directory_modal_spec.js @@ -4,12 +4,12 @@ import { nextTick } from 'vue'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import waitForPromises from 'helpers/wait_for_promises'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; import { visitUrl } from '~/lib/utils/url_utility'; import NewDirectoryModal from '~/repository/components/new_directory_modal.vue'; -jest.mock('~/flash'); +jest.mock('~/alert'); jest.mock('~/lib/utils/url_utility', () => ({ visitUrl: jest.fn(), })); @@ -188,7 +188,7 @@ describe('NewDirectoryModal', () => { expect(findModal().props('actionPrimary').attributes[0].disabled).toBe(true); }); - it('creates a flash error', async () => { + it('creates an alert error', async () => { mock.onPost(initialProps.path).timeout(); await fillForm({ dirName: 'foo', branchName: 'master', commitMessage: 'foo' }); diff --git a/spec/frontend/repository/components/tree_content_spec.js b/spec/frontend/repository/components/tree_content_spec.js index f694c8e9166..4f49fe2fa65 100644 --- a/spec/frontend/repository/components/tree_content_spec.js +++ b/spec/frontend/repository/components/tree_content_spec.js @@ -6,7 +6,7 @@ import FileTable from '~/repository/components/table/index.vue'; import TreeContent from 'jh_else_ce/repository/components/tree_content.vue'; import { loadCommits, isRequested, resetRequestedCommits } from '~/repository/commits_service'; import waitForPromises from 'helpers/wait_for_promises'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { i18n } from '~/repository/constants'; import { graphQLErrors } from '../mock_data'; @@ -15,7 +15,7 @@ jest.mock('~/repository/commits_service', () => ({ isRequested: jest.fn(), resetRequestedCommits: jest.fn(), })); -jest.mock('~/flash'); +jest.mock('~/alert'); let vm; let $apollo; diff --git a/spec/frontend/repository/components/upload_blob_modal_spec.js b/spec/frontend/repository/components/upload_blob_modal_spec.js index 9de0666f27a..2397fe000cf 100644 --- a/spec/frontend/repository/components/upload_blob_modal_spec.js +++ b/spec/frontend/repository/components/upload_blob_modal_spec.js @@ -4,13 +4,13 @@ import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { nextTick } from 'vue'; import waitForPromises from 'helpers/wait_for_promises'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; import { visitUrl } from '~/lib/utils/url_utility'; import UploadBlobModal from '~/repository/components/upload_blob_modal.vue'; import UploadDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue'; -jest.mock('~/flash'); +jest.mock('~/alert'); jest.mock('~/lib/utils/url_utility', () => ({ visitUrl: jest.fn(), joinPaths: () => '/new_upload', @@ -184,7 +184,7 @@ describe('UploadBlobModal', () => { await waitForPromises(); }); - it('creates a flash error', () => { + it('creates an alert error', () => { expect(createAlert).toHaveBeenCalledWith({ message: 'Error uploading file. Please try again.', }); diff --git a/spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js b/spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js index 85cd8d51272..13670fac0ca 100644 --- a/spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js +++ b/spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js @@ -5,13 +5,13 @@ import { useFakeDate } from 'helpers/fake_date'; import { initEmojiMock, clearEmojiMock } from 'helpers/emoji'; import * as UserApi from '~/api/user_api'; import EmojiPicker from '~/emoji/components/picker.vue'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import stubChildren from 'helpers/stub_children'; import SetStatusModalWrapper from '~/set_status_modal/set_status_modal_wrapper.vue'; import { AVAILABILITY_STATUS } from '~/set_status_modal/constants'; import SetStatusForm from '~/set_status_modal/set_status_form.vue'; -jest.mock('~/flash'); +jest.mock('~/alert'); describe('SetStatusModalWrapper', () => { let wrapper; @@ -244,7 +244,7 @@ describe('SetStatusModalWrapper', () => { return initModal({ mockOnUpdateFailure: false }); }); - it('flashes an error message', async () => { + it('alerts an error message', async () => { findModal().vm.$emit('primary'); await nextTick(); diff --git a/spec/frontend/sidebar/components/labels/labels_select_widget/labels_select_root_spec.js b/spec/frontend/sidebar/components/labels/labels_select_widget/labels_select_root_spec.js index c5432eea3ff..ae7c4068069 100644 --- a/spec/frontend/sidebar/components/labels/labels_select_widget/labels_select_root_spec.js +++ b/spec/frontend/sidebar/components/labels/labels_select_widget/labels_select_root_spec.js @@ -4,7 +4,7 @@ import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import { createAlert } from '~/flash'; -import { IssuableType, TYPE_EPIC, TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/issues/constants'; +import { TYPE_EPIC, TYPE_ISSUE, TYPE_MERGE_REQUEST, TYPE_TEST_CASE } from '~/issues/constants'; import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue'; import DropdownContents from '~/sidebar/components/labels/labels_select_widget/dropdown_contents.vue'; import DropdownValue from '~/sidebar/components/labels/labels_select_widget/dropdown_value.vue'; @@ -38,7 +38,7 @@ const updateLabelsMutation = { [TYPE_ISSUE]: updateIssueLabelsMutation, [TYPE_MERGE_REQUEST]: updateMergeRequestLabelsMutation, [TYPE_EPIC]: updateEpicLabelsMutation, - [IssuableType.TestCase]: updateTestCaseLabelsMutation, + [TYPE_TEST_CASE]: updateTestCaseLabelsMutation, }; describe('LabelsSelectRoot', () => { @@ -216,7 +216,7 @@ describe('LabelsSelectRoot', () => { ${TYPE_ISSUE} ${TYPE_MERGE_REQUEST} ${TYPE_EPIC} - ${IssuableType.TestCase} + ${TYPE_TEST_CASE} `('when updating labels for $issuableType', ({ issuableType }) => { const label = { id: 'gid://gitlab/ProjectLabel/2' }; diff --git a/spec/frontend/sidebar/components/sidebar_dropdown_spec.js b/spec/frontend/sidebar/components/sidebar_dropdown_spec.js index 9f3d689edee..7a0044c00ac 100644 --- a/spec/frontend/sidebar/components/sidebar_dropdown_spec.js +++ b/spec/frontend/sidebar/components/sidebar_dropdown_spec.js @@ -10,7 +10,7 @@ import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { TYPE_ISSUE } from '~/issues/constants'; import SidebarDropdown from '~/sidebar/components/sidebar_dropdown.vue'; import { IssuableAttributeType } from '~/sidebar/constants'; @@ -23,7 +23,7 @@ import { noCurrentMilestoneResponse, } from '../mock_data'; -jest.mock('~/flash'); +jest.mock('~/alert'); describe('SidebarDropdown component', () => { let wrapper; diff --git a/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js b/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js index 0b7712cf29e..388a59eb6d2 100644 --- a/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js +++ b/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js @@ -7,7 +7,7 @@ import createMockApollo from 'helpers/mock_apollo_helper'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { TYPE_ISSUE } from '~/issues/constants'; import { timeFor } from '~/lib/utils/datetime_utility'; @@ -27,7 +27,7 @@ import { mockMilestone2, } from '../mock_data'; -jest.mock('~/flash'); +jest.mock('~/alert'); describe('SidebarDropdownWidget', () => { let wrapper; diff --git a/spec/frontend/snippets/components/edit_spec.js b/spec/frontend/snippets/components/edit_spec.js index e7dab0ad79d..e24f96e22ad 100644 --- a/spec/frontend/snippets/components/edit_spec.js +++ b/spec/frontend/snippets/components/edit_spec.js @@ -9,7 +9,7 @@ import { stubPerformanceWebAPI } from 'helpers/performance'; import waitForPromises from 'helpers/wait_for_promises'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import GetSnippetQuery from 'shared_queries/snippet/snippet.query.graphql'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import * as urlUtils from '~/lib/utils/url_utility'; import SnippetEditApp from '~/snippets/components/edit.vue'; import SnippetBlobActionsEdit from '~/snippets/components/snippet_blob_actions_edit.vue'; @@ -25,7 +25,7 @@ import UpdateSnippetMutation from '~/snippets/mutations/update_snippet.mutation. import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue'; import { testEntries, createGQLSnippetsQueryResponse, createGQLSnippet } from '../test_utils'; -jest.mock('~/flash'); +jest.mock('~/alert'); const TEST_UPLOADED_FILES = ['foo/bar.txt', 'alpha/beta.js']; const TEST_API_ERROR = new Error('TEST_API_ERROR'); @@ -347,7 +347,7 @@ describe('Snippet Edit app', () => { projectPath ${'project/path'} ${''} - `('should flash error (projectPath=$projectPath)', async ({ projectPath }) => { + `('should alert error (projectPath=$projectPath)', async ({ projectPath }) => { mutateSpy.mockResolvedValue(createMutationResponseWithErrors('createSnippet')); await createComponentAndLoad({ @@ -373,7 +373,7 @@ describe('Snippet Edit app', () => { ${'project/path'} ${''} `( - 'should flash error with (snippet=$snippetGid, projectPath=$projectPath)', + 'should alert error with (snippet=$snippetGid, projectPath=$projectPath)', async ({ projectPath }) => { mutateSpy.mockResolvedValue(createMutationResponseWithErrors('updateSnippet')); @@ -405,7 +405,7 @@ describe('Snippet Edit app', () => { expect(urlUtils.redirectTo).not.toHaveBeenCalled(); }); - it('should flash', () => { + it('should alert', () => { // Apollo automatically wraps the resolver's error in a NetworkError expect(createAlert).toHaveBeenCalledWith({ message: `Can't update snippet: ${TEST_API_ERROR.message}`, diff --git a/spec/frontend/snippets/components/snippet_blob_edit_spec.js b/spec/frontend/snippets/components/snippet_blob_edit_spec.js index 82c4a37ccc9..ec8e6517058 100644 --- a/spec/frontend/snippets/components/snippet_blob_edit_spec.js +++ b/spec/frontend/snippets/components/snippet_blob_edit_spec.js @@ -4,14 +4,14 @@ import AxiosMockAdapter from 'axios-mock-adapter'; import { TEST_HOST } from 'helpers/test_constants'; import waitForPromises from 'helpers/wait_for_promises'; import BlobHeaderEdit from '~/blob/components/blob_edit_header.vue'; -import { createAlert } from '~/flash'; +import { createAlert } from '~/alert'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status'; import { joinPaths } from '~/lib/utils/url_utility'; import SnippetBlobEdit from '~/snippets/components/snippet_blob_edit.vue'; import SourceEditor from '~/vue_shared/components/source_editor.vue'; -jest.mock('~/flash'); +jest.mock('~/alert'); const TEST_ID = 'blob_local_7'; const TEST_PATH = 'foo/bar/test.md'; @@ -123,7 +123,7 @@ describe('Snippet Blob Edit component', () => { createComponent(); }); - it('should call flash', async () => { + it('should call alert', async () => { await waitForPromises(); expect(createAlert).toHaveBeenCalledWith({ diff --git a/spec/frontend/snippets/components/snippet_header_spec.js b/spec/frontend/snippets/components/snippet_header_spec.js index c930c9f635b..21597d47206 100644 --- a/spec/frontend/snippets/components/snippet_header_spec.js +++ b/spec/frontend/snippets/components/snippet_header_spec.js @@ -10,9 +10,9 @@ import { differenceInMilliseconds } from '~/lib/utils/datetime_utility'; import SnippetHeader, { i18n } from '~/snippets/components/snippet_header.vue'; import DeleteSnippetMutation from '~/snippets/mutations/delete_snippet.mutation.graphql'; import axios from '~/lib/utils/axios_utils'; -import { createAlert, VARIANT_DANGER, VARIANT_SUCCESS } from '~/flash'; +import { createAlert, VARIANT_DANGER, VARIANT_SUCCESS } from '~/alert'; -jest.mock('~/flash'); +jest.mock('~/alert'); describe('Snippet header component', () => { let wrapper; @@ -271,7 +271,7 @@ describe('Snippet header component', () => { ${200} | ${VARIANT_SUCCESS} | ${i18n.snippetSpamSuccess} ${500} | ${VARIANT_DANGER} | ${i18n.snippetSpamFailure} `( - 'renders a "$variant" flash message with "$text" message for a request with a "$request" response', + 'renders a "$variant" alert message with "$text" message for a request with a "$request" response', async ({ request, variant, text }) => { const submitAsSpamBtn = findButtons().at(2); mock.onPost(reportAbusePath).reply(request); diff --git a/spec/lib/gitlab/ci/parsers/security/common_spec.rb b/spec/lib/gitlab/ci/parsers/security/common_spec.rb index 5d2d22c04fc..421aa29f860 100644 --- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb +++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::Ci::Parsers::Security::Common do +RSpec.describe Gitlab::Ci::Parsers::Security::Common, feature_category: :vulnerability_management do describe '#parse!' do let_it_be(:scanner_data) do { @@ -410,6 +410,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do end end + describe 'setting the `found_by_pipeline` attribute' do + subject { report.findings.map(&:found_by_pipeline).uniq } + + it { is_expected.to eq([pipeline]) } + end + describe 'parsing tracking' do let(:finding) { report.findings.first } diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 013070f7be5..c1de8125c0d 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -1100,6 +1100,16 @@ RSpec.describe Note do end end + describe '#for_work_item?' do + it 'returns true for a work item' do + expect(build(:note_on_work_item).for_work_item?).to be true + end + + it 'returns false for an issue' do + expect(build(:note_on_issue).for_work_item?).to be false + end + end + describe '#for_project_snippet?' do it 'returns true for a project snippet note' do expect(build(:note_on_project_snippet).for_project_snippet?).to be true diff --git a/spec/models/work_item_spec.rb b/spec/models/work_item_spec.rb index 6aacaa3c119..13f17e11276 100644 --- a/spec/models/work_item_spec.rb +++ b/spec/models/work_item_spec.rb @@ -163,6 +163,30 @@ RSpec.describe WorkItem, feature_category: :portfolio_management do end end + describe 'transform_quick_action_params' do + let(:work_item) { build(:work_item, :task) } + + subject(:transformed_params) do + work_item.transform_quick_action_params({ + title: 'bar', + assignee_ids: ['foo'] + }) + end + + it 'correctly separates widget params from regular params' do + expect(transformed_params).to eq({ + common: { + title: 'bar' + }, + widgets: { + assignees_widget: { + assignee_ids: ['foo'] + } + } + }) + end + end + describe 'callbacks' do describe 'record_create_action' do it 'records the creation action after saving' do diff --git a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb index a6253ba424b..2a0b5f291dc 100644 --- a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb @@ -104,7 +104,8 @@ RSpec.describe 'Adding a Note', feature_category: :team_planning do end context 'as work item' do - let(:noteable) { create(:work_item, :issue, project: project) } + let_it_be(:project) { create(:project) } + let_it_be(:noteable) { create(:work_item, :issue, project: project) } context 'when using internal param' do let(:variables_extra) { { internal: true } } @@ -130,6 +131,19 @@ RSpec.describe 'Adding a Note', feature_category: :team_planning do it_behaves_like 'a mutation that returns top-level errors', errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR] end + + context 'when body contains quick actions' do + let_it_be(:noteable) { create(:work_item, :task, project: project) } + + let(:variables_extra) { {} } + + it_behaves_like 'work item supports labels widget updates via quick actions' + it_behaves_like 'work item does not support labels widget updates via quick actions' + it_behaves_like 'work item supports assignee widget updates via quick actions' + it_behaves_like 'work item does not support assignee widget updates via quick actions' + it_behaves_like 'work item supports start and due date widget updates via quick actions' + it_behaves_like 'work item does not support start and due date widget updates via quick actions' + end end end diff --git a/spec/services/notes/quick_actions_service_spec.rb b/spec/services/notes/quick_actions_service_spec.rb index bca954c3959..0791b5855ec 100644 --- a/spec/services/notes/quick_actions_service_spec.rb +++ b/spec/services/notes/quick_actions_service_spec.rb @@ -253,6 +253,12 @@ RSpec.describe Notes::QuickActionsService do describe '.noteable_update_service_class' do include_context 'note on noteable' + it 'returns WorkItems::UpdateService for a note on a work item' do + note = create(:note_on_work_item, project: project) + + expect(described_class.noteable_update_service_class(note)).to eq(WorkItems::UpdateService) + end + it 'returns Issues::UpdateService for a note on an issue' do note = create(:note_on_issue, project: project) @@ -322,6 +328,84 @@ RSpec.describe Notes::QuickActionsService do let(:merge_request) { create(:merge_request, source_project: project) } let(:note) { build(:note_on_merge_request, project: project, noteable: merge_request) } end + + context 'note on work item that supports quick actions' do + include_context 'note on noteable' + + let_it_be(:work_item, reload: true) { create(:work_item, project: project) } + + let(:note) { build(:note_on_work_item, project: project, noteable: work_item) } + + let!(:labels) { create_pair(:label, project: project) } + + before do + note.note = note_text + end + + describe 'note with only command' do + describe '/close, /label & /assign' do + let(:note_text) do + %(/close\n/label ~#{labels.first.name} ~#{labels.last.name}\n/assign @#{assignee.username}\n) + end + + it 'closes noteable, sets labels, assigns and leave no note' do + content = execute(note) + + expect(content).to be_empty + expect(note.noteable).to be_closed + expect(note.noteable.labels).to match_array(labels) + expect(note.noteable.assignees).to eq([assignee]) + end + end + + describe '/reopen' do + before do + note.noteable.close! + expect(note.noteable).to be_closed + end + let(:note_text) { '/reopen' } + + it 'opens the noteable, and leave no note' do + content = execute(note) + + expect(content).to be_empty + expect(note.noteable).to be_open + end + end + end + + describe 'note with command & text' do + describe '/close, /label, /assign' do + let(:note_text) do + %(HELLO\n/close\n/label ~#{labels.first.name} ~#{labels.last.name}\n/assign @#{assignee.username}\nWORLD) + end + + it 'closes noteable, sets labels, assigns, and sets milestone to noteable' do + content = execute(note) + + expect(content).to eq "HELLO\nWORLD" + expect(note.noteable).to be_closed + expect(note.noteable.labels).to match_array(labels) + expect(note.noteable.assignees).to eq([assignee]) + end + end + + describe '/reopen' do + before do + note.noteable.close + expect(note.noteable).to be_closed + end + let(:note_text) { "HELLO\n/reopen\nWORLD" } + + it 'opens the noteable' do + content = execute(note) + + expect(content).to eq "HELLO\nWORLD" + expect(note.noteable).to be_open + end + end + end + end end context 'CE restriction for issue assignees' do diff --git a/spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb b/spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb new file mode 100644 index 00000000000..5f4e7e5d4e7 --- /dev/null +++ b/spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb @@ -0,0 +1,154 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'work item supports assignee widget updates via quick actions' do + let_it_be(:developer) { create(:user).tap { |user| project.add_developer(user) } } + + context 'when assigning a user' do + let(:body) { "/assign @#{developer.username}" } + + it 'updates the work item assignee' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + noteable.reload + end.to change { noteable.assignee_ids }.from([]).to([developer.id]) + + expect(response).to have_gitlab_http_status(:success) + end + end + + context 'when unassigning a user' do + let(:body) { "/unassign @#{developer.username}" } + + before do + noteable.update!(assignee_ids: [developer.id]) + end + + it 'updates the work item assignee' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + noteable.reload + end.to change { noteable.assignee_ids }.from([developer.id]).to([]) + + expect(response).to have_gitlab_http_status(:success) + end + end +end + +RSpec.shared_examples 'work item does not support assignee widget updates via quick actions' do + let(:developer) { create(:user).tap { |user| project.add_developer(user) } } + let(:body) { "Updating assignee.\n/assign @#{developer.username}" } + + before do + WorkItems::Type.default_by_type(:task).widget_definitions + .find_by_widget_type(:assignees).update!(disabled: true) + end + + it 'ignores the quick action' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + noteable.reload + end.not_to change { noteable.assignee_ids } + end +end + +RSpec.shared_examples 'work item supports labels widget updates via quick actions' do + shared_examples 'work item labels are updated' do + it do + expect do + post_graphql_mutation(mutation, current_user: current_user) + noteable.reload + end.to change { noteable.labels.count }.to(expected_labels.count) + + expect(noteable.labels).to match_array(expected_labels) + end + end + + let_it_be(:existing_label) { create(:label, project: project) } + let_it_be(:label1) { create(:label, project: project) } + let_it_be(:label2) { create(:label, project: project) } + + let(:add_label_ids) { [] } + let(:remove_label_ids) { [] } + + before_all do + noteable.update!(labels: [existing_label]) + end + + context 'when only removing labels' do + let(:remove_label_ids) { [existing_label.to_gid.to_s] } + let(:expected_labels) { [] } + let(:body) { "/remove_label ~\"#{existing_label.name}\"" } + + it_behaves_like 'work item labels are updated' + end + + context 'when only adding labels' do + let(:add_label_ids) { [label1.to_gid.to_s, label2.to_gid.to_s] } + let(:expected_labels) { [label1, label2, existing_label] } + let(:body) { "/labels ~\"#{label1.name}\" ~\"#{label2.name}\"" } + + it_behaves_like 'work item labels are updated' + end + + context 'when adding and removing labels' do + let(:remove_label_ids) { [existing_label.to_gid.to_s] } + let(:add_label_ids) { [label1.to_gid.to_s, label2.to_gid.to_s] } + let(:expected_labels) { [label1, label2] } + let(:body) { "/label ~\"#{label1.name}\" ~\"#{label2.name}\"\n/remove_label ~\"#{existing_label.name}\"" } + + it_behaves_like 'work item labels are updated' + end +end + +RSpec.shared_examples 'work item does not support labels widget updates via quick actions' do + let(:label1) { create(:label, project: project) } + let(:body) { "Updating labels.\n/labels ~\"#{label1.name}\"" } + + before do + WorkItems::Type.default_by_type(:task).widget_definitions + .find_by_widget_type(:labels).update!(disabled: true) + end + + it 'ignores the quick action' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + noteable.reload + end.not_to change { noteable.labels.count } + + expect(noteable.labels).to be_empty + end +end + +RSpec.shared_examples 'work item supports start and due date widget updates via quick actions' do + let(:due_date) { Date.today } + let(:body) { "/remove_due_date" } + + before do + noteable.update!(due_date: due_date) + end + + it 'updates start and due date' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + noteable.reload + end.to not_change(noteable, :start_date).and( + change { noteable.due_date }.from(due_date).to(nil) + ) + end +end + +RSpec.shared_examples 'work item does not support start and due date widget updates via quick actions' do + let(:body) { "Updating due date.\n/due today" } + + before do + WorkItems::Type.default_by_type(:task).widget_definitions + .find_by_widget_type(:start_and_due_date).update!(disabled: true) + end + + it 'ignores the quick action' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + noteable.reload + end.not_to change { noteable.due_date } + end +end |