diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-17 14:33:21 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-11-17 14:33:21 +0300 |
commit | 7021455bd1ed7b125c55eb1b33c5a01f2bc55ee0 (patch) | |
tree | 5bdc2229f5198d516781f8d24eace62fc7e589e9 /spec/frontend/issuable | |
parent | 185b095e93520f96e9cfc31d9c3e69b498cdab7c (diff) |
Add latest changes from gitlab-org/gitlab@15-6-stable-eev15.6.0-rc42
Diffstat (limited to 'spec/frontend/issuable')
3 files changed, 589 insertions, 21 deletions
diff --git a/spec/frontend/issuable/bulk_update_sidebar/components/move_issues_button_spec.js b/spec/frontend/issuable/bulk_update_sidebar/components/move_issues_button_spec.js new file mode 100644 index 00000000000..c432d722637 --- /dev/null +++ b/spec/frontend/issuable/bulk_update_sidebar/components/move_issues_button_spec.js @@ -0,0 +1,554 @@ +import { shallowMount } from '@vue/test-utils'; +import Vue, { nextTick } from 'vue'; +import { cloneDeep } from 'lodash'; +import VueApollo from 'vue-apollo'; +import { GlAlert } from '@gitlab/ui'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import { useMockLocationHelper } from 'helpers/mock_window_location_helper'; +import createFlash from '~/flash'; +import { logError } from '~/lib/logger'; +import IssuableMoveDropdown from '~/vue_shared/components/sidebar/issuable_move_dropdown.vue'; +import MoveIssuesButton from '~/issuable/bulk_update_sidebar/components/move_issues_button.vue'; +import issuableEventHub from '~/issues/list/eventhub'; +import moveIssueMutation from '~/issuable/bulk_update_sidebar/components/graphql/mutations/move_issue.mutation.graphql'; +import getIssuesQuery from 'ee_else_ce/issues/list/queries/get_issues.query.graphql'; +import getIssuesCountsQuery from 'ee_else_ce/issues/list/queries/get_issues_counts.query.graphql'; +import { getIssuesCountsQueryResponse, getIssuesQueryResponse } from 'jest/issues/list/mock_data'; +import { + WORK_ITEM_TYPE_ENUM_ISSUE, + WORK_ITEM_TYPE_ENUM_INCIDENT, + WORK_ITEM_TYPE_ENUM_TASK, + WORK_ITEM_TYPE_ENUM_TEST_CASE, +} from '~/work_items/constants'; + +jest.mock('~/flash'); +jest.mock('~/lib/logger'); +useMockLocationHelper(); + +const mockDefaultProps = { + projectFullPath: 'flight/FlightJS', + projectsFetchPath: '/-/autocomplete/projects?project_id=1', +}; + +const mockDestinationProject = { + full_path: 'gitlab-org/GitLabTest', +}; + +const mockMutationErrorMessage = 'Example error message'; + +const mockIssue = { + iid: '15', + type: WORK_ITEM_TYPE_ENUM_ISSUE, +}; + +const mockIncident = { + iid: '32', + type: WORK_ITEM_TYPE_ENUM_INCIDENT, +}; + +const mockTask = { + iid: '40', + type: WORK_ITEM_TYPE_ENUM_TASK, +}; + +const mockTestCase = { + iid: '51', + type: WORK_ITEM_TYPE_ENUM_TEST_CASE, +}; + +const selectedIssuesMocks = { + tasksOnly: [mockTask], + testCasesOnly: [mockTestCase], + issuesOnly: [mockIssue, mockIncident], + tasksAndTestCases: [mockTask, mockTestCase], + issuesAndTasks: [mockIssue, mockIncident, mockTask], + issuesAndTestCases: [mockIssue, mockIncident, mockTestCase], + issuesTasksAndTestCases: [mockIssue, mockIncident, mockTask, mockTestCase], +}; + +let getIssuesQueryCompleteResponse = getIssuesQueryResponse; +if (IS_EE) { + getIssuesQueryCompleteResponse = cloneDeep(getIssuesQueryResponse); + getIssuesQueryCompleteResponse.data.project.issues.nodes[0].blockingCount = 1; + getIssuesQueryCompleteResponse.data.project.issues.nodes[0].healthStatus = null; + getIssuesQueryCompleteResponse.data.project.issues.nodes[0].weight = 5; +} + +const resolvedMutationWithoutErrorsMock = jest.fn().mockResolvedValue({ + data: { + issueMove: { + errors: [], + }, + }, +}); + +const resolvedMutationWithErrorsMock = jest.fn().mockResolvedValue({ + data: { + issueMove: { + errors: [{ message: mockMutationErrorMessage }], + }, + }, +}); + +const rejectedMutationMock = jest.fn().mockRejectedValue({}); + +const mockIssuesQueryResponse = jest.fn().mockResolvedValue(getIssuesQueryCompleteResponse); +const mockIssuesCountsQueryResponse = jest.fn().mockResolvedValue(getIssuesCountsQueryResponse); + +describe('MoveIssuesButton', () => { + Vue.use(VueApollo); + + let wrapper; + let fakeApollo; + + const findAlert = () => wrapper.findComponent(GlAlert); + const findDropdown = () => wrapper.findComponent(IssuableMoveDropdown); + const emitMoveIssuablesEvent = () => { + findDropdown().vm.$emit('move-issuable', mockDestinationProject); + }; + + const createComponent = (data = {}, mutationResolverMock = rejectedMutationMock) => { + fakeApollo = createMockApollo([ + [moveIssueMutation, mutationResolverMock], + [getIssuesQuery, mockIssuesQueryResponse], + [getIssuesCountsQuery, mockIssuesCountsQueryResponse], + ]); + + fakeApollo.defaultClient.cache.writeQuery({ + query: getIssuesQuery, + variables: { + isProject: true, + fullPath: mockDefaultProps.projectFullPath, + }, + data: getIssuesQueryCompleteResponse.data, + }); + + fakeApollo.defaultClient.cache.writeQuery({ + query: getIssuesCountsQuery, + variables: { + isProject: true, + }, + data: getIssuesCountsQueryResponse.data, + }); + + wrapper = shallowMount(MoveIssuesButton, { + data() { + return { + ...data, + }; + }, + propsData: { + ...mockDefaultProps, + }, + apolloProvider: fakeApollo, + }); + }; + + beforeEach(() => { + // Needed due to a bug in Apollo: https://github.com/apollographql/apollo-client/issues/8900 + // eslint-disable-next-line no-console + console.warn = jest.fn(); + }); + + afterEach(() => { + wrapper.destroy(); + fakeApollo = null; + }); + + describe('`Move selected` dropdown', () => { + it('renders disabled by default', () => { + createComponent(); + expect(findDropdown().exists()).toBe(true); + expect(findDropdown().attributes('disabled')).toBe('true'); + }); + + it.each` + selectedIssuablesMock | disabled | status | testMessage + ${[]} | ${true} | ${'disabled'} | ${'nothing is selected'} + ${selectedIssuesMocks.tasksOnly} | ${true} | ${'disabled'} | ${'only tasks are selected'} + ${selectedIssuesMocks.testCasesOnly} | ${true} | ${'disabled'} | ${'only test cases are selected'} + ${selectedIssuesMocks.issuesOnly} | ${false} | ${'enabled'} | ${'only issues are selected'} + ${selectedIssuesMocks.tasksAndTestCases} | ${true} | ${'disabled'} | ${'tasks and test cases are selected'} + ${selectedIssuesMocks.issuesAndTasks} | ${false} | ${'enabled'} | ${'issues and tasks are selected'} + ${selectedIssuesMocks.issuesAndTestCases} | ${false} | ${'enabled'} | ${'issues and test cases are selected'} + ${selectedIssuesMocks.issuesTasksAndTestCases} | ${false} | ${'enabled'} | ${'issues and tasks and test cases are selected'} + `('renders $status if $testMessage', async ({ selectedIssuablesMock, disabled }) => { + createComponent({ selectedIssuables: selectedIssuablesMock }); + + await nextTick(); + + if (disabled) { + expect(findDropdown().attributes('disabled')).toBe('true'); + } else { + expect(findDropdown().attributes('disabled')).toBeUndefined(); + } + }); + }); + + describe('warning message', () => { + it.each` + selectedIssuablesMock | warningExists | visibility | message | testMessage + ${[]} | ${false} | ${'not visible'} | ${'empty'} | ${'nothing is selected'} + ${selectedIssuesMocks.tasksOnly} | ${true} | ${'visible'} | ${'Tasks can not be moved.'} | ${'only tasks are selected'} + ${selectedIssuesMocks.testCasesOnly} | ${true} | ${'visible'} | ${'Test cases can not be moved.'} | ${'only test cases are selected'} + ${selectedIssuesMocks.issuesOnly} | ${false} | ${'not visible'} | ${'empty'} | ${'only issues are selected'} + ${selectedIssuesMocks.tasksAndTestCases} | ${true} | ${'visible'} | ${'Tasks and test cases can not be moved.'} | ${'tasks and test cases are selected'} + ${selectedIssuesMocks.issuesAndTasks} | ${true} | ${'visible'} | ${'Tasks can not be moved.'} | ${'issues and tasks are selected'} + ${selectedIssuesMocks.issuesAndTestCases} | ${true} | ${'visible'} | ${'Test cases can not be moved.'} | ${'issues and test cases are selected'} + ${selectedIssuesMocks.issuesTasksAndTestCases} | ${true} | ${'visible'} | ${'Tasks and test cases can not be moved.'} | ${'issues and tasks and test cases are selected'} + `( + 'is $visibility with `$message` message if $testMessage', + async ({ selectedIssuablesMock, warningExists, message }) => { + createComponent({ selectedIssuables: selectedIssuablesMock }); + + await nextTick(); + + const alert = findAlert(); + expect(alert.exists()).toBe(warningExists); + + if (warningExists) { + expect(alert.text()).toBe(message); + expect(alert.attributes('variant')).toBe('warning'); + } + }, + ); + }); + + describe('moveIssues method', () => { + describe('changes the `Move selected` dropdown loading state', () => { + it('keeps loading state to false when no issue is selected', async () => { + createComponent(); + emitMoveIssuablesEvent(); + + await nextTick(); + + expect(findDropdown().props('moveInProgress')).toBe(false); + }); + + it('keeps loading state to false when only tasks are selected', async () => { + createComponent({ selectedIssuables: selectedIssuesMocks.tasksOnly }); + emitMoveIssuablesEvent(); + + await nextTick(); + + expect(findDropdown().props('moveInProgress')).toBe(false); + }); + + it('keeps loading state to false when only test cases are selected', async () => { + createComponent({ selectedIssuables: selectedIssuesMocks.testCasesOnly }); + emitMoveIssuablesEvent(); + + await nextTick(); + + expect(findDropdown().props('moveInProgress')).toBe(false); + }); + + it('keeps loading state to false when only tasks and test cases are selected', async () => { + createComponent({ selectedIssuables: selectedIssuesMocks.tasksAndTestCases }); + emitMoveIssuablesEvent(); + + await nextTick(); + + expect(findDropdown().props('moveInProgress')).toBe(false); + }); + + it('sets loading state to true when issues are moving', async () => { + createComponent({ selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases }); + emitMoveIssuablesEvent(); + + await nextTick(); + + expect(findDropdown().props('moveInProgress')).toBe(true); + }); + + it('sets loading state to false when all mutations succeed', async () => { + createComponent( + { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases }, + resolvedMutationWithoutErrorsMock, + ); + emitMoveIssuablesEvent(); + + await nextTick(); + await waitForPromises(); + + expect(findDropdown().props('moveInProgress')).toBe(false); + }); + + it('sets loading state to false when a mutation returns errors', async () => { + createComponent( + { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases }, + resolvedMutationWithErrorsMock, + ); + emitMoveIssuablesEvent(); + + await nextTick(); + await waitForPromises(); + + expect(findDropdown().props('moveInProgress')).toBe(false); + }); + + it('sets loading state to false when a mutation is rejected', async () => { + createComponent({ selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases }); + emitMoveIssuablesEvent(); + + await nextTick(); + await waitForPromises(); + + expect(findDropdown().props('moveInProgress')).toBe(false); + }); + }); + + describe('handles events', () => { + beforeEach(() => { + jest.spyOn(issuableEventHub, '$emit'); + }); + + it('does not emit any event when no issue is selected', async () => { + createComponent(); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(issuableEventHub.$emit).not.toHaveBeenCalled(); + }); + + it('does not emit any event when only tasks are selected', async () => { + createComponent({ selectedIssuables: selectedIssuesMocks.tasksOnly }); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(issuableEventHub.$emit).not.toHaveBeenCalled(); + }); + + it('does not emit any event when only test cases are selected', async () => { + createComponent({ selectedIssuables: selectedIssuesMocks.testCasesOnly }); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(issuableEventHub.$emit).not.toHaveBeenCalled(); + }); + + it('does not emit any event when only tasks and test cases are selected', async () => { + createComponent({ selectedIssuables: selectedIssuesMocks.tasksAndTestCases }); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(issuableEventHub.$emit).not.toHaveBeenCalled(); + }); + + it('emits `issuables:bulkMoveStarted` when issues are moving', async () => { + createComponent({ selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases }); + emitMoveIssuablesEvent(); + + expect(issuableEventHub.$emit).toHaveBeenCalledWith('issuables:bulkMoveStarted'); + }); + + it('emits `issuables:bulkMoveEnded` when all mutations succeed', async () => { + createComponent( + { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases }, + resolvedMutationWithoutErrorsMock, + ); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(issuableEventHub.$emit).toHaveBeenCalledWith('issuables:bulkMoveEnded'); + }); + + it('emits `issuables:bulkMoveEnded` when a mutation returns errors', async () => { + createComponent( + { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases }, + resolvedMutationWithErrorsMock, + ); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(issuableEventHub.$emit).toHaveBeenCalledWith('issuables:bulkMoveEnded'); + }); + + it('emits `issuables:bulkMoveEnded` when a mutation is rejected', async () => { + createComponent({ selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases }); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(issuableEventHub.$emit).toHaveBeenCalledWith('issuables:bulkMoveEnded'); + }); + }); + + describe('shows errors', () => { + it('does not create flashes or logs errors when no issue is selected', async () => { + createComponent(); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(logError).not.toHaveBeenCalled(); + expect(createFlash).not.toHaveBeenCalled(); + }); + + it('does not create flashes or logs errors when only tasks are selected', async () => { + createComponent({ selectedIssuables: selectedIssuesMocks.tasksOnly }); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(logError).not.toHaveBeenCalled(); + expect(createFlash).not.toHaveBeenCalled(); + }); + + it('does not create flashes or logs errors when only test cases are selected', async () => { + createComponent({ selectedIssuables: selectedIssuesMocks.testCasesOnly }); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(logError).not.toHaveBeenCalled(); + expect(createFlash).not.toHaveBeenCalled(); + }); + + it('does not create flashes or logs errors when only tasks and test cases are selected', async () => { + createComponent({ selectedIssuables: selectedIssuesMocks.tasksAndTestCases }); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(logError).not.toHaveBeenCalled(); + expect(createFlash).not.toHaveBeenCalled(); + }); + + it('does not create flashes or logs errors when issues are moved without errors', async () => { + createComponent( + { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases }, + resolvedMutationWithoutErrorsMock, + ); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(logError).not.toHaveBeenCalled(); + expect(createFlash).not.toHaveBeenCalled(); + }); + + it('creates a flash and logs errors when a mutation returns errors', async () => { + createComponent( + { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases }, + resolvedMutationWithErrorsMock, + ); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + // We're mocking two issues so it will log two errors + expect(logError).toHaveBeenCalledTimes(2); + expect(logError).toHaveBeenNthCalledWith( + 1, + `Error moving issue. Error message: ${mockMutationErrorMessage}`, + ); + expect(logError).toHaveBeenNthCalledWith( + 2, + `Error moving issue. Error message: ${mockMutationErrorMessage}`, + ); + + // Only one flash is created even if multiple errors are reported + expect(createFlash).toHaveBeenCalledTimes(1); + expect(createFlash).toHaveBeenCalledWith({ + message: 'There was an error while moving the issues.', + }); + }); + + it('creates a flash but not logs errors when a mutation is rejected', async () => { + createComponent({ selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases }); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(logError).not.toHaveBeenCalled(); + expect(createFlash).toHaveBeenCalledTimes(1); + expect(createFlash).toHaveBeenCalledWith({ + message: 'There was an error while moving the issues.', + }); + }); + }); + + describe('calls mutations', () => { + it('does not call any mutation when no issue is selected', async () => { + createComponent({}, resolvedMutationWithoutErrorsMock); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(resolvedMutationWithoutErrorsMock).not.toHaveBeenCalled(); + }); + + it('does not call any mutation when only tasks are selected', async () => { + createComponent( + { selectedIssuables: selectedIssuesMocks.tasksOnly }, + resolvedMutationWithoutErrorsMock, + ); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(resolvedMutationWithoutErrorsMock).not.toHaveBeenCalled(); + }); + + it('does not call any mutation when only test cases are selected', async () => { + createComponent( + { selectedIssuables: selectedIssuesMocks.testCasesOnly }, + resolvedMutationWithoutErrorsMock, + ); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(resolvedMutationWithoutErrorsMock).not.toHaveBeenCalled(); + }); + + it('does not call any mutation when only tasks and test cases are selected', async () => { + createComponent( + { selectedIssuables: selectedIssuesMocks.tasksAndTestCases }, + resolvedMutationWithoutErrorsMock, + ); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + expect(resolvedMutationWithoutErrorsMock).not.toHaveBeenCalled(); + }); + + it('calls a mutation for every selected issue skipping tasks', async () => { + createComponent( + { selectedIssuables: selectedIssuesMocks.issuesTasksAndTestCases }, + resolvedMutationWithoutErrorsMock, + ); + emitMoveIssuablesEvent(); + + await waitForPromises(); + + // We mock three elements but only two are valid issues since the task is skipped + expect(resolvedMutationWithoutErrorsMock).toHaveBeenCalledTimes(2); + expect(resolvedMutationWithoutErrorsMock).toHaveBeenNthCalledWith(1, { + moveIssueInput: { + projectPath: mockDefaultProps.projectFullPath, + iid: selectedIssuesMocks.issuesTasksAndTestCases[0].iid.toString(), + targetProjectPath: mockDestinationProject.full_path, + }, + }); + + expect(resolvedMutationWithoutErrorsMock).toHaveBeenNthCalledWith(2, { + moveIssueInput: { + projectPath: mockDefaultProps.projectFullPath, + iid: selectedIssuesMocks.issuesTasksAndTestCases[1].iid.toString(), + targetProjectPath: mockDestinationProject.full_path, + }, + }); + }); + }); + }); +}); diff --git a/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js b/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js index 1b2935ce5d1..996b2406240 100644 --- a/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js +++ b/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js @@ -40,12 +40,12 @@ describe('RelatedIssuesBlock', () => { }); it.each` - issuableType | pathIdSeparator | titleText | helpLinkText | addButtonText - ${'issue'} | ${PathIdSeparator.Issue} | ${'Linked items'} | ${'Read more about related issues'} | ${'Add a related issue'} - ${'epic'} | ${PathIdSeparator.Epic} | ${'Linked epics'} | ${'Read more about related epics'} | ${'Add a related epic'} + issuableType | pathIdSeparator | titleText | addButtonText + ${'issue'} | ${PathIdSeparator.Issue} | ${'Linked items'} | ${'Add a related issue'} + ${'epic'} | ${PathIdSeparator.Epic} | ${'Linked epics'} | ${'Add a related epic'} `( - 'displays "$titleText" in the header, "$helpLinkText" aria-label for help link, and "$addButtonText" aria-label for add button when issuableType is set to "$issuableType"', - ({ issuableType, pathIdSeparator, titleText, helpLinkText, addButtonText }) => { + 'displays "$titleText" in the header and "$addButtonText" aria-label for add button when issuableType is set to "$issuableType"', + ({ issuableType, pathIdSeparator, titleText, addButtonText }) => { wrapper = mountExtended(RelatedIssuesBlock, { propsData: { pathIdSeparator, @@ -56,9 +56,6 @@ describe('RelatedIssuesBlock', () => { }); expect(wrapper.find('.card-title').text()).toContain(titleText); - expect(wrapper.find('[data-testid="help-link"]').attributes('aria-label')).toBe( - helpLinkText, - ); expect(findIssueCountBadgeAddButton().attributes('aria-label')).toBe(addButtonText); }, ); @@ -100,7 +97,7 @@ describe('RelatedIssuesBlock', () => { slots: { 'header-actions': headerActions }, }); - expect(wrapper.find('[data-testid="custom-button"]').html()).toBe(headerActions); + expect(wrapper.findByTestId('custom-button').html()).toBe(headerActions); }); }); @@ -260,15 +257,30 @@ describe('RelatedIssuesBlock', () => { }); }); - it('toggle button is disabled when issue has no related items', () => { - wrapper = shallowMountExtended(RelatedIssuesBlock, { - propsData: { - pathIdSeparator: PathIdSeparator.Issue, - relatedIssues: [], - issuableType: 'issue', - }, - }); + describe('empty state', () => { + it.each` + issuableType | pathIdSeparator | showCategorizedIssues | emptyText | helpLinkText + ${'issue'} | ${PathIdSeparator.Issue} | ${false} | ${"Link issues together to show that they're related."} | ${'Learn more about linking issues'} + ${'issue'} | ${PathIdSeparator.Issue} | ${true} | ${"Link issues together to show that they're related or that one is blocking others."} | ${'Learn more about linking issues'} + ${'incident'} | ${PathIdSeparator.Issue} | ${false} | ${"Link incidents together to show that they're related."} | ${'Learn more about linking issues and incidents'} + ${'incident'} | ${PathIdSeparator.Issue} | ${true} | ${"Link incidents together to show that they're related or that one is blocking others."} | ${'Learn more about linking issues and incidents'} + ${'epic'} | ${PathIdSeparator.Epic} | ${true} | ${"Link epics together to show that they're related or that one is blocking others."} | ${'Learn more about linking epics'} + `( + 'displays "$emptyText" in the body and "$helpLinkText" aria-label for help link', + ({ issuableType, pathIdSeparator, showCategorizedIssues, emptyText, helpLinkText }) => { + wrapper = mountExtended(RelatedIssuesBlock, { + propsData: { + pathIdSeparator, + issuableType, + canAdmin: true, + helpPath: '/help/user/project/issues/related_issues', + showCategorizedIssues, + }, + }); - expect(findToggleButton().props('disabled')).toBe(true); + expect(wrapper.findByTestId('related-issues-body').text()).toContain(emptyText); + expect(wrapper.findByTestId('help-link').attributes('aria-label')).toBe(helpLinkText); + }, + ); }); }); diff --git a/spec/frontend/issuable/related_issues/components/related_issues_root_spec.js b/spec/frontend/issuable/related_issues/components/related_issues_root_spec.js index 680dbd68493..bedf8bcaf34 100644 --- a/spec/frontend/issuable/related_issues/components/related_issues_root_spec.js +++ b/spec/frontend/issuable/related_issues/components/related_issues_root_spec.js @@ -201,18 +201,20 @@ describe('RelatedIssuesRoot', () => { ]); }); - it('displays a message from the backend upon error', async () => { + it('passes an error message from the backend upon error', async () => { const input = '#123'; const message = 'error'; mock.onPost(defaultProps.endpoint).reply(409, { message }); wrapper.vm.store.setPendingReferences([issuable1.reference, issuable2.reference]); - expect(createAlert).not.toHaveBeenCalled(); + expect(findRelatedIssuesBlock().props('hasError')).toBe(false); + expect(findRelatedIssuesBlock().props('itemAddFailureMessage')).toBe(null); findRelatedIssuesBlock().vm.$emit('addIssuableFormSubmit', input); await waitForPromises(); - expect(createAlert).toHaveBeenCalledWith({ message }); + expect(findRelatedIssuesBlock().props('hasError')).toBe(true); + expect(findRelatedIssuesBlock().props('itemAddFailureMessage')).toBe(message); }); }); |