385 lines
12 KiB
JavaScript
385 lines
12 KiB
JavaScript
import { GlTable, GlAlert, GlLoadingIcon, GlDropdown, GlIcon, GlAvatar } from '@gitlab/ui';
|
|
import { mount } from '@vue/test-utils';
|
|
import axios from 'axios';
|
|
import MockAdapter from 'axios-mock-adapter';
|
|
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
|
|
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
|
import mockAlerts from 'jest/vue_shared/alert_details/mocks/alerts.json';
|
|
import AlertManagementTable from '~/alert_management/components/alert_management_table.vue';
|
|
import { visitUrl } from '~/lib/utils/url_utility';
|
|
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
|
|
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
|
import defaultProvideValues from '../mocks/alerts_provide_config.json';
|
|
|
|
jest.mock('~/lib/utils/url_utility', () => ({
|
|
visitUrl: jest.fn().mockName('visitUrlMock'),
|
|
joinPaths: jest.requireActual('~/lib/utils/url_utility').joinPaths,
|
|
setUrlFragment: jest.requireActual('~/lib/utils/url_utility').setUrlFragment,
|
|
}));
|
|
|
|
describe('AlertManagementTable', () => {
|
|
let wrapper;
|
|
let mock;
|
|
|
|
const findAlertsTable = () => wrapper.findComponent(GlTable);
|
|
const findAlerts = () => wrapper.findAll('table tbody tr');
|
|
const findAlert = () => wrapper.findComponent(GlAlert);
|
|
const findLoader = () => wrapper.findComponent(GlLoadingIcon);
|
|
const findStatusDropdown = () => wrapper.findComponent(GlDropdown);
|
|
const findDateFields = () => wrapper.findAllComponents(TimeAgo);
|
|
const findSearch = () => wrapper.findComponent(FilteredSearchBar);
|
|
const findSeverityColumnHeader = () => wrapper.findByTestId('alert-management-severity-sort');
|
|
const findFirstIDField = () => wrapper.findAllByTestId('idField').at(0);
|
|
const findAssignees = () => wrapper.findAllByTestId('assigneesField');
|
|
const findSeverityFields = () => wrapper.findAllByTestId('severityField');
|
|
const findIssueFields = () => wrapper.findAllByTestId('issueField');
|
|
const alertsCount = {
|
|
open: 24,
|
|
triggered: 20,
|
|
acknowledged: 16,
|
|
resolved: 11,
|
|
all: 26,
|
|
};
|
|
const findDeprecationNotice = () => wrapper.findByTestId('alerts-deprecation-warning');
|
|
|
|
function mountComponent({ provide = {}, data = {}, loading = false, stubs = {} } = {}) {
|
|
wrapper = extendedWrapper(
|
|
mount(AlertManagementTable, {
|
|
provide: {
|
|
...defaultProvideValues,
|
|
alertManagementEnabled: true,
|
|
userCanEnableAlertManagement: true,
|
|
hasManagedPrometheus: false,
|
|
...provide,
|
|
},
|
|
data() {
|
|
return data;
|
|
},
|
|
mocks: {
|
|
$apollo: {
|
|
mutate: jest.fn(),
|
|
query: jest.fn(),
|
|
queries: {
|
|
alerts: {
|
|
loading,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
stubs,
|
|
directives: {
|
|
GlTooltip: createMockDirective(),
|
|
},
|
|
}),
|
|
);
|
|
}
|
|
|
|
beforeEach(() => {
|
|
mock = new MockAdapter(axios);
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (wrapper) {
|
|
wrapper.destroy();
|
|
}
|
|
mock.restore();
|
|
});
|
|
|
|
describe('Alerts table', () => {
|
|
it('loading state', () => {
|
|
mountComponent({
|
|
data: { alerts: {}, alertsCount: null },
|
|
loading: true,
|
|
});
|
|
expect(findAlertsTable().exists()).toBe(true);
|
|
expect(findLoader().exists()).toBe(true);
|
|
expect(findAlerts().at(0).classes()).not.toContain('gl-hover-bg-blue-50');
|
|
});
|
|
|
|
it('error state', () => {
|
|
mountComponent({
|
|
data: { alerts: { errors: ['error'] }, alertsCount: null, errored: true },
|
|
loading: false,
|
|
});
|
|
expect(findAlertsTable().exists()).toBe(true);
|
|
expect(findAlertsTable().text()).toContain('No alerts to display');
|
|
expect(findLoader().exists()).toBe(false);
|
|
expect(findAlert().props().variant).toBe('danger');
|
|
expect(findAlerts().at(0).classes()).not.toContain('gl-hover-bg-blue-50');
|
|
});
|
|
|
|
it('empty state', () => {
|
|
mountComponent({
|
|
data: {
|
|
alerts: { list: [], pageInfo: {} },
|
|
alertsCount: { all: 0 },
|
|
errored: false,
|
|
isErrorAlertDismissed: false,
|
|
searchTerm: '',
|
|
assigneeUsername: '',
|
|
},
|
|
loading: false,
|
|
});
|
|
|
|
expect(findAlertsTable().exists()).toBe(true);
|
|
expect(findAlertsTable().text()).toContain('No alerts to display');
|
|
expect(findLoader().exists()).toBe(false);
|
|
expect(findAlert().props().variant).toBe('info');
|
|
expect(findAlerts().at(0).classes()).not.toContain('gl-hover-bg-blue-50');
|
|
});
|
|
|
|
it('has data state', () => {
|
|
mountComponent({
|
|
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
|
|
loading: false,
|
|
});
|
|
expect(findLoader().exists()).toBe(false);
|
|
expect(findAlertsTable().exists()).toBe(true);
|
|
expect(findAlerts()).toHaveLength(mockAlerts.length);
|
|
expect(findAlerts().at(0).classes()).toContain('gl-hover-bg-blue-50');
|
|
});
|
|
|
|
it('displays the alert ID and title formatted correctly', () => {
|
|
mountComponent({
|
|
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
|
|
loading: false,
|
|
});
|
|
|
|
expect(findFirstIDField().exists()).toBe(true);
|
|
expect(findFirstIDField().text()).toBe(`#${mockAlerts[0].iid} ${mockAlerts[0].title}`);
|
|
});
|
|
|
|
it('displays status dropdown', () => {
|
|
mountComponent({
|
|
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
|
|
loading: false,
|
|
});
|
|
expect(findStatusDropdown().exists()).toBe(true);
|
|
});
|
|
|
|
it('does not display a dropdown status header', () => {
|
|
mountComponent({
|
|
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
|
|
loading: false,
|
|
});
|
|
expect(findStatusDropdown().find('.dropdown-title').exists()).toBe(false);
|
|
});
|
|
|
|
it('shows correct severity icons', async () => {
|
|
mountComponent({
|
|
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
|
|
loading: false,
|
|
});
|
|
|
|
await wrapper.vm.$nextTick();
|
|
|
|
expect(wrapper.find(GlTable).exists()).toBe(true);
|
|
expect(findAlertsTable().find(GlIcon).classes('icon-critical')).toBe(true);
|
|
});
|
|
|
|
it('renders severity text', () => {
|
|
mountComponent({
|
|
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
|
|
loading: false,
|
|
});
|
|
|
|
expect(findSeverityFields().at(0).text()).toBe('Critical');
|
|
});
|
|
|
|
it('renders Unassigned when no assignee(s) present', () => {
|
|
mountComponent({
|
|
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
|
|
loading: false,
|
|
});
|
|
|
|
expect(findAssignees().at(0).text()).toBe('Unassigned');
|
|
});
|
|
|
|
it('renders user avatar when assignee present', () => {
|
|
mountComponent({
|
|
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
|
|
loading: false,
|
|
});
|
|
|
|
const avatar = findAssignees().at(1).find(GlAvatar);
|
|
const { src, label } = avatar.attributes();
|
|
const { name, avatarUrl } = mockAlerts[1].assignees.nodes[0];
|
|
|
|
expect(avatar.exists()).toBe(true);
|
|
expect(label).toBe(name);
|
|
expect(src).toBe(avatarUrl);
|
|
});
|
|
|
|
it('navigates to the detail page when alert row is clicked', () => {
|
|
mountComponent({
|
|
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
|
|
loading: false,
|
|
});
|
|
|
|
expect(visitUrl).not.toHaveBeenCalled();
|
|
|
|
findAlerts().at(0).trigger('click');
|
|
expect(visitUrl).toHaveBeenCalledWith('/1527542/details', false);
|
|
});
|
|
|
|
it('navigates to the detail page in new tab when alert row is clicked with the metaKey', () => {
|
|
mountComponent({
|
|
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
|
|
loading: false,
|
|
});
|
|
|
|
expect(visitUrl).not.toHaveBeenCalled();
|
|
|
|
findAlerts().at(0).trigger('click', {
|
|
metaKey: true,
|
|
});
|
|
|
|
expect(visitUrl).toHaveBeenCalledWith('/1527542/details', true);
|
|
});
|
|
|
|
it.each`
|
|
managedAlertsDeprecation | hasManagedPrometheus | isVisible
|
|
${false} | ${false} | ${false}
|
|
${false} | ${true} | ${true}
|
|
${true} | ${false} | ${false}
|
|
${true} | ${true} | ${false}
|
|
`(
|
|
'when the deprecation feature flag is $managedAlertsDeprecation and has managed prometheus is $hasManagedPrometheus',
|
|
({ hasManagedPrometheus, managedAlertsDeprecation, isVisible }) => {
|
|
mountComponent({
|
|
provide: { hasManagedPrometheus, glFeatures: { managedAlertsDeprecation } },
|
|
});
|
|
expect(findDeprecationNotice().exists()).toBe(isVisible);
|
|
},
|
|
);
|
|
|
|
describe('alert issue links', () => {
|
|
beforeEach(() => {
|
|
mountComponent({
|
|
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
|
|
loading: false,
|
|
});
|
|
});
|
|
|
|
it('shows "None" when no link exists', () => {
|
|
expect(findIssueFields().at(0).text()).toBe('None');
|
|
});
|
|
|
|
it('renders a link when one exists with the issue state and title tooltip', () => {
|
|
const issueField = findIssueFields().at(1);
|
|
const tooltip = getBinding(issueField.element, 'gl-tooltip');
|
|
|
|
expect(issueField.text()).toBe(`#1 (closed)`);
|
|
expect(issueField.attributes('href')).toBe('/gitlab-org/gitlab/-/issues/incident/1');
|
|
expect(issueField.attributes('title')).toBe('My test issue');
|
|
expect(tooltip).not.toBe(undefined);
|
|
});
|
|
});
|
|
|
|
describe('handle date fields', () => {
|
|
it('should display time ago dates when values provided', () => {
|
|
mountComponent({
|
|
data: {
|
|
alerts: {
|
|
list: [
|
|
{
|
|
iid: 1,
|
|
status: 'acknowledged',
|
|
startedAt: '2020-03-17T23:18:14.996Z',
|
|
severity: 'high',
|
|
assignees: { nodes: [] },
|
|
},
|
|
],
|
|
},
|
|
alertsCount,
|
|
errored: false,
|
|
},
|
|
loading: false,
|
|
});
|
|
expect(findDateFields().length).toBe(1);
|
|
});
|
|
|
|
it('should not display time ago dates when values not provided', () => {
|
|
mountComponent({
|
|
data: {
|
|
alerts: [
|
|
{
|
|
iid: 1,
|
|
status: 'acknowledged',
|
|
startedAt: null,
|
|
severity: 'high',
|
|
},
|
|
],
|
|
alertsCount,
|
|
errored: false,
|
|
},
|
|
loading: false,
|
|
});
|
|
expect(findDateFields().exists()).toBe(false);
|
|
});
|
|
|
|
describe('New Alert indicator', () => {
|
|
const oldAlert = mockAlerts[0];
|
|
|
|
const newAlert = { ...oldAlert, isNew: true };
|
|
|
|
it('should highlight the row when alert is new', () => {
|
|
mountComponent({
|
|
data: { alerts: { list: [newAlert] }, alertsCount, errored: false },
|
|
loading: false,
|
|
});
|
|
|
|
expect(findAlerts().at(0).classes()).toContain('new-alert');
|
|
});
|
|
|
|
it('should not highlight the row when alert is not new', () => {
|
|
mountComponent({
|
|
data: { alerts: { list: [oldAlert] }, alertsCount, errored: false },
|
|
loading: false,
|
|
});
|
|
|
|
expect(findAlerts().at(0).classes()).not.toContain('new-alert');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('sorting the alert list by column', () => {
|
|
beforeEach(() => {
|
|
mountComponent({
|
|
data: {
|
|
alerts: { list: mockAlerts },
|
|
errored: false,
|
|
sort: 'STARTED_AT_DESC',
|
|
alertsCount,
|
|
},
|
|
loading: false,
|
|
stubs: { GlTable },
|
|
});
|
|
});
|
|
|
|
it('updates sort with new direction and column key', () => {
|
|
findSeverityColumnHeader().trigger('click');
|
|
|
|
expect(wrapper.vm.$data.sort).toBe('SEVERITY_DESC');
|
|
|
|
findSeverityColumnHeader().trigger('click');
|
|
|
|
expect(wrapper.vm.$data.sort).toBe('SEVERITY_ASC');
|
|
});
|
|
});
|
|
|
|
describe('Search', () => {
|
|
beforeEach(() => {
|
|
mountComponent({
|
|
data: { alerts: { list: mockAlerts }, alertsCount, errored: false },
|
|
loading: false,
|
|
});
|
|
});
|
|
|
|
it('renders the search component', () => {
|
|
expect(findSearch().exists()).toBe(true);
|
|
});
|
|
});
|
|
});
|