diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-16 06:09:57 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-16 06:09:57 +0000 |
commit | 93c27b216aa57d57ebd8f5f2581e45dc300324b8 (patch) | |
tree | a6cbeb92b9f24baf499cc9ed276e1c0a70ea50c7 /spec/frontend | |
parent | 43b91399aeb9f58afc9599b51a941b2bbc745a28 (diff) | |
download | gitlab-ce-93c27b216aa57d57ebd8f5f2581e45dc300324b8.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
8 files changed, 646 insertions, 30 deletions
diff --git a/spec/frontend/boards/board_card_inner_spec.js b/spec/frontend/boards/board_card_inner_spec.js index 4487fc15de6..36043b09636 100644 --- a/spec/frontend/boards/board_card_inner_spec.js +++ b/spec/frontend/boards/board_card_inner_spec.js @@ -1,11 +1,14 @@ import { GlLabel } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; import { range } from 'lodash'; +import Vuex from 'vuex'; +import BoardBlockedIcon from '~/boards/components/board_blocked_icon.vue'; import BoardCardInner from '~/boards/components/board_card_inner.vue'; +import { issuableTypes } from '~/boards/constants'; import eventHub from '~/boards/eventhub'; import defaultStore from '~/boards/stores'; import { updateHistory } from '~/lib/utils/url_utility'; -import { mockLabelList } from './mock_data'; +import { mockLabelList, mockIssue } from './mock_data'; jest.mock('~/lib/utils/url_utility'); jest.mock('~/boards/eventhub'); @@ -29,8 +32,28 @@ describe('Board card component', () => { let wrapper; let issue; let list; + let store; + + const findBoardBlockedIcon = () => wrapper.find(BoardBlockedIcon); + + const createStore = () => { + store = new Vuex.Store({ + ...defaultStore, + state: { + ...defaultStore.state, + issuableType: issuableTypes.issue, + }, + getters: { + isGroupBoard: () => true, + isEpicBoard: () => false, + isProjectBoard: () => false, + }, + }); + }; + + const createWrapper = (props = {}) => { + createStore(); - const createWrapper = (props = {}, store = defaultStore) => { wrapper = mount(BoardCardInner, { store, propsData: { @@ -41,6 +64,13 @@ describe('Board card component', () => { stubs: { GlLabel: true, }, + mocks: { + $apollo: { + queries: { + blockingIssuables: { loading: false }, + }, + }, + }, provide: { rootPath: '/', scopedLabelsAvailable: false, @@ -51,14 +81,9 @@ describe('Board card component', () => { beforeEach(() => { list = mockLabelList; issue = { - title: 'Testing', - id: 1, - iid: 1, - confidential: false, + ...mockIssue, labels: [list.label], assignees: [], - referencePath: '#1', - webUrl: '/test/1', weight: 1, }; @@ -68,6 +93,7 @@ describe('Board card component', () => { afterEach(() => { wrapper.destroy(); wrapper = null; + store = null; jest.clearAllMocks(); }); @@ -87,18 +113,38 @@ describe('Board card component', () => { expect(wrapper.find('.confidential-icon').exists()).toBe(false); }); - it('does not render blocked icon', () => { - expect(wrapper.find('.issue-blocked-icon').exists()).toBe(false); - }); - it('renders issue ID with #', () => { - expect(wrapper.find('.board-card-number').text()).toContain(`#${issue.id}`); + expect(wrapper.find('.board-card-number').text()).toContain(`#${issue.iid}`); }); it('does not render assignee', () => { expect(wrapper.find('.board-card-assignee .avatar').exists()).toBe(false); }); + describe('blocked', () => { + it('renders blocked icon if issue is blocked', async () => { + createWrapper({ + item: { + ...issue, + blocked: true, + }, + }); + + expect(findBoardBlockedIcon().exists()).toBe(true); + }); + + it('does not show blocked icon if issue is not blocked', () => { + createWrapper({ + item: { + ...issue, + blocked: false, + }, + }); + + expect(findBoardBlockedIcon().exists()).toBe(false); + }); + }); + describe('confidential issue', () => { beforeEach(() => { wrapper.setProps({ @@ -303,21 +349,6 @@ describe('Board card component', () => { }); }); - describe('blocked', () => { - beforeEach(() => { - wrapper.setProps({ - item: { - ...wrapper.props('item'), - blocked: true, - }, - }); - }); - - it('renders blocked icon if issue is blocked', () => { - expect(wrapper.find('.issue-blocked-icon').exists()).toBe(true); - }); - }); - describe('filterByLabel method', () => { beforeEach(() => { delete window.location; diff --git a/spec/frontend/boards/components/__snapshots__/board_blocked_icon_spec.js.snap b/spec/frontend/boards/components/__snapshots__/board_blocked_icon_spec.js.snap new file mode 100644 index 00000000000..c000f300e4d --- /dev/null +++ b/spec/frontend/boards/components/__snapshots__/board_blocked_icon_spec.js.snap @@ -0,0 +1,30 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`BoardBlockedIcon on mouseenter on blocked icon with more than three blocking issues matches the snapshot 1`] = ` +"<div class=\\"gl-display-inline\\"><svg data-testid=\\"issue-blocked-icon\\" aria-hidden=\\"true\\" class=\\"issue-blocked-icon gl-mr-2 gl-cursor-pointer gl-icon s16\\" id=\\"blocked-icon-uniqueId\\"> + <use href=\\"#issue-block\\"></use> + </svg> + <div class=\\"gl-popover\\"> + <ul class=\\"gl-list-style-none gl-p-0\\"> + <li><a href=\\"http://gdk.test:3000/gitlab-org/my-project-1/-/issues/6\\" class=\\"gl-link gl-text-blue-500! gl-font-sm\\">my-project-1#6</a> + <p data-testid=\\"issuable-title\\" class=\\"gl-mb-3 gl-display-block!\\"> + blocking issue title 1 + </p> + </li> + <li><a href=\\"http://gdk.test:3000/gitlab-org/my-project-1/-/issues/5\\" class=\\"gl-link gl-text-blue-500! gl-font-sm\\">my-project-1#5</a> + <p data-testid=\\"issuable-title\\" class=\\"gl-mb-3 gl-display-block!\\"> + blocking issue title 2 + blocking issue title 2 + blocking issue title 2 + bloc… + </p> + </li> + <li><a href=\\"http://gdk.test:3000/gitlab-org/my-project-1/-/issues/4\\" class=\\"gl-link gl-text-blue-500! gl-font-sm\\">my-project-1#4</a> + <p data-testid=\\"issuable-title\\" class=\\"gl-mb-3 gl-display-block!\\"> + blocking issue title 3 + </p> + </li> + </ul> + <div class=\\"gl-mt-4\\"> + <p data-testid=\\"hidden-blocking-count\\" class=\\"gl-mb-3\\">+ 1 more issue</p> <a data-testid=\\"view-all-issues\\" href=\\"http://gdk.test:3000/gitlab-org/my-project-1/-/issues/0#related-issues\\" class=\\"gl-link gl-text-blue-500! gl-font-sm\\">View all blocking issues</a> + </div><span data-testid=\\"popover-title\\">Blocked by 4 issues</span> + </div> +</div>" +`; diff --git a/spec/frontend/boards/components/board_blocked_icon_spec.js b/spec/frontend/boards/components/board_blocked_icon_spec.js new file mode 100644 index 00000000000..7b04942f056 --- /dev/null +++ b/spec/frontend/boards/components/board_blocked_icon_spec.js @@ -0,0 +1,226 @@ +import { GlIcon, GlLink, GlPopover, GlLoadingIcon } from '@gitlab/ui'; +import { shallowMount, mount } from '@vue/test-utils'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import BoardBlockedIcon from '~/boards/components/board_blocked_icon.vue'; +import { blockingIssuablesQueries, issuableTypes } from '~/boards/constants'; +import { truncate } from '~/lib/utils/text_utility'; +import { + mockIssue, + mockBlockingIssue1, + mockBlockingIssue2, + mockBlockingIssuablesResponse1, + mockBlockingIssuablesResponse2, + mockBlockingIssuablesResponse3, + mockBlockedIssue1, + mockBlockedIssue2, +} from '../mock_data'; + +describe('BoardBlockedIcon', () => { + let wrapper; + let mockApollo; + + const findGlIcon = () => wrapper.find(GlIcon); + const findGlPopover = () => wrapper.find(GlPopover); + const findGlLink = () => wrapper.find(GlLink); + const findPopoverTitle = () => wrapper.findByTestId('popover-title'); + const findIssuableTitle = () => wrapper.findByTestId('issuable-title'); + const findHiddenBlockingCount = () => wrapper.findByTestId('hidden-blocking-count'); + const findViewAllIssuableLink = () => wrapper.findByTestId('view-all-issues'); + + const waitForApollo = async () => { + jest.runOnlyPendingTimers(); + await waitForPromises(); + }; + + const mouseenter = async () => { + findGlIcon().vm.$emit('mouseenter'); + + await wrapper.vm.$nextTick(); + await waitForApollo(); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + const createWrapperWithApollo = ({ + item = mockBlockedIssue1, + blockingIssuablesSpy = jest.fn().mockResolvedValue(mockBlockingIssuablesResponse1), + } = {}) => { + mockApollo = createMockApollo([ + [blockingIssuablesQueries[issuableTypes.issue].query, blockingIssuablesSpy], + ]); + + Vue.use(VueApollo); + wrapper = extendedWrapper( + mount(BoardBlockedIcon, { + apolloProvider: mockApollo, + propsData: { + item: { + ...mockIssue, + ...item, + }, + uniqueId: 'uniqueId', + issuableType: issuableTypes.issue, + }, + attachTo: document.body, + }), + ); + }; + + const createWrapper = ({ item = {}, queries = {}, data = {}, loading = false } = {}) => { + wrapper = extendedWrapper( + shallowMount(BoardBlockedIcon, { + propsData: { + item: { + ...mockIssue, + ...item, + }, + uniqueId: 'uniqueid', + issuableType: issuableTypes.issue, + }, + data() { + return { + ...data, + }; + }, + mocks: { + $apollo: { + queries: { + blockingIssuables: { loading }, + ...queries, + }, + }, + }, + stubs: { + GlPopover, + }, + attachTo: document.body, + }), + ); + }; + + it('should render blocked icon', () => { + createWrapper(); + + expect(findGlIcon().exists()).toBe(true); + }); + + it('should display a loading spinner while loading', () => { + createWrapper({ loading: true }); + + expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); + }); + + it('should not query for blocking issuables by default', async () => { + createWrapperWithApollo(); + + expect(findGlPopover().text()).not.toContain(mockBlockingIssue1.title); + }); + + describe('on mouseenter on blocked icon', () => { + it('should query for blocking issuables and render the result', async () => { + createWrapperWithApollo(); + + expect(findGlPopover().text()).not.toContain(mockBlockingIssue1.title); + + await mouseenter(); + + expect(findGlPopover().exists()).toBe(true); + expect(findIssuableTitle().text()).toContain(mockBlockingIssue1.title); + expect(wrapper.vm.skip).toBe(true); + }); + + it('should emit "blocking-issuables-error" event on query error', async () => { + const mockError = new Error('mayday'); + createWrapperWithApollo({ blockingIssuablesSpy: jest.fn().mockRejectedValue(mockError) }); + + await mouseenter(); + + const [ + [ + { + message, + error: { networkError }, + }, + ], + ] = wrapper.emitted('blocking-issuables-error'); + expect(message).toBe('Failed to fetch blocking issues'); + expect(networkError).toBe(mockError); + }); + + describe('with a single blocking issue', () => { + beforeEach(async () => { + createWrapperWithApollo(); + + await mouseenter(); + }); + + it('should render a title of the issuable', async () => { + expect(findIssuableTitle().text()).toBe(mockBlockingIssue1.title); + }); + + it('should render issuable reference and link to the issuable', async () => { + const formattedRef = mockBlockingIssue1.reference.split('/')[1]; + + expect(findGlLink().text()).toBe(formattedRef); + expect(findGlLink().attributes('href')).toBe(mockBlockingIssue1.webUrl); + }); + + it('should render popover title with correct blocking issuable count', async () => { + expect(findPopoverTitle().text()).toBe('Blocked by 1 issue'); + }); + }); + + describe('when issue has a long title', () => { + it('should render a truncated title', async () => { + createWrapperWithApollo({ + blockingIssuablesSpy: jest.fn().mockResolvedValue(mockBlockingIssuablesResponse2), + }); + + await mouseenter(); + + const truncatedTitle = truncate( + mockBlockingIssue2.title, + wrapper.vm.$options.textTruncateWidth, + ); + expect(findIssuableTitle().text()).toBe(truncatedTitle); + }); + }); + + describe('with more than three blocking issues', () => { + beforeEach(async () => { + createWrapperWithApollo({ + item: mockBlockedIssue2, + blockingIssuablesSpy: jest.fn().mockResolvedValue(mockBlockingIssuablesResponse3), + }); + + await mouseenter(); + }); + + it('matches the snapshot', () => { + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('should render popover title with correct blocking issuable count', async () => { + expect(findPopoverTitle().text()).toBe('Blocked by 4 issues'); + }); + + it('should render the number of hidden blocking issuables', () => { + expect(findHiddenBlockingCount().text()).toBe('+ 1 more issue'); + }); + + it('should link to the blocked issue page at the related issue anchor', async () => { + expect(findViewAllIssuableLink().text()).toBe('View all blocking issues'); + expect(findViewAllIssuableLink().attributes('href')).toBe( + `${mockBlockedIssue2.webUrl}#related-issues`, + ); + }); + }); + }); +}); diff --git a/spec/frontend/boards/components/board_content_sidebar_spec.js b/spec/frontend/boards/components/board_content_sidebar_spec.js new file mode 100644 index 00000000000..b4a01c78e6b --- /dev/null +++ b/spec/frontend/boards/components/board_content_sidebar_spec.js @@ -0,0 +1,125 @@ +import { GlDrawer } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import Vuex from 'vuex'; +import { stubComponent } from 'helpers/stub_component'; +import BoardContentSidebar from '~/boards/components/board_content_sidebar.vue'; +import BoardSidebarDueDate from '~/boards/components/sidebar/board_sidebar_due_date.vue'; +import BoardSidebarIssueTitle from '~/boards/components/sidebar/board_sidebar_issue_title.vue'; +import BoardSidebarLabelsSelect from '~/boards/components/sidebar/board_sidebar_labels_select.vue'; +import BoardSidebarMilestoneSelect from '~/boards/components/sidebar/board_sidebar_milestone_select.vue'; +import BoardSidebarSubscription from '~/boards/components/sidebar/board_sidebar_subscription.vue'; +import { ISSUABLE } from '~/boards/constants'; +import { mockIssue, mockIssueGroupPath, mockIssueProjectPath } from '../mock_data'; + +describe('BoardContentSidebar', () => { + let wrapper; + let store; + + const createStore = ({ mockGetters = {}, mockActions = {} } = {}) => { + store = new Vuex.Store({ + state: { + sidebarType: ISSUABLE, + issues: { [mockIssue.id]: mockIssue }, + activeId: mockIssue.id, + issuableType: 'issue', + }, + getters: { + activeIssue: () => mockIssue, + groupPathForActiveIssue: () => mockIssueGroupPath, + projectPathForActiveIssue: () => mockIssueProjectPath, + isSidebarOpen: () => true, + ...mockGetters, + }, + actions: mockActions, + }); + }; + + const createComponent = () => { + wrapper = shallowMount(BoardContentSidebar, { + provide: { + canUpdate: true, + rootPath: '/', + groupId: '#', + }, + store, + stubs: { + GlDrawer: stubComponent(GlDrawer, { + template: '<div><slot name="header"></slot><slot></slot></div>', + }), + }, + mocks: { + $apollo: { + queries: { + participants: { + loading: false, + }, + }, + }, + }, + }); + }; + + beforeEach(() => { + createStore(); + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('confirms we render GlDrawer', () => { + expect(wrapper.find(GlDrawer).exists()).toBe(true); + }); + + it('does not render GlDrawer when isSidebarOpen is false', () => { + createStore({ mockGetters: { isSidebarOpen: () => false } }); + createComponent(); + + expect(wrapper.find(GlDrawer).exists()).toBe(false); + }); + + it('applies an open attribute', () => { + expect(wrapper.find(GlDrawer).props('open')).toBe(true); + }); + + it('renders BoardSidebarLabelsSelect', () => { + expect(wrapper.find(BoardSidebarLabelsSelect).exists()).toBe(true); + }); + + it('renders BoardSidebarIssueTitle', () => { + expect(wrapper.find(BoardSidebarIssueTitle).exists()).toBe(true); + }); + + it('renders BoardSidebarDueDate', () => { + expect(wrapper.find(BoardSidebarDueDate).exists()).toBe(true); + }); + + it('renders BoardSidebarSubscription', () => { + expect(wrapper.find(BoardSidebarSubscription).exists()).toBe(true); + }); + + it('renders BoardSidebarMilestoneSelect', () => { + expect(wrapper.find(BoardSidebarMilestoneSelect).exists()).toBe(true); + }); + + describe('when we emit close', () => { + let toggleBoardItem; + + beforeEach(() => { + toggleBoardItem = jest.fn(); + createStore({ mockActions: { toggleBoardItem } }); + createComponent(); + }); + + it('calls toggleBoardItem with correct parameters', async () => { + wrapper.find(GlDrawer).vm.$emit('close'); + + expect(toggleBoardItem).toHaveBeenCalledTimes(1); + expect(toggleBoardItem).toHaveBeenCalledWith(expect.any(Object), { + boardItem: mockIssue, + sidebarType: ISSUABLE, + }); + }); + }); +}); diff --git a/spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js b/spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js new file mode 100644 index 00000000000..03924bfa8d3 --- /dev/null +++ b/spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js @@ -0,0 +1,58 @@ +/* + To avoid duplicating tests in time_tracker.spec, + this spec only contains a simple test to check rendering. + + A detailed feature spec is used to test time tracking feature + in swimlanes sidebar. +*/ + +import { shallowMount } from '@vue/test-utils'; +import BoardSidebarTimeTracker from '~/boards/components/sidebar/board_sidebar_time_tracker.vue'; +import { createStore } from '~/boards/stores'; +import IssuableTimeTracker from '~/sidebar/components/time_tracking/time_tracker.vue'; + +describe('BoardSidebarTimeTracker', () => { + let wrapper; + let store; + + const createComponent = (options) => { + wrapper = shallowMount(BoardSidebarTimeTracker, { + store, + ...options, + }); + }; + + beforeEach(() => { + store = createStore(); + store.state.boardItems = { + 1: { + timeEstimate: 3600, + totalTimeSpent: 1800, + humanTimeEstimate: '1h', + humanTotalTimeSpent: '30min', + }, + }; + store.state.activeId = '1'; + }); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + it.each([[true], [false]])( + 'renders IssuableTimeTracker with correct spent and estimated time (timeTrackingLimitToHours=%s)', + (timeTrackingLimitToHours) => { + createComponent({ provide: { timeTrackingLimitToHours } }); + + expect(wrapper.find(IssuableTimeTracker).props()).toEqual({ + timeEstimate: 3600, + timeSpent: 1800, + humanTimeEstimate: '1h', + humanTimeSpent: '30min', + limitToHours: timeTrackingLimitToHours, + showCollapsed: false, + }); + }, + ); +}); diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js index 500240d00fc..a8a6423b47c 100644 --- a/spec/frontend/boards/mock_data.js +++ b/spec/frontend/boards/mock_data.js @@ -125,7 +125,7 @@ export const labels = [ export const rawIssue = { title: 'Issue 1', id: 'gid://gitlab/Issue/436', - iid: 27, + iid: '27', dueDate: null, timeEstimate: 0, weight: null, @@ -152,7 +152,7 @@ export const rawIssue = { export const mockIssue = { id: 'gid://gitlab/Issue/436', - iid: 27, + iid: '27', title: 'Issue 1', dueDate: null, timeEstimate: 0, @@ -398,3 +398,93 @@ export const mockActiveGroupProjects = [ { ...mockGroupProject1, archived: false }, { ...mockGroupProject2, archived: false }, ]; + +export const mockIssueGroupPath = 'gitlab-org'; +export const mockIssueProjectPath = `${mockIssueGroupPath}/gitlab-test`; + +export const mockBlockingIssue1 = { + id: 'gid://gitlab/Issue/525', + iid: '6', + title: 'blocking issue title 1', + reference: 'gitlab-org/my-project-1#6', + webUrl: 'http://gdk.test:3000/gitlab-org/my-project-1/-/issues/6', + __typename: 'Issue', +}; + +export const mockBlockingIssue2 = { + id: 'gid://gitlab/Issue/524', + iid: '5', + title: + 'blocking issue title 2 + blocking issue title 2 + blocking issue title 2 + blocking issue title 2', + reference: 'gitlab-org/my-project-1#5', + webUrl: 'http://gdk.test:3000/gitlab-org/my-project-1/-/issues/5', + __typename: 'Issue', +}; + +export const mockBlockingIssue3 = { + id: 'gid://gitlab/Issue/523', + iid: '4', + title: 'blocking issue title 3', + reference: 'gitlab-org/my-project-1#4', + webUrl: 'http://gdk.test:3000/gitlab-org/my-project-1/-/issues/4', + __typename: 'Issue', +}; + +export const mockBlockingIssue4 = { + id: 'gid://gitlab/Issue/522', + iid: '3', + title: 'blocking issue title 4', + reference: 'gitlab-org/my-project-1#3', + webUrl: 'http://gdk.test:3000/gitlab-org/my-project-1/-/issues/3', + __typename: 'Issue', +}; + +export const mockBlockingIssuablesResponse1 = { + data: { + issuable: { + __typename: 'Issue', + id: 'gid://gitlab/Issue/527', + blockingIssuables: { + __typename: 'IssueConnection', + nodes: [mockBlockingIssue1], + }, + }, + }, +}; + +export const mockBlockingIssuablesResponse2 = { + data: { + issuable: { + __typename: 'Issue', + id: 'gid://gitlab/Issue/527', + blockingIssuables: { + __typename: 'IssueConnection', + nodes: [mockBlockingIssue2], + }, + }, + }, +}; + +export const mockBlockingIssuablesResponse3 = { + data: { + issuable: { + __typename: 'Issue', + id: 'gid://gitlab/Issue/527', + blockingIssuables: { + __typename: 'IssueConnection', + nodes: [mockBlockingIssue1, mockBlockingIssue2, mockBlockingIssue3, mockBlockingIssue4], + }, + }, + }, +}; + +export const mockBlockedIssue1 = { + id: '527', + blockedByCount: 1, +}; + +export const mockBlockedIssue2 = { + id: '527', + blockedByCount: 4, + webUrl: 'http://gdk.test:3000/gitlab-org/my-project-1/-/issues/0', +}; diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js index 69d2c8977fb..c0f91d3c629 100644 --- a/spec/frontend/boards/stores/actions_spec.js +++ b/spec/frontend/boards/stores/actions_spec.js @@ -1,3 +1,4 @@ +import * as Sentry from '@sentry/browser'; import testAction from 'helpers/vuex_action_helper'; import { fullBoardId, @@ -1378,6 +1379,51 @@ describe('toggleBoardItem', () => { }); }); +describe('setError', () => { + it('should commit mutation SET_ERROR', () => { + testAction({ + action: actions.setError, + payload: { message: 'mayday' }, + expectedMutations: [ + { + payload: 'mayday', + type: types.SET_ERROR, + }, + ], + }); + }); + + it('should capture error using Sentry when captureError is true', () => { + jest.spyOn(Sentry, 'captureException'); + + const mockError = new Error(); + actions.setError( + { commit: () => {} }, + { + message: 'mayday', + error: mockError, + captureError: true, + }, + ); + + expect(Sentry.captureException).toHaveBeenNthCalledWith(1, mockError); + }); +}); + +describe('unsetError', () => { + it('should commit mutation SET_ERROR with undefined as payload', () => { + testAction({ + action: actions.unsetError, + expectedMutations: [ + { + payload: undefined, + type: types.SET_ERROR, + }, + ], + }); + }); +}); + describe('fetchBacklog', () => { expectNotImplemented(actions.fetchBacklog); }); diff --git a/spec/frontend/boards/stores/mutations_spec.js b/spec/frontend/boards/stores/mutations_spec.js index 33897cc0250..d781f403b93 100644 --- a/spec/frontend/boards/stores/mutations_spec.js +++ b/spec/frontend/boards/stores/mutations_spec.js @@ -666,4 +666,14 @@ describe('Board Store Mutations', () => { expect(state.selectedBoardItems).toEqual([]); }); }); + + describe('SET_ERROR', () => { + it('Should set error state', () => { + state.error = undefined; + + mutations[types.SET_ERROR](state, 'mayday'); + + expect(state.error).toBe('mayday'); + }); + }); }); |