Commit dc5d84f1 authored by Alexander Turinske's avatar Alexander Turinske Committed by Natalia Tepluhina

Update alerts list to fail gracefully on GraphQL failure

- update tests
parent f85dc2a0
......@@ -56,7 +56,7 @@ export default {
},
update: ({ project }) => project?.alertManagementAlerts.nodes || [],
result({ data }) {
this.pageInfo = data?.project?.alertManagementAlerts?.pageInfo;
this.pageInfo = data?.project?.alertManagementAlerts?.pageInfo || {};
},
error() {
this.errored = true;
......
---
title: Update alerts list to fail gracefully on GraphQL failure
merge_request: 55817
author:
type: fixed
......@@ -3,7 +3,7 @@ import { shallowMount } from '@vue/test-utils';
import AlertStatus from 'ee/threat_monitoring/components/alerts/alert_status.vue';
import waitForPromises from 'helpers/wait_for_promises';
import updateAlertStatusMutation from '~/graphql_shared/mutations/alert_status_update.mutation.graphql';
import { mockAlerts } from '../../mock_data';
import { mockAlerts } from '../../mocks/mock_data';
const mockAlert = mockAlerts[0];
......
import { GlIntersectionObserver, GlSkeletonLoading } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { createLocalVue, mount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import AlertFilters from 'ee/threat_monitoring/components/alerts/alert_filters.vue';
import AlertStatus from 'ee/threat_monitoring/components/alerts/alert_status.vue';
import AlertsList from 'ee/threat_monitoring/components/alerts/alerts_list.vue';
import { DEFAULT_FILTERS } from 'ee/threat_monitoring/components/alerts/constants';
import createMockApollo from 'helpers/mock_apollo_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { mockAlerts } from '../../mock_data';
import getAlertsQuery from '~/graphql_shared/queries/get_alerts.query.graphql';
import { defaultQuerySpy, emptyQuerySpy, loadingQuerySpy } from '../../mocks/mock_apollo';
import { mockAlerts, mockPageInfo } from '../../mocks/mock_data';
const alerts = mockAlerts;
const pageInfo = {
endCursor: 'eyJpZCI6IjIwIiwic3RhcnRlZF9hdCI6IjIwMjAtMTItMDMgMjM6MTI6NDkuODM3Mjc1MDAwIFVUQyJ9',
hasNextPage: true,
hasPreviousPage: false,
startCursor: 'eyJpZCI6IjM5Iiwic3RhcnRlZF9hdCI6IjIwMjAtMTItMDQgMTg6MDE6MDcuNzY1ODgyMDAwIFVUQyJ9',
};
let localVue;
describe('AlertsList component', () => {
let wrapper;
const refetchSpy = jest.fn();
const apolloMock = {
const DEFAULT_PROJECT_PATH = '#';
const DEFAULT_SORT = 'STARTED_AT_DESC';
const PAGE_SIZE = 20;
const defaultProps = { filters: DEFAULT_FILTERS };
let querySpy;
const createMockApolloProvider = (query) => {
localVue.use(VueApollo);
return createMockApollo([[getAlertsQuery, query]]);
};
const shallowApolloMock = {
queries: {
alerts: {
fetchMore: jest.fn().mockResolvedValue(),
......@@ -28,7 +36,6 @@ describe('AlertsList component', () => {
},
},
};
const defaultProps = { filters: DEFAULT_FILTERS };
const findAlertFilters = () => wrapper.findComponent(AlertFilters);
const findUnconfiguredAlert = () => wrapper.findByTestId('threat-alerts-unconfigured');
......@@ -43,16 +50,31 @@ describe('AlertsList component', () => {
const findGlIntersectionObserver = () => wrapper.findComponent(GlIntersectionObserver);
const findGlSkeletonLoading = () => wrapper.findComponent(GlSkeletonLoading);
const createWrapper = ({ $apollo = apolloMock, data = {}, stubs = {} } = {}) => {
wrapper = extendedWrapper(
mount(AlertsList, {
const createWrapper = ({ $apollo, apolloSpy = defaultQuerySpy, data, stubs = {} } = {}) => {
let apolloOptions;
if ($apollo) {
apolloOptions = {
mocks: {
$apollo,
},
data,
};
} else {
localVue = createLocalVue();
querySpy = apolloSpy;
const mockApollo = createMockApolloProvider(querySpy);
apolloOptions = {
localVue,
apolloProvider: mockApollo,
};
}
wrapper = extendedWrapper(
mount(AlertsList, {
propsData: defaultProps,
provide: {
documentationPath: '#',
projectPath: '#',
projectPath: DEFAULT_PROJECT_PATH,
},
stubs: {
AlertStatus: true,
......@@ -62,9 +84,7 @@ describe('AlertsList component', () => {
GlIntersectionObserver: true,
...stubs,
},
data() {
return data;
},
...apolloOptions,
}),
);
};
......@@ -76,7 +96,7 @@ describe('AlertsList component', () => {
describe('default state', () => {
beforeEach(() => {
createWrapper({ data: { alerts, pageInfo } });
createWrapper();
});
it('shows threat monitoring alert filters', () => {
......@@ -84,7 +104,15 @@ describe('AlertsList component', () => {
});
it('does have the default filters initially', () => {
expect(wrapper.vm.filters).toEqual(DEFAULT_FILTERS);
expect(querySpy).toHaveBeenCalledTimes(1);
expect(querySpy).toHaveBeenCalledWith(
expect.objectContaining({
firstPageSize: PAGE_SIZE,
projectPath: DEFAULT_PROJECT_PATH,
sort: DEFAULT_SORT,
...DEFAULT_FILTERS,
}),
);
});
it('does update its filters on filter event emitted', async () => {
......@@ -146,7 +174,7 @@ describe('AlertsList component', () => {
describe('empty state', () => {
beforeEach(() => {
createWrapper({ data: { alerts: [] } });
createWrapper({ apolloSpy: emptyQuerySpy });
});
it('does show the empty state', () => {
......@@ -160,10 +188,7 @@ describe('AlertsList component', () => {
describe('loading state', () => {
beforeEach(() => {
const apolloMockLoading = {
queries: { alerts: { loading: true } },
};
createWrapper({ $apollo: apolloMockLoading });
createWrapper({ apolloSpy: loadingQuerySpy });
});
it('does show the loading state', () => {
......@@ -207,10 +232,11 @@ describe('AlertsList component', () => {
describe('loading more alerts', () => {
it('does request more data', async () => {
createWrapper({
data: {
alerts,
pageInfo,
},
$apollo: shallowApolloMock,
data: () => ({
alerts: mockAlerts,
pageInfo: mockPageInfo,
}),
});
findGlIntersectionObserver().vm.$emit('appear');
await wrapper.vm.$nextTick();
......@@ -220,9 +246,12 @@ describe('AlertsList component', () => {
describe('changing alert status', () => {
beforeEach(() => {
createWrapper();
wrapper.setData({
alerts,
createWrapper({
$apollo: shallowApolloMock,
data: () => ({
alerts: mockAlerts,
pageInfo: mockPageInfo,
}),
});
});
......
......@@ -6,7 +6,7 @@ import {
ALL_ENVIRONMENT_NAME,
} from 'ee/threat_monitoring/constants';
import createStore from 'ee/threat_monitoring/store';
import { mockEnvironmentsResponse } from '../mock_data';
import { mockEnvironmentsResponse } from '../mocks/mock_data';
const mockEnvironments = mockEnvironmentsResponse.environments;
const currentEnvironment = mockEnvironments[1];
......
......@@ -5,7 +5,7 @@ import PolicyDrawer from 'ee/threat_monitoring/components/policy_editor/policy_d
import { PREDEFINED_NETWORK_POLICIES } from 'ee/threat_monitoring/constants';
import createStore from 'ee/threat_monitoring/store';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { mockPoliciesResponse } from '../mock_data';
import { mockPoliciesResponse } from '../mocks/mock_data';
const mockData = mockPoliciesResponse.map((policy) => convertObjectPropsToCamelCase(policy));
......
......@@ -2,7 +2,7 @@ import { GlAreaChart } from '@gitlab/ui/dist/charts';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { TOTAL_REQUESTS, ANOMALOUS_REQUESTS } from 'ee/threat_monitoring/components/constants';
import StatisticsHistory from 'ee/threat_monitoring/components/statistics_history.vue';
import { mockNominalHistory, mockAnomalousHistory } from '../mock_data';
import { mockNominalHistory, mockAnomalousHistory } from '../mocks/mock_data';
let resizeCallback = null;
const MockResizeObserverDirective = {
......
......@@ -4,7 +4,7 @@ import ThreatMonitoringFilters from 'ee/threat_monitoring/components/threat_moni
import createStore from 'ee/threat_monitoring/store';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
import { timeRanges, defaultTimeRange } from '~/vue_shared/constants';
import { mockEnvironmentsResponse } from '../mock_data';
import { mockEnvironmentsResponse } from '../mocks/mock_data';
const mockEnvironments = mockEnvironmentsResponse.environments;
......
......@@ -5,7 +5,7 @@ import StatisticsSummary from 'ee/threat_monitoring/components/statistics_summar
import ThreatMonitoringSection from 'ee/threat_monitoring/components/threat_monitoring_section.vue';
import createStore from 'ee/threat_monitoring/store';
import { mockNominalHistory, mockAnomalousHistory } from '../mock_data';
import { mockNominalHistory, mockAnomalousHistory } from '../mocks/mock_data';
describe('ThreatMonitoringSection component', () => {
let store;
......
import { mockAlerts, mockPageInfo } from './mock_data';
export const defaultQuerySpy = jest.fn().mockResolvedValue({
data: { project: { alertManagementAlerts: { nodes: mockAlerts, pageInfo: mockPageInfo } } },
});
export const emptyQuerySpy = jest.fn().mockResolvedValue({
data: {
project: {
alertManagementAlerts: {
nodes: [],
pageInfo: { endCursor: '', hasNextPage: false, hasPreviousPage: false, startCursor: '' },
},
},
},
});
export const loadingQuerySpy = jest.fn().mockReturnValue(new Promise(() => {}));
......@@ -94,14 +94,22 @@ export const formattedMockNetworkPolicyStatisticsResponse = {
export const mockAlerts = [
{
iid: '01',
assignees: { nodes: [] },
eventCount: '1',
issueIid: null,
issue: { iid: '1', state: '', title: '' },
title: 'Issue 01',
severity: 'HIGH',
status: 'TRIGGERED',
startedAt: '2020-11-19T18:36:23Z',
},
{
iid: '02',
eventCount: '2',
assignees: { nodes: [] },
issueIid: null,
issue: { iid: '2', state: '', title: '' },
severity: 'CRITICAL',
title: 'Issue 02',
status: 'ACKNOWLEDGED',
startedAt: '2020-11-16T21:59:28Z',
......@@ -109,15 +117,30 @@ export const mockAlerts = [
{
iid: '03',
eventCount: '3',
assignees: { nodes: [] },
issueIid: null,
issue: { iid: '3', state: '', title: '' },
severity: 'MEDIUM',
title: 'Issue 03',
status: 'RESOLVED',
startedAt: '2020-11-13T20:03:04Z',
},
{
iid: '04',
assignees: { nodes: [] },
issueIid: null,
issue: { iid: '4', state: '', title: '' },
severity: 'LOW',
eventCount: '4',
title: 'Issue 04',
status: 'IGNORED',
startedAt: '2020-10-29T13:37:55Z',
},
];
export const mockPageInfo = {
endCursor: 'eyJpZCI6IjIwIiwic3RhcnRlZF9hdCI6IjIwMjAtMTItMDMgMjM6MTI6NDkuODM3Mjc1MDAwIFVUQyJ9',
hasNextPage: true,
hasPreviousPage: false,
startCursor: 'eyJpZCI6IjM5Iiwic3RhcnRlZF9hdCI6IjIwMjAtMTItMDQgMTg6MDE6MDcuNzY1ODgyMDAwIFVUQyJ9',
};
......@@ -9,7 +9,7 @@ import httpStatus from '~/lib/utils/http_status';
import { joinPaths } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import { mockPoliciesResponse } from '../../../mock_data';
import { mockPoliciesResponse } from '../../../mocks/mock_data';
jest.mock('~/flash');
......
......@@ -8,7 +8,7 @@ import { deprecatedCreateFlash as createFlash } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status';
import { mockEnvironmentsResponse } from '../../../mock_data';
import { mockEnvironmentsResponse } from '../../../mocks/mock_data';
jest.mock('~/flash');
......
......@@ -8,7 +8,7 @@ import { deprecatedCreateFlash as createFlash } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status';
import { mockWafStatisticsResponse } from '../../../mock_data';
import { mockWafStatisticsResponse } from '../../../mocks/mock_data';
jest.mock('~/flash');
......
import * as types from 'ee/threat_monitoring/store/modules/threat_monitoring_statistics/mutation_types';
import mutationsFactory from 'ee/threat_monitoring/store/modules/threat_monitoring_statistics/mutations';
import { mockWafStatisticsResponse } from '../../../mock_data';
import { mockWafStatisticsResponse } from '../../../mocks/mock_data';
describe('threatMonitoringStatistics mutations', () => {
let state;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment