Skip to content

Commit

Permalink
fix export response (#70473)
Browse files Browse the repository at this point in the history
* fix export response

* update unit tests
  • Loading branch information
angorayc committed Jul 1, 2020
1 parent c2ebad5 commit a145421
Showing 4 changed files with 156 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -4,9 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { shallow } from 'enzyme';
import { shallow, mount } from 'enzyme';
import React from 'react';
import { GenericDownloaderComponent } from './index';
import { GenericDownloaderComponent, ExportSelectedData } from './index';
import { errorToToaster } from '../toasters';

jest.mock('../toasters', () => ({
useStateToaster: jest.fn(() => [jest.fn(), jest.fn()]),
errorToToaster: jest.fn(),
}));

describe('GenericDownloader', () => {
test('renders correctly against snapshot', () => {
@@ -19,4 +25,16 @@ describe('GenericDownloader', () => {
);
expect(wrapper).toMatchSnapshot();
});

test('show toaster with correct error message if error occurrs', () => {
mount(
<GenericDownloaderComponent
filename={'export_rules.ndjson'}
onExportSuccess={jest.fn()}
exportSelectedData={('some error' as unknown) as ExportSelectedData}
ids={['123']}
/>
);
expect((errorToToaster as jest.Mock).mock.calls[0][0].title).toEqual('Failed to export data…');
});
});
Original file line number Diff line number Diff line change
@@ -7,8 +7,8 @@
import { i18n } from '@kbn/i18n';

export const EXPORT_FAILURE = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.components.ruleDownloader.exportFailureTitle',
'xpack.securitySolution.detectionEngine.rules.components.genericDownloader.exportFailureTitle',
{
defaultMessage: 'Failed to export rules…',
defaultMessage: 'Failed to export data…',
}
);
142 changes: 129 additions & 13 deletions x-pack/plugins/security_solution/public/timelines/containers/api.test.ts
Original file line number Diff line number Diff line change
@@ -7,10 +7,11 @@ import * as api from './api';
import { KibanaServices } from '../../common/lib/kibana';
import { TimelineType, TimelineStatus } from '../../../common/types/timeline';
import { TIMELINE_DRAFT_URL, TIMELINE_URL } from '../../../common/constants';
import { ImportDataProps } from '../../alerts/containers/detection_engine/rules/types';

jest.mock('../../common/lib/kibana', () => {
return {
KibanaServices: { get: jest.fn() },
KibanaServices: { get: jest.fn(() => ({ http: { fetch: jest.fn() } })) },
};
});

@@ -173,6 +174,7 @@ describe('persistTimeline', () => {

beforeAll(() => {
jest.resetAllMocks();
jest.resetModules();

(KibanaServices.get as jest.Mock).mockReturnValue({
http: {
@@ -188,10 +190,6 @@ describe('persistTimeline', () => {
});
});

afterAll(() => {
jest.resetAllMocks();
});

test('it should create a draft timeline if given status is draft and timelineId is null', () => {
expect(postMock).toHaveBeenCalledWith(TIMELINE_DRAFT_URL, {
body: JSON.stringify({
@@ -334,6 +332,7 @@ describe('persistTimeline', () => {

beforeAll(() => {
jest.resetAllMocks();
jest.resetModules();

(KibanaServices.get as jest.Mock).mockReturnValue({
http: {
@@ -345,10 +344,6 @@ describe('persistTimeline', () => {
api.persistTimeline({ timelineId, timeline: importTimeline, version });
});

afterAll(() => {
jest.resetAllMocks();
});

test('it should update timeline', () => {
expect(postMock.mock.calls[0][0]).toEqual(TIMELINE_URL);
});
@@ -474,6 +469,7 @@ describe('persistTimeline', () => {

beforeAll(() => {
jest.resetAllMocks();
jest.resetModules();

(KibanaServices.get as jest.Mock).mockReturnValue({
http: {
@@ -485,10 +481,6 @@ describe('persistTimeline', () => {
api.persistTimeline({ timelineId, timeline: inputTimeline, version });
});

afterAll(() => {
jest.resetAllMocks();
});

test('it should update timeline', () => {
expect(patchMock.mock.calls[0][0]).toEqual(TIMELINE_URL);
});
@@ -506,3 +498,127 @@ describe('persistTimeline', () => {
});
});
});

describe('importTimelines', () => {
const fileToImport = { fileToImport: {} } as ImportDataProps;
const fetchMock = jest.fn();

beforeAll(() => {
jest.resetAllMocks();
jest.resetModules();

(KibanaServices.get as jest.Mock).mockReturnValue({
http: {
fetch: fetchMock,
},
});
api.importTimelines(fileToImport);
});

test('should pass correct args to KibanaServices - url', () => {
expect(fetchMock.mock.calls[0][0]).toEqual('/api/timeline/_import');
});

test('should pass correct args to KibanaServices - args', () => {
expect(JSON.stringify(fetchMock.mock.calls[0][1])).toEqual(
JSON.stringify({
method: 'POST',
headers: { 'Content-Type': undefined },
body: new FormData(),
signal: undefined,
})
);
});
});

describe('exportSelectedTimeline', () => {
const ids = ['123', 'abc'];
const fetchMock = jest.fn();

beforeAll(() => {
jest.resetAllMocks();
jest.resetModules();

(KibanaServices.get as jest.Mock).mockReturnValue({
http: {
fetch: fetchMock,
},
});
api.exportSelectedTimeline({
filename: 'timelines_export.ndjson',
ids,
signal: {} as AbortSignal,
});
});

test('should pass correct args to KibanaServices', () => {
expect(fetchMock).toBeCalledWith('/api/timeline/_export', {
body: JSON.stringify({ ids }),
method: 'POST',
query: { file_name: 'timelines_export.ndjson' },
signal: {},
});
});
});

describe('getDraftTimeline', () => {
const timelineType = { timelineType: TimelineType.default };
const getMock = jest.fn();

beforeAll(() => {
jest.resetAllMocks();
jest.resetModules();

(KibanaServices.get as jest.Mock).mockReturnValue({
http: {
get: getMock,
},
});
api.getDraftTimeline(timelineType);
});

test('should pass correct args to KibanaServices', () => {
expect(getMock).toBeCalledWith('/api/timeline/_draft', {
query: timelineType,
});
});
});

describe('cleanDraftTimeline', () => {
const postMock = jest.fn();

beforeEach(() => {
jest.resetAllMocks();
jest.resetModules();

(KibanaServices.get as jest.Mock).mockReturnValue({
http: {
post: postMock,
},
});
});

test('should pass correct args to KibanaServices - timeline', () => {
const args = { timelineType: TimelineType.default };

api.cleanDraftTimeline(args);

expect(postMock).toBeCalledWith('/api/timeline/_draft', {
body: JSON.stringify(args),
});
});

test('should pass correct args to KibanaServices - timeline template', () => {
const args = {
timelineType: TimelineType.template,
templateTimelineId: 'test-123',
templateTimelineVersion: 1,
};

api.cleanDraftTimeline(args);

expect(postMock).toBeCalledWith('/api/timeline/_draft', {
body: JSON.stringify(args),
});
});
});
Original file line number Diff line number Diff line change
@@ -132,6 +132,7 @@ export const persistTimeline = async ({

export const importTimelines = async ({
fileToImport,
signal,
}: ImportDataProps): Promise<ImportDataResponse> => {
const formData = new FormData();
formData.append('file', fileToImport);
@@ -140,24 +141,24 @@ export const importTimelines = async ({
method: 'POST',
headers: { 'Content-Type': undefined },
body: formData,
signal,
});
};

export const exportSelectedTimeline: ExportSelectedData = async ({
export const exportSelectedTimeline: ExportSelectedData = ({
filename = `timelines_export.ndjson`,
ids = [],
signal,
}): Promise<Blob> => {
const body = ids.length > 0 ? JSON.stringify({ ids }) : undefined;
const response = await KibanaServices.get().http.fetch<{ body: Blob }>(`${TIMELINE_EXPORT_URL}`, {
return KibanaServices.get().http.fetch<Blob>(`${TIMELINE_EXPORT_URL}`, {
method: 'POST',
body,
query: {
file_name: filename,
},
signal,
});

return response.body;
};

export const getDraftTimeline = async ({

0 comments on commit a145421

Please sign in to comment.