mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[SecuritySolution] Add timeline middleware tests (#178009)
## Summary In the previous work for https://github.com/elastic/kibana/issues/175427, we replaced redux-observable with plain redux middlewares. The code that was based on redux-observable wasn't tested, so as part of the refactoring we're now adding tests to all timeline middlewares in this PR. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
parent
bb33687d37
commit
8f39000270
7 changed files with 679 additions and 5 deletions
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createMockStore } from '../../../common/mock';
|
||||
import { selectTimelineById } from '../selectors';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
|
||||
import {
|
||||
setChanged,
|
||||
updateKqlMode,
|
||||
showTimeline,
|
||||
applyKqlFilterQuery,
|
||||
addProvider,
|
||||
dataProviderEdited,
|
||||
removeColumn,
|
||||
removeProvider,
|
||||
updateColumns,
|
||||
updateEqlOptions,
|
||||
updateDataProviderEnabled,
|
||||
updateDataProviderExcluded,
|
||||
updateDataProviderType,
|
||||
updateProviders,
|
||||
updateRange,
|
||||
updateSort,
|
||||
upsertColumn,
|
||||
updateDataView,
|
||||
updateTitleAndDescription,
|
||||
setExcludedRowRendererIds,
|
||||
setFilters,
|
||||
setSavedQueryId,
|
||||
updateSavedSearch,
|
||||
} from '../actions';
|
||||
import { timelineChangedTypes } from './timeline_changed';
|
||||
|
||||
jest.mock('../actions', () => {
|
||||
const actual = jest.requireActual('../actions');
|
||||
return {
|
||||
...actual,
|
||||
setChanged: jest.fn().mockImplementation((...args) => actual.setChanged(...args)),
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* This is a copy of the timeline changed types from the actual middleware.
|
||||
* The purpose of this copy is to enforce changes to the original to fail.
|
||||
* These changes will need to be applied to the copy to pass the tests.
|
||||
* That way, we are preventing accidental changes to the original.
|
||||
*/
|
||||
const timelineChangedTypesCopy = [
|
||||
applyKqlFilterQuery.type,
|
||||
addProvider.type,
|
||||
dataProviderEdited.type,
|
||||
removeProvider.type,
|
||||
setExcludedRowRendererIds.type,
|
||||
setFilters.type,
|
||||
setSavedQueryId.type,
|
||||
updateDataProviderEnabled.type,
|
||||
updateDataProviderExcluded.type,
|
||||
updateDataProviderType.type,
|
||||
updateEqlOptions.type,
|
||||
updateKqlMode.type,
|
||||
updateProviders.type,
|
||||
updateTitleAndDescription.type,
|
||||
|
||||
updateDataView.type,
|
||||
removeColumn.type,
|
||||
updateColumns.type,
|
||||
updateSort.type,
|
||||
updateRange.type,
|
||||
upsertColumn.type,
|
||||
|
||||
updateSavedSearch.type,
|
||||
];
|
||||
|
||||
const setChangedMock = setChanged as unknown as jest.Mock;
|
||||
|
||||
describe('Timeline changed middleware', () => {
|
||||
let store = createMockStore();
|
||||
|
||||
beforeEach(() => {
|
||||
store = createMockStore();
|
||||
setChangedMock.mockClear();
|
||||
});
|
||||
|
||||
it('should mark a timeline as changed for some actions', () => {
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test).kqlMode).toEqual('filter');
|
||||
|
||||
store.dispatch(updateKqlMode({ id: TimelineId.test, kqlMode: 'search' }));
|
||||
|
||||
expect(setChangedMock).toHaveBeenCalledWith({ id: TimelineId.test, changed: true });
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test).kqlMode).toEqual('search');
|
||||
});
|
||||
|
||||
it('should check that all correct actions are used to check for changes', () => {
|
||||
timelineChangedTypesCopy.forEach((changedType) => {
|
||||
expect(timelineChangedTypes.has(changedType)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not mark a timeline as changed for some actions', () => {
|
||||
store.dispatch(showTimeline({ id: TimelineId.test, show: true }));
|
||||
expect(setChangedMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -36,7 +36,7 @@ import {
|
|||
/**
|
||||
* All action types that will mark a timeline as changed
|
||||
*/
|
||||
const timelineChangedTypes = new Set([
|
||||
export const timelineChangedTypes = new Set([
|
||||
applyKqlFilterQuery.type,
|
||||
addProvider.type,
|
||||
dataProviderEdited.type,
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createMockStore, kibanaMock } from '../../../common/mock';
|
||||
import { selectTimelineById } from '../selectors';
|
||||
import { persistFavorite } from '../../containers/api';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { refreshTimelines } from './helpers';
|
||||
|
||||
import {
|
||||
startTimelineSaving,
|
||||
endTimelineSaving,
|
||||
updateIsFavorite,
|
||||
showCallOutUnauthorizedMsg,
|
||||
updateTimeline,
|
||||
} from '../actions';
|
||||
|
||||
jest.mock('../actions', () => {
|
||||
const actual = jest.requireActual('../actions');
|
||||
const endTLSaving = jest.fn((...args) => actual.endTimelineSaving(...args));
|
||||
(endTLSaving as unknown as { match: Function }).match = () => false;
|
||||
return {
|
||||
...actual,
|
||||
showCallOutUnauthorizedMsg: jest
|
||||
.fn()
|
||||
.mockImplementation((...args) => actual.showCallOutUnauthorizedMsg(...args)),
|
||||
startTimelineSaving: jest
|
||||
.fn()
|
||||
.mockImplementation((...args) => actual.startTimelineSaving(...args)),
|
||||
endTimelineSaving: endTLSaving,
|
||||
};
|
||||
});
|
||||
jest.mock('../../containers/api');
|
||||
jest.mock('./helpers');
|
||||
|
||||
const startTimelineSavingMock = startTimelineSaving as unknown as jest.Mock;
|
||||
const endTimelineSavingMock = endTimelineSaving as unknown as jest.Mock;
|
||||
const showCallOutUnauthorizedMsgMock = showCallOutUnauthorizedMsg as unknown as jest.Mock;
|
||||
|
||||
describe('Timeline favorite middleware', () => {
|
||||
let store = createMockStore(undefined, undefined, kibanaMock);
|
||||
const newVersion = 'new_version';
|
||||
const newSavedObjectId = 'new_so_id';
|
||||
|
||||
beforeEach(() => {
|
||||
store = createMockStore(undefined, undefined, kibanaMock);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should persist a timeline favorite when a favorite action is dispatched', async () => {
|
||||
(persistFavorite as jest.Mock).mockResolvedValue({
|
||||
data: {
|
||||
persistFavorite: {
|
||||
code: 200,
|
||||
favorite: [{}],
|
||||
savedObjectId: newSavedObjectId,
|
||||
version: newVersion,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test).isFavorite).toEqual(false);
|
||||
await store.dispatch(updateIsFavorite({ id: TimelineId.test, isFavorite: true }));
|
||||
|
||||
expect(startTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(refreshTimelines as unknown as jest.Mock).toHaveBeenCalled();
|
||||
expect(endTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test)).toEqual(
|
||||
expect.objectContaining({
|
||||
isFavorite: true,
|
||||
savedObjectId: newSavedObjectId,
|
||||
version: newVersion,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should persist a timeline un-favorite when a favorite action is dispatched for a favorited timeline', async () => {
|
||||
store.dispatch(
|
||||
updateTimeline({
|
||||
id: TimelineId.test,
|
||||
timeline: {
|
||||
...selectTimelineById(store.getState(), TimelineId.test),
|
||||
isFavorite: true,
|
||||
},
|
||||
})
|
||||
);
|
||||
(persistFavorite as jest.Mock).mockResolvedValue({
|
||||
data: {
|
||||
persistFavorite: {
|
||||
code: 200,
|
||||
favorite: [],
|
||||
savedObjectId: newSavedObjectId,
|
||||
version: newVersion,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test).isFavorite).toEqual(true);
|
||||
await store.dispatch(updateIsFavorite({ id: TimelineId.test, isFavorite: false }));
|
||||
|
||||
expect(startTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(refreshTimelines as unknown as jest.Mock).toHaveBeenCalled();
|
||||
expect(endTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test)).toEqual(
|
||||
expect.objectContaining({
|
||||
isFavorite: false,
|
||||
savedObjectId: newSavedObjectId,
|
||||
version: newVersion,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should show an error message when the call is unauthorized', async () => {
|
||||
(persistFavorite as jest.Mock).mockResolvedValue({
|
||||
data: {
|
||||
persistFavorite: {
|
||||
code: 403,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await store.dispatch(updateIsFavorite({ id: TimelineId.test, isFavorite: true }));
|
||||
|
||||
expect(startTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(endTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(showCallOutUnauthorizedMsgMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show a generic error when the persistence throws', async () => {
|
||||
const addDangerMock = jest.spyOn(kibanaMock.notifications.toasts, 'addDanger');
|
||||
(persistFavorite as jest.Mock).mockImplementation(() => {
|
||||
throw new Error();
|
||||
});
|
||||
|
||||
await store.dispatch(updateIsFavorite({ id: TimelineId.test, isFavorite: true }));
|
||||
|
||||
expect(startTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(endTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(addDangerMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createMockStore, kibanaMock } from '../../../common/mock';
|
||||
import { selectTimelineById } from '../selectors';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { persistNote } from '../../containers/notes/api';
|
||||
import { refreshTimelines } from './helpers';
|
||||
|
||||
import {
|
||||
startTimelineSaving,
|
||||
endTimelineSaving,
|
||||
showCallOutUnauthorizedMsg,
|
||||
addNote,
|
||||
addNoteToEvent,
|
||||
} from '../actions';
|
||||
import { updateNote } from '../../../common/store/app/actions';
|
||||
import { createNote } from '../../components/notes/helpers';
|
||||
|
||||
jest.mock('../actions', () => {
|
||||
const actual = jest.requireActual('../actions');
|
||||
const endTLSaving = jest.fn((...args) => actual.endTimelineSaving(...args));
|
||||
(endTLSaving as unknown as { match: Function }).match = () => false;
|
||||
return {
|
||||
...actual,
|
||||
showCallOutUnauthorizedMsg: jest
|
||||
.fn()
|
||||
.mockImplementation((...args) => actual.showCallOutUnauthorizedMsg(...args)),
|
||||
startTimelineSaving: jest
|
||||
.fn()
|
||||
.mockImplementation((...args) => actual.startTimelineSaving(...args)),
|
||||
endTimelineSaving: endTLSaving,
|
||||
};
|
||||
});
|
||||
jest.mock('../../containers/notes/api');
|
||||
jest.mock('./helpers');
|
||||
|
||||
const startTimelineSavingMock = startTimelineSaving as unknown as jest.Mock;
|
||||
const endTimelineSavingMock = endTimelineSaving as unknown as jest.Mock;
|
||||
const showCallOutUnauthorizedMsgMock = showCallOutUnauthorizedMsg as unknown as jest.Mock;
|
||||
|
||||
describe('Timeline note middleware', () => {
|
||||
let store = createMockStore(undefined, undefined, kibanaMock);
|
||||
const testNote = createNote({ newNote: 'test', user: 'elastic' });
|
||||
const testEventId = 'test';
|
||||
|
||||
beforeEach(() => {
|
||||
store = createMockStore(undefined, undefined, kibanaMock);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should persist a timeline note', async () => {
|
||||
(persistNote as jest.Mock).mockResolvedValue({
|
||||
data: {
|
||||
persistNote: {
|
||||
code: 200,
|
||||
message: 'success',
|
||||
note: {
|
||||
noteId: testNote.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test).noteIds).toEqual([]);
|
||||
await store.dispatch(updateNote({ note: testNote }));
|
||||
await store.dispatch(addNote({ id: TimelineId.test, noteId: testNote.id }));
|
||||
|
||||
expect(startTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(refreshTimelines as unknown as jest.Mock).toHaveBeenCalled();
|
||||
expect(endTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test).noteIds).toContain(testNote.id);
|
||||
});
|
||||
|
||||
it('should persist a note on an event of a timeline', async () => {
|
||||
(persistNote as jest.Mock).mockResolvedValue({
|
||||
data: {
|
||||
persistNote: {
|
||||
code: 200,
|
||||
message: 'success',
|
||||
note: {
|
||||
noteId: testNote.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test).eventIdToNoteIds).toEqual({});
|
||||
await store.dispatch(updateNote({ note: testNote }));
|
||||
await store.dispatch(
|
||||
addNoteToEvent({ eventId: testEventId, id: TimelineId.test, noteId: testNote.id })
|
||||
);
|
||||
|
||||
expect(startTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(refreshTimelines as unknown as jest.Mock).toHaveBeenCalled();
|
||||
expect(endTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test).eventIdToNoteIds).toEqual(
|
||||
expect.objectContaining({
|
||||
[testEventId]: [testNote.id],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should show an error message when the call is unauthorized', async () => {
|
||||
(persistNote as jest.Mock).mockResolvedValue({
|
||||
data: {
|
||||
persistNote: {
|
||||
code: 403,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await store.dispatch(updateNote({ note: testNote }));
|
||||
await store.dispatch(addNote({ id: TimelineId.test, noteId: testNote.id }));
|
||||
|
||||
expect(startTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(endTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(showCallOutUnauthorizedMsgMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show a generic error when the persistence throws', async () => {
|
||||
const addDangerMock = jest.spyOn(kibanaMock.notifications.toasts, 'addDanger');
|
||||
(persistNote as jest.Mock).mockImplementation(() => {
|
||||
throw new Error();
|
||||
});
|
||||
|
||||
await store.dispatch(updateNote({ note: testNote }));
|
||||
await store.dispatch(addNote({ id: TimelineId.test, noteId: testNote.id }));
|
||||
|
||||
expect(startTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(endTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(addDangerMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createMockStore, kibanaMock } from '../../../common/mock';
|
||||
import { selectTimelineById } from '../selectors';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { persistPinnedEvent } from '../../containers/pinned_event/api';
|
||||
import { refreshTimelines } from './helpers';
|
||||
|
||||
import {
|
||||
startTimelineSaving,
|
||||
endTimelineSaving,
|
||||
pinEvent,
|
||||
unPinEvent,
|
||||
showCallOutUnauthorizedMsg,
|
||||
updateTimeline,
|
||||
} from '../actions';
|
||||
|
||||
jest.mock('../actions', () => {
|
||||
const actual = jest.requireActual('../actions');
|
||||
const endTLSaving = jest.fn((...args) => actual.endTimelineSaving(...args));
|
||||
(endTLSaving as unknown as { match: Function }).match = () => false;
|
||||
return {
|
||||
...actual,
|
||||
showCallOutUnauthorizedMsg: jest
|
||||
.fn()
|
||||
.mockImplementation((...args) => actual.showCallOutUnauthorizedMsg(...args)),
|
||||
startTimelineSaving: jest
|
||||
.fn()
|
||||
.mockImplementation((...args) => actual.startTimelineSaving(...args)),
|
||||
endTimelineSaving: endTLSaving,
|
||||
};
|
||||
});
|
||||
jest.mock('../../containers/pinned_event/api');
|
||||
jest.mock('./helpers');
|
||||
|
||||
const startTimelineSavingMock = startTimelineSaving as unknown as jest.Mock;
|
||||
const endTimelineSavingMock = endTimelineSaving as unknown as jest.Mock;
|
||||
const showCallOutUnauthorizedMsgMock = showCallOutUnauthorizedMsg as unknown as jest.Mock;
|
||||
|
||||
describe('Timeline pinned event middleware', () => {
|
||||
let store = createMockStore(undefined, undefined, kibanaMock);
|
||||
const testEventId = 'test';
|
||||
|
||||
beforeEach(() => {
|
||||
store = createMockStore(undefined, undefined, kibanaMock);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should persist a timeline pin event action', async () => {
|
||||
(persistPinnedEvent as jest.Mock).mockResolvedValue({
|
||||
data: {
|
||||
persistPinnedEventOnTimeline: {
|
||||
code: 200,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test).pinnedEventIds).toEqual({});
|
||||
await store.dispatch(pinEvent({ id: TimelineId.test, eventId: testEventId }));
|
||||
|
||||
expect(startTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(refreshTimelines as unknown as jest.Mock).toHaveBeenCalled();
|
||||
expect(endTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test).pinnedEventIds).toEqual({
|
||||
[testEventId]: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should persist a timeline un-pin event', async () => {
|
||||
store.dispatch(
|
||||
updateTimeline({
|
||||
id: TimelineId.test,
|
||||
timeline: {
|
||||
...selectTimelineById(store.getState(), TimelineId.test),
|
||||
pinnedEventIds: {
|
||||
[testEventId]: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
(persistPinnedEvent as jest.Mock).mockResolvedValue({
|
||||
data: {},
|
||||
});
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test).pinnedEventIds).toEqual({
|
||||
[testEventId]: true,
|
||||
});
|
||||
await store.dispatch(unPinEvent({ id: TimelineId.test, eventId: testEventId }));
|
||||
|
||||
expect(startTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(refreshTimelines as unknown as jest.Mock).toHaveBeenCalled();
|
||||
expect(endTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test).pinnedEventIds).toEqual({});
|
||||
});
|
||||
|
||||
it('should show an error message when the call is unauthorized', async () => {
|
||||
(persistPinnedEvent as jest.Mock).mockResolvedValue({
|
||||
data: {
|
||||
persistPinnedEventOnTimeline: {
|
||||
code: 403,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await store.dispatch(unPinEvent({ id: TimelineId.test, eventId: testEventId }));
|
||||
|
||||
expect(startTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(endTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(showCallOutUnauthorizedMsgMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show a generic error when the persistence throws', async () => {
|
||||
const addDangerMock = jest.spyOn(kibanaMock.notifications.toasts, 'addDanger');
|
||||
(persistPinnedEvent as jest.Mock).mockImplementation(() => {
|
||||
throw new Error();
|
||||
});
|
||||
|
||||
await store.dispatch(pinEvent({ id: TimelineId.test, eventId: testEventId }));
|
||||
|
||||
expect(startTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(endTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(addDangerMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -8,12 +8,172 @@
|
|||
import type { Filter } from '@kbn/es-query';
|
||||
import { FilterStateStore } from '@kbn/es-query';
|
||||
import { Direction } from '../../../../common/search_strategy';
|
||||
import { TimelineTabs } from '../../../../common/types/timeline';
|
||||
import { TimelineId, TimelineTabs } from '../../../../common/types/timeline';
|
||||
import { TimelineType, TimelineStatus } from '../../../../common/api/timeline';
|
||||
import { convertTimelineAsInput } from './timeline_save';
|
||||
import type { TimelineModel } from '../model';
|
||||
import { createMockStore, kibanaMock } from '../../../common/mock';
|
||||
import { selectTimelineById } from '../selectors';
|
||||
import { copyTimeline, persistTimeline } from '../../containers/api';
|
||||
import { refreshTimelines } from './helpers';
|
||||
import * as i18n from '../../pages/translations';
|
||||
|
||||
import {
|
||||
startTimelineSaving,
|
||||
endTimelineSaving,
|
||||
showCallOutUnauthorizedMsg,
|
||||
saveTimeline,
|
||||
setChanged,
|
||||
} from '../actions';
|
||||
|
||||
jest.mock('../actions', () => {
|
||||
const actual = jest.requireActual('../actions');
|
||||
const endTLSaving = jest.fn((...args) => actual.endTimelineSaving(...args));
|
||||
(endTLSaving as unknown as { match: Function }).match = () => false;
|
||||
return {
|
||||
...actual,
|
||||
showCallOutUnauthorizedMsg: jest
|
||||
.fn()
|
||||
.mockImplementation((...args) => actual.showCallOutUnauthorizedMsg(...args)),
|
||||
startTimelineSaving: jest
|
||||
.fn()
|
||||
.mockImplementation((...args) => actual.startTimelineSaving(...args)),
|
||||
endTimelineSaving: endTLSaving,
|
||||
};
|
||||
});
|
||||
jest.mock('../../containers/api');
|
||||
jest.mock('./helpers');
|
||||
|
||||
const startTimelineSavingMock = startTimelineSaving as unknown as jest.Mock;
|
||||
const endTimelineSavingMock = endTimelineSaving as unknown as jest.Mock;
|
||||
const showCallOutUnauthorizedMsgMock = showCallOutUnauthorizedMsg as unknown as jest.Mock;
|
||||
|
||||
describe('Timeline save middleware', () => {
|
||||
let store = createMockStore(undefined, undefined, kibanaMock);
|
||||
|
||||
beforeEach(() => {
|
||||
store = createMockStore(undefined, undefined, kibanaMock);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should persist a timeline', async () => {
|
||||
(persistTimeline as jest.Mock).mockResolvedValue({
|
||||
data: {
|
||||
persistTimeline: {
|
||||
code: 200,
|
||||
message: 'success',
|
||||
timeline: {
|
||||
savedObjectId: 'soid',
|
||||
version: 'newVersion',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
await store.dispatch(setChanged({ id: TimelineId.test, changed: true }));
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test)).toEqual(
|
||||
expect.objectContaining({
|
||||
version: null,
|
||||
changed: true,
|
||||
})
|
||||
);
|
||||
await store.dispatch(saveTimeline({ id: TimelineId.test, saveAsNew: false }));
|
||||
|
||||
expect(startTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(persistTimeline as unknown as jest.Mock).toHaveBeenCalled();
|
||||
expect(refreshTimelines as unknown as jest.Mock).toHaveBeenCalled();
|
||||
expect(endTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test)).toEqual(
|
||||
expect.objectContaining({
|
||||
version: 'newVersion',
|
||||
changed: false,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should copy a timeline', async () => {
|
||||
(copyTimeline as jest.Mock).mockResolvedValue({
|
||||
data: {
|
||||
persistTimeline: {
|
||||
code: 200,
|
||||
message: 'success',
|
||||
timeline: {
|
||||
savedObjectId: 'soid',
|
||||
version: 'newVersion',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
await store.dispatch(setChanged({ id: TimelineId.test, changed: true }));
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test)).toEqual(
|
||||
expect.objectContaining({
|
||||
version: null,
|
||||
changed: true,
|
||||
})
|
||||
);
|
||||
await store.dispatch(saveTimeline({ id: TimelineId.test, saveAsNew: true }));
|
||||
|
||||
expect(copyTimeline as unknown as jest.Mock).toHaveBeenCalled();
|
||||
expect(startTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(refreshTimelines as unknown as jest.Mock).toHaveBeenCalled();
|
||||
expect(endTimelineSavingMock).toHaveBeenCalled();
|
||||
expect(selectTimelineById(store.getState(), TimelineId.test)).toEqual(
|
||||
expect.objectContaining({
|
||||
version: 'newVersion',
|
||||
changed: false,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should show an error message in case of a conflict', async () => {
|
||||
const addDangerMock = jest.spyOn(kibanaMock.notifications.toasts, 'addDanger');
|
||||
(copyTimeline as jest.Mock).mockResolvedValue({
|
||||
status_code: 409,
|
||||
message: 'test conflict',
|
||||
});
|
||||
await store.dispatch(saveTimeline({ id: TimelineId.test, saveAsNew: true }));
|
||||
|
||||
expect(refreshTimelines as unknown as jest.Mock).not.toHaveBeenCalled();
|
||||
expect(addDangerMock).toHaveBeenCalledWith({
|
||||
title: i18n.TIMELINE_VERSION_CONFLICT_TITLE,
|
||||
text: i18n.TIMELINE_VERSION_CONFLICT_DESCRIPTION,
|
||||
});
|
||||
});
|
||||
|
||||
it('should show the provided message in case of an error response', async () => {
|
||||
const addDangerMock = jest.spyOn(kibanaMock.notifications.toasts, 'addDanger');
|
||||
(persistTimeline as jest.Mock).mockResolvedValue({
|
||||
status_code: 404,
|
||||
message: 'test error message',
|
||||
});
|
||||
await store.dispatch(saveTimeline({ id: TimelineId.test, saveAsNew: false }));
|
||||
|
||||
expect(refreshTimelines as unknown as jest.Mock).not.toHaveBeenCalled();
|
||||
expect(addDangerMock).toHaveBeenCalledWith({
|
||||
title: i18n.UPDATE_TIMELINE_ERROR_TITLE,
|
||||
text: 'test error message',
|
||||
});
|
||||
});
|
||||
|
||||
it('should show a generic error in case of an empty response', async () => {
|
||||
const addDangerMock = jest.spyOn(kibanaMock.notifications.toasts, 'addDanger');
|
||||
(persistTimeline as jest.Mock).mockResolvedValue(null);
|
||||
await store.dispatch(saveTimeline({ id: TimelineId.test, saveAsNew: false }));
|
||||
|
||||
expect(refreshTimelines as unknown as jest.Mock).not.toHaveBeenCalled();
|
||||
expect(addDangerMock).toHaveBeenCalledWith({
|
||||
title: i18n.UPDATE_TIMELINE_ERROR_TITLE,
|
||||
text: i18n.UPDATE_TIMELINE_ERROR_TEXT,
|
||||
});
|
||||
});
|
||||
|
||||
it('should show an error message when the call is unauthorized', async () => {
|
||||
(persistTimeline as jest.Mock).mockResolvedValue({ data: { persistTimeline: { code: 403 } } });
|
||||
await store.dispatch(saveTimeline({ id: TimelineId.test, saveAsNew: false }));
|
||||
|
||||
expect(refreshTimelines as unknown as jest.Mock).not.toHaveBeenCalled();
|
||||
expect(showCallOutUnauthorizedMsgMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('Timeline Save Middleware', () => {
|
||||
describe('#convertTimelineAsInput ', () => {
|
||||
test('should return a TimelineInput instead of TimelineModel ', () => {
|
||||
const columns: TimelineModel['columns'] = [
|
||||
|
|
|
@ -99,7 +99,7 @@ export const saveTimelineMiddleware: (kibana: CoreStart) => Middleware<{}, State
|
|||
return;
|
||||
}
|
||||
|
||||
const response = result.data.persistTimeline;
|
||||
const response = result?.data?.persistTimeline;
|
||||
if (response == null) {
|
||||
kibana.notifications.toasts.addDanger({
|
||||
title: i18n.UPDATE_TIMELINE_ERROR_TITLE,
|
||||
|
@ -273,7 +273,7 @@ const convertToString = (obj: unknown) => {
|
|||
type PossibleResponse = TimelineResponse | TimelineErrorResponse;
|
||||
|
||||
function isTimelineErrorResponse(response: PossibleResponse): response is TimelineErrorResponse {
|
||||
return 'status_code' in response || 'statusCode' in response;
|
||||
return response && ('status_code' in response || 'statusCode' in response);
|
||||
}
|
||||
|
||||
function getErrorFromResponse(response: TimelineErrorResponse) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue