summaryrefslogtreecommitdiff
path: root/spec/frontend/incidents/components/incidents_list_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/incidents/components/incidents_list_spec.js')
-rw-r--r--spec/frontend/incidents/components/incidents_list_spec.js302
1 files changed, 112 insertions, 190 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);
});
});
});