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