diff options
Diffstat (limited to 'spec/frontend/issues/list/components/issues_list_app_spec.js')
-rw-r--r-- | spec/frontend/issues/list/components/issues_list_app_spec.js | 120 |
1 files changed, 99 insertions, 21 deletions
diff --git a/spec/frontend/issues/list/components/issues_list_app_spec.js b/spec/frontend/issues/list/components/issues_list_app_spec.js index d92ba527b5c..3f2c3c3ec5f 100644 --- a/spec/frontend/issues/list/components/issues_list_app_spec.js +++ b/spec/frontend/issues/list/components/issues_list_app_spec.js @@ -8,8 +8,6 @@ import VueApollo from 'vue-apollo'; import VueRouter from 'vue-router'; 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 getIssuesWithoutCrmQuery from 'ee_else_ce/issues/list/queries/get_issues_without_crm.query.graphql'; -import getIssuesCountsWithoutCrmQuery from 'ee_else_ce/issues/list/queries/get_issues_counts_without_crm.query.graphql'; import createMockApollo from 'helpers/mock_apollo_helper'; import setWindowLocation from 'helpers/set_window_location_helper'; import { TEST_HOST } from 'helpers/test_constants'; @@ -38,9 +36,11 @@ import { TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_AUTHOR, TOKEN_TYPE_CONFIDENTIAL, + TOKEN_TYPE_CONTACT, TOKEN_TYPE_LABEL, TOKEN_TYPE_MILESTONE, TOKEN_TYPE_MY_REACTION, + TOKEN_TYPE_ORGANIZATION, TOKEN_TYPE_RELEASE, TOKEN_TYPE_TYPE, urlSortParams, @@ -67,6 +67,9 @@ describe('CE IssuesListApp component', () => { autocompleteAwardEmojisPath: 'autocomplete/award/emojis/path', calendarPath: 'calendar/path', canBulkUpdate: false, + canCreateProjects: false, + canReadCrmContact: false, + canReadCrmOrganization: false, emptyStateSvgPath: 'empty-state.svg', exportCsvPath: 'export/csv/path', fullPath: 'path/to/project', @@ -77,6 +80,7 @@ describe('CE IssuesListApp component', () => { hasIssueWeightsFeature: true, hasIterationsFeature: true, hasMultipleIssueAssigneesFeature: true, + hasScopedLabelsFeature: true, initialEmail: 'email@example.com', initialSort: CREATED_DESC, isAnonymousSearchDisabled: false, @@ -86,6 +90,7 @@ describe('CE IssuesListApp component', () => { isSignedIn: true, jiraIntegrationPath: 'jira/integration/path', newIssuePath: 'new/issue/path', + newProjectPath: 'new/project/path', releasesPath: 'releases/path', rssPath: 'rss/path', showNewIssueLink: true, @@ -100,6 +105,9 @@ describe('CE IssuesListApp component', () => { defaultQueryResponse.data.project.issues.nodes[0].weight = 5; } + const mockIssuesQueryResponse = jest.fn().mockResolvedValue(defaultQueryResponse); + const mockIssuesCountsQueryResponse = jest.fn().mockResolvedValue(getIssuesCountsQueryResponse); + const findCsvImportExportButtons = () => wrapper.findComponent(CsvImportExportButtons); const findIssuableByEmail = () => wrapper.findComponent(IssuableByEmail); const findGlButton = () => wrapper.findComponent(GlButton); @@ -113,16 +121,15 @@ describe('CE IssuesListApp component', () => { const mountComponent = ({ provide = {}, data = {}, - issuesQueryResponse = jest.fn().mockResolvedValue(defaultQueryResponse), - issuesCountsQueryResponse = jest.fn().mockResolvedValue(getIssuesCountsQueryResponse), + issuesQueryResponse = mockIssuesQueryResponse, + issuesCountsQueryResponse = mockIssuesCountsQueryResponse, sortPreferenceMutationResponse = jest.fn().mockResolvedValue(setSortPreferenceMutationResponse), + stubs = {}, mountFn = shallowMount, } = {}) => { const requestHandlers = [ [getIssuesQuery, issuesQueryResponse], [getIssuesCountsQuery, issuesCountsQueryResponse], - [getIssuesWithoutCrmQuery, issuesQueryResponse], - [getIssuesCountsWithoutCrmQuery, issuesCountsQueryResponse], [setSortPreferenceMutation, sortPreferenceMutationResponse], ]; @@ -136,6 +143,7 @@ describe('CE IssuesListApp component', () => { data() { return data; }, + stubs, }); }; @@ -156,6 +164,22 @@ describe('CE IssuesListApp component', () => { return waitForPromises(); }); + it('queries list with types `ISSUE` and `INCIDENT', () => { + const expectedTypes = ['ISSUE', 'INCIDENT', 'TEST_CASE']; + + expect(mockIssuesQueryResponse).toHaveBeenCalledWith( + expect.objectContaining({ + types: expectedTypes, + }), + ); + + expect(mockIssuesCountsQueryResponse).toHaveBeenCalledWith( + expect.objectContaining({ + types: expectedTypes, + }), + ); + }); + it('renders', () => { expect(findIssuableList().props()).toMatchObject({ namespace: defaultProvide.fullPath, @@ -301,17 +325,23 @@ describe('CE IssuesListApp component', () => { describe('initial url params', () => { describe('page', () => { it('page_after is set from the url params', () => { - setWindowLocation('?page_after=randomCursorString'); + setWindowLocation('?page_after=randomCursorString&first_page_size=20'); wrapper = mountComponent(); - expect(wrapper.vm.$route.query).toMatchObject({ page_after: 'randomCursorString' }); + expect(wrapper.vm.$route.query).toMatchObject({ + page_after: 'randomCursorString', + first_page_size: '20', + }); }); it('page_before is set from the url params', () => { - setWindowLocation('?page_before=anotherRandomCursorString'); + setWindowLocation('?page_before=anotherRandomCursorString&last_page_size=20'); wrapper = mountComponent(); - expect(wrapper.vm.$route.query).toMatchObject({ page_before: 'anotherRandomCursorString' }); + expect(wrapper.vm.$route.query).toMatchObject({ + page_before: 'anotherRandomCursorString', + last_page_size: '20', + }); }); }); @@ -515,10 +545,12 @@ describe('CE IssuesListApp component', () => { it('shows empty state', () => { expect(findGlEmptyState().props()).toMatchObject({ - description: IssuesListApp.i18n.noIssuesSignedInDescription, title: IssuesListApp.i18n.noIssuesSignedInTitle, svgPath: defaultProvide.emptyStateSvgPath, }); + expect(findGlEmptyState().text()).toContain( + IssuesListApp.i18n.noIssuesSignedInDescription, + ); }); it('shows "New issue" and import/export buttons', () => { @@ -532,11 +564,11 @@ describe('CE IssuesListApp component', () => { it('shows Jira integration information', () => { const paragraphs = wrapper.findAll('p'); - expect(paragraphs.at(1).text()).toContain(IssuesListApp.i18n.jiraIntegrationTitle); - expect(paragraphs.at(2).text()).toContain( + expect(paragraphs.at(2).text()).toContain(IssuesListApp.i18n.jiraIntegrationTitle); + expect(paragraphs.at(3).text()).toContain( 'Enable the Jira integration to view your Jira issues in GitLab.', ); - expect(paragraphs.at(3).text()).toContain( + expect(paragraphs.at(4).text()).toContain( IssuesListApp.i18n.jiraIntegrationSecondaryMessage, ); expect(findGlLink().text()).toBe('Enable the Jira integration'); @@ -544,6 +576,29 @@ describe('CE IssuesListApp component', () => { }); }); + describe('when user is logged in and can create projects', () => { + beforeEach(() => { + wrapper = mountComponent({ + provide: { canCreateProjects: true, hasAnyIssues: false, isSignedIn: true }, + stubs: { GlEmptyState }, + }); + }); + + it('shows empty state with additional description about creating projects', () => { + expect(findGlEmptyState().text()).toContain( + IssuesListApp.i18n.noIssuesSignedInDescription, + ); + expect(findGlEmptyState().text()).toContain( + IssuesListApp.i18n.noGroupIssuesSignedInDescription, + ); + }); + + it('shows "New project" button', () => { + expect(findGlButton().text()).toBe(IssuesListApp.i18n.newProjectLabel); + expect(findGlButton().attributes('href')).toBe(defaultProvide.newProjectPath); + }); + }); + describe('when user is logged out', () => { beforeEach(() => { wrapper = mountComponent({ @@ -587,6 +642,21 @@ describe('CE IssuesListApp component', () => { }); }); + describe('when user does not have CRM enabled', () => { + beforeEach(() => { + wrapper = mountComponent({ + provide: { canReadCrmContact: false, canReadCrmOrganization: false }, + }); + }); + + it('does not render Contact or Organization tokens', () => { + expect(findIssuableList().props('searchTokens')).not.toMatchObject([ + { type: TOKEN_TYPE_CONTACT }, + { type: TOKEN_TYPE_ORGANIZATION }, + ]); + }); + }); + describe('when all tokens are available', () => { const originalGon = window.gon; @@ -599,7 +669,13 @@ describe('CE IssuesListApp component', () => { current_user_avatar_url: mockCurrentUser.avatar_url, }; - wrapper = mountComponent({ provide: { isSignedIn: true } }); + wrapper = mountComponent({ + provide: { + canReadCrmContact: true, + canReadCrmOrganization: true, + isSignedIn: true, + }, + }); }); afterEach(() => { @@ -615,9 +691,11 @@ describe('CE IssuesListApp component', () => { { type: TOKEN_TYPE_ASSIGNEE, preloadedAuthors }, { type: TOKEN_TYPE_AUTHOR, preloadedAuthors }, { type: TOKEN_TYPE_CONFIDENTIAL }, + { type: TOKEN_TYPE_CONTACT }, { type: TOKEN_TYPE_LABEL }, { type: TOKEN_TYPE_MILESTONE }, { type: TOKEN_TYPE_MY_REACTION }, + { type: TOKEN_TYPE_ORGANIZATION }, { type: TOKEN_TYPE_RELEASE }, { type: TOKEN_TYPE_TYPE }, ]); @@ -675,10 +753,10 @@ describe('CE IssuesListApp component', () => { }); describe.each` - event | paramName | paramValue - ${'next-page'} | ${'page_after'} | ${'endCursor'} - ${'previous-page'} | ${'page_before'} | ${'startCursor'} - `('when "$event" event is emitted by IssuableList', ({ event, paramName, paramValue }) => { + event | params + ${'next-page'} | ${{ page_after: 'endCursor', page_before: undefined, first_page_size: 20, last_page_size: undefined }} + ${'previous-page'} | ${{ page_after: undefined, page_before: 'startCursor', first_page_size: undefined, last_page_size: 20 }} + `('when "$event" event is emitted by IssuableList', ({ event, params }) => { beforeEach(() => { wrapper = mountComponent({ data: { @@ -697,9 +775,9 @@ describe('CE IssuesListApp component', () => { expect(scrollUp).toHaveBeenCalled(); }); - it(`updates url with "${paramName}" param`, () => { + it(`updates url`, () => { expect(wrapper.vm.$router.push).toHaveBeenCalledWith({ - query: expect.objectContaining({ [paramName]: paramValue }), + query: expect.objectContaining(params), }); }); }); |