diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-21 07:08:36 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-21 07:08:36 +0000 |
commit | 48aff82709769b098321c738f3444b9bdaa694c6 (patch) | |
tree | e00c7c43e2d9b603a5a6af576b1685e400410dee /spec/frontend/incidents | |
parent | 879f5329ee916a948223f8f43d77fba4da6cd028 (diff) | |
download | gitlab-ce-48aff82709769b098321c738f3444b9bdaa694c6.tar.gz |
Add latest changes from gitlab-org/gitlab@13-5-stable-eev13.5.0-rc42
Diffstat (limited to 'spec/frontend/incidents')
-rw-r--r-- | spec/frontend/incidents/components/incidents_list_spec.js | 302 | ||||
-rw-r--r-- | spec/frontend/incidents/mocks/incidents.json | 6 |
2 files changed, 116 insertions, 192 deletions
diff --git a/spec/frontend/incidents/components/incidents_list_spec.js b/spec/frontend/incidents/components/incidents_list_spec.js index 307806e0a8a..709f66bb352 100644 --- a/spec/frontend/incidents/components/incidents_list_spec.js +++ b/spec/frontend/incidents/components/incidents_list_spec.js @@ -1,28 +1,28 @@ import { mount } from '@vue/test-utils'; -import { - GlAlert, - GlLoadingIcon, - GlTable, - GlAvatar, - GlPagination, - GlSearchBoxByType, - GlTab, - GlTabs, - GlBadge, - GlEmptyState, -} from '@gitlab/ui'; +import { GlAlert, GlLoadingIcon, GlTable, GlAvatar, GlEmptyState } from '@gitlab/ui'; +import Tracking from '~/tracking'; import { visitUrl, joinPaths, mergeUrlParams } from '~/lib/utils/url_utility'; import IncidentsList from '~/incidents/components/incidents_list.vue'; import SeverityToken from '~/sidebar/components/severity/severity.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; -import { I18N, INCIDENT_STATUS_TABS } from '~/incidents/constants'; +import { + I18N, + TH_CREATED_AT_TEST_ID, + TH_SEVERITY_TEST_ID, + TH_PUBLISHED_TEST_ID, + trackIncidentCreateNewOptions, + trackIncidentListViewsOptions, +} from '~/incidents/constants'; import mockIncidents from '../mocks/incidents.json'; jest.mock('~/lib/utils/url_utility', () => ({ visitUrl: jest.fn().mockName('visitUrlMock'), - joinPaths: jest.fn().mockName('joinPaths'), - mergeUrlParams: jest.fn().mockName('mergeUrlParams'), + joinPaths: jest.fn(), + mergeUrlParams: jest.fn(), + setUrlParams: jest.fn(), + updateHistory: jest.fn(), })); +jest.mock('~/tracking'); describe('Incidents List', () => { let wrapper; @@ -41,23 +41,22 @@ describe('Incidents List', () => { const findAlert = () => wrapper.find(GlAlert); const findLoader = () => wrapper.find(GlLoadingIcon); const findTimeAgo = () => wrapper.findAll(TimeAgoTooltip); - const findDateColumnHeader = () => - wrapper.find('[data-testid="incident-management-created-at-sort"]'); - const findSearch = () => wrapper.find(GlSearchBoxByType); - const findAssingees = () => wrapper.findAll('[data-testid="incident-assignees"]'); + const findAssignees = () => wrapper.findAll('[data-testid="incident-assignees"]'); + const findIncidentSlaHeader = () => wrapper.find('[data-testid="incident-management-sla"]'); const findCreateIncidentBtn = () => wrapper.find('[data-testid="createIncidentBtn"]'); const findClosedIcon = () => wrapper.findAll("[data-testid='incident-closed']"); - const findPagination = () => wrapper.find(GlPagination); - const findStatusFilterTabs = () => wrapper.findAll(GlTab); - const findStatusFilterBadge = () => wrapper.findAll(GlBadge); - const findStatusTabs = () => wrapper.find(GlTabs); const findEmptyState = () => wrapper.find(GlEmptyState); const findSeverity = () => wrapper.findAll(SeverityToken); + const findIncidentSla = () => wrapper.findAll("[data-testid='incident-sla']"); - function mountComponent({ data = { incidents: [], incidentsCount: {} }, loading = false }) { + function mountComponent({ data = {}, loading = false, provide = {} } = {}) { wrapper = mount(IncidentsList, { data() { - return data; + return { + incidents: [], + incidentsCount: {}, + ...data, + }; }, mocks: { $apollo: { @@ -73,14 +72,20 @@ describe('Incidents List', () => { newIssuePath, incidentTemplateName, incidentType, - issuePath: '/project/isssues', + issuePath: '/project/issues', publishedAvailable: true, emptyListSvgPath, + textQuery: '', + authorUsernameQuery: '', + assigneeUsernameQuery: '', + slaFeatureAvailable: true, + ...provide, }, stubs: { GlButton: true, GlAvatar: true, GlEmptyState: true, + ServiceLevelAgreementCell: true, }, }); } @@ -153,14 +158,14 @@ describe('Incidents List', () => { describe('Assignees', () => { it('shows Unassigned when there are no assignees', () => { expect( - findAssingees() + findAssignees() .at(0) .text(), ).toBe(I18N.unassigned); }); it('renders an avatar component when there is an assignee', () => { - const avatar = findAssingees() + const avatar = findAssignees() .at(1) .find(GlAvatar); const { src, label } = avatar.attributes(); @@ -171,13 +176,6 @@ describe('Incidents List', () => { expect(src).toBe(avatarUrl); }); - it('contains a link to the issue details', () => { - findTableRows() - .at(0) - .trigger('click'); - expect(visitUrl).toHaveBeenCalledWith(joinPaths(`/project/isssues/`, mockIncidents[0].iid)); - }); - it('renders a closed icon for closed incidents', () => { expect(findClosedIcon().length).toBe( mockIncidents.filter(({ state }) => state === 'closed').length, @@ -188,6 +186,44 @@ describe('Incidents List', () => { it('renders severity per row', () => { expect(findSeverity().length).toBe(mockIncidents.length); }); + + it('contains a link to the incident details page', async () => { + findTableRows() + .at(0) + .trigger('click'); + expect(visitUrl).toHaveBeenCalledWith( + joinPaths(`/project/issues/incident`, mockIncidents[0].iid), + ); + }); + + describe('Incident SLA field', () => { + it('displays the column when the feature is available', () => { + mountComponent({ + data: { incidents: { list: mockIncidents } }, + provide: { slaFeatureAvailable: true }, + }); + + expect(findIncidentSlaHeader().text()).toContain('Time to SLA'); + }); + + it('does not display the column when the feature is not available', () => { + mountComponent({ + data: { incidents: { list: mockIncidents } }, + provide: { slaFeatureAvailable: false }, + }); + + expect(findIncidentSlaHeader().exists()).toBe(false); + }); + + it('renders an SLA for each incident', () => { + mountComponent({ + data: { incidents: { list: mockIncidents } }, + provide: { slaFeatureAvailable: true }, + }); + + expect(findIncidentSla().length).toBe(mockIncidents.length); + }); + }); }); describe('Create Incident', () => { @@ -198,7 +234,7 @@ describe('Incidents List', () => { }); }); - it('shows the button linking to new incidents page with prefilled incident template when clicked', () => { + it('shows the button linking to new incidents page with pre-filled incident template when clicked', () => { expect(findCreateIncidentBtn().exists()).toBe(true); findCreateIncidentBtn().trigger('click'); expect(mergeUrlParams).toHaveBeenCalledWith( @@ -207,11 +243,10 @@ describe('Incidents List', () => { ); }); - it('sets button loading on click', () => { + it('sets button loading on click', async () => { findCreateIncidentBtn().vm.$emit('click'); - return wrapper.vm.$nextTick().then(() => { - expect(findCreateIncidentBtn().attributes('loading')).toBe('true'); - }); + await wrapper.vm.$nextTick(); + expect(findCreateIncidentBtn().attributes('loading')).toBe('true'); }); it("doesn't show the button when list is empty", () => { @@ -221,175 +256,62 @@ describe('Incidents List', () => { }); expect(findCreateIncidentBtn().exists()).toBe(false); }); + + it('should track create new incident button', async () => { + findCreateIncidentBtn().vm.$emit('click'); + await wrapper.vm.$nextTick(); + expect(Tracking.event).toHaveBeenCalled(); + }); }); - describe('Pagination', () => { + describe('sorting the incident list by column', () => { beforeEach(() => { mountComponent({ - data: { - incidents: { - list: mockIncidents, - pageInfo: { hasNextPage: true, hasPreviousPage: true }, - }, - incidentsCount, - errored: false, - }, + data: { incidents: { list: mockIncidents }, incidentsCount }, loading: false, }); }); - it('should render pagination', () => { - expect(wrapper.find(GlPagination).exists()).toBe(true); - }); - - describe('prevPage', () => { - it('returns prevPage button', () => { - findPagination().vm.$emit('input', 3); - - return wrapper.vm.$nextTick(() => { - expect( - findPagination() - .findAll('.page-item') - .at(0) - .text(), - ).toBe('Prev'); - }); - }); - - it('returns prevPage number', () => { - findPagination().vm.$emit('input', 3); - - return wrapper.vm.$nextTick(() => { - expect(wrapper.vm.prevPage).toBe(2); - }); - }); - - it('returns 0 when it is the first page', () => { - findPagination().vm.$emit('input', 1); - - return wrapper.vm.$nextTick(() => { - expect(wrapper.vm.prevPage).toBe(0); - }); - }); - }); - - describe('nextPage', () => { - it('returns nextPage button', () => { - findPagination().vm.$emit('input', 3); - - return wrapper.vm.$nextTick(() => { - expect( - findPagination() - .findAll('.page-item') - .at(1) - .text(), - ).toBe('Next'); - }); - }); - - it('returns nextPage number', () => { - mountComponent({ - data: { - incidents: { - list: [...mockIncidents, ...mockIncidents, ...mockIncidents], - pageInfo: { hasNextPage: true, hasPreviousPage: true }, - }, - incidentsCount, - errored: false, - }, - loading: false, - }); - findPagination().vm.$emit('input', 1); - - return wrapper.vm.$nextTick(() => { - expect(wrapper.vm.nextPage).toBe(2); - }); - }); - - it('returns `null` when currentPage is already last page', () => { - findStatusTabs().vm.$emit('input', 1); - findPagination().vm.$emit('input', 1); - return wrapper.vm.$nextTick(() => { - expect(wrapper.vm.nextPage).toBeNull(); - }); - }); - }); - - describe('Search', () => { - beforeEach(() => { - mountComponent({ - data: { - incidents: { - list: mockIncidents, - pageInfo: { hasNextPage: true, hasPreviousPage: true }, - }, - incidentsCount, - errored: false, - }, - loading: false, - }); - }); - - it('renders the search component for incidents', () => { - expect(findSearch().exists()).toBe(true); - }); - - it('sets the `searchTerm` graphql variable', () => { - const SEARCH_TERM = 'Simple Incident'; - - findSearch().vm.$emit('input', SEARCH_TERM); - - expect(wrapper.vm.$data.searchTerm).toBe(SEARCH_TERM); - }); - }); - - describe('Status Filter Tabs', () => { - beforeEach(() => { - mountComponent({ - data: { incidents: { list: mockIncidents }, incidentsCount }, - loading: false, - stubs: { - GlTab: true, - }, - }); - }); - - it('should display filter tabs', () => { - const tabs = findStatusFilterTabs().wrappers; - - tabs.forEach((tab, i) => { - expect(tab.attributes('data-testid')).toContain(INCIDENT_STATUS_TABS[i].status); - }); - }); - - it('should display filter tabs with alerts count badge for each status', () => { - const tabs = findStatusFilterTabs().wrappers; - const badges = findStatusFilterBadge(); + const descSort = 'descending'; + const ascSort = 'ascending'; + const noneSort = 'none'; - tabs.forEach((tab, i) => { - const status = INCIDENT_STATUS_TABS[i].status.toLowerCase(); - expect(tab.attributes('data-testid')).toContain(INCIDENT_STATUS_TABS[i].status); - expect(badges.at(i).text()).toContain(incidentsCount[status]); - }); - }); + it.each` + selector | initialSort | firstSort | nextSort + ${TH_CREATED_AT_TEST_ID} | ${descSort} | ${ascSort} | ${descSort} + ${TH_SEVERITY_TEST_ID} | ${noneSort} | ${descSort} | ${ascSort} + ${TH_PUBLISHED_TEST_ID} | ${noneSort} | ${descSort} | ${ascSort} + `('updates sort with new direction', async ({ selector, initialSort, firstSort, nextSort }) => { + const [[attr, value]] = Object.entries(selector); + const columnHeader = () => wrapper.find(`[${attr}="${value}"]`); + expect(columnHeader().attributes('aria-sort')).toBe(initialSort); + columnHeader().trigger('click'); + await wrapper.vm.$nextTick(); + expect(columnHeader().attributes('aria-sort')).toBe(firstSort); + columnHeader().trigger('click'); + await wrapper.vm.$nextTick(); + expect(columnHeader().attributes('aria-sort')).toBe(nextSort); }); }); - describe('sorting the incident list by column', () => { + describe('Snowplow tracking', () => { beforeEach(() => { mountComponent({ - data: { incidents: { list: mockIncidents }, incidentsCount }, + data: { incidents: { list: mockIncidents }, incidentsCount: {} }, loading: false, }); }); - it('updates sort with new direction and column key', () => { - expect(findDateColumnHeader().attributes('aria-sort')).toBe('descending'); + it('should track incident list views', () => { + const { category, action } = trackIncidentListViewsOptions; + expect(Tracking.event).toHaveBeenCalledWith(category, action); + }); - findDateColumnHeader().trigger('click'); - return wrapper.vm.$nextTick(() => { - expect(findDateColumnHeader().attributes('aria-sort')).toBe('ascending'); - }); + it('should track incident creation events', async () => { + findCreateIncidentBtn().vm.$emit('click'); + await wrapper.vm.$nextTick(); + const { category, action } = trackIncidentCreateNewOptions; + expect(Tracking.event).toHaveBeenCalledWith(category, action); }); }); }); diff --git a/spec/frontend/incidents/mocks/incidents.json b/spec/frontend/incidents/mocks/incidents.json index 42b3d6d3eb6..07c87a5d43d 100644 --- a/spec/frontend/incidents/mocks/incidents.json +++ b/spec/frontend/incidents/mocks/incidents.json @@ -5,7 +5,8 @@ "createdAt": "2020-06-03T15:46:08Z", "assignees": {}, "state": "opened", - "severity": "CRITICAL" + "severity": "CRITICAL", + "slaDueAt": "2020-06-04T12:46:08Z" }, { "iid": "14", @@ -22,7 +23,8 @@ ] }, "state": "opened", - "severity": "HIGH" + "severity": "HIGH", + "slaDueAt": null }, { "iid": "13", |