Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix export response #70473

Merged
merged 3 commits into from
Jul 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -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
Expand Up @@ -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
Expand Up @@ -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() } })) },
};
});

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

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

(KibanaServices.get as jest.Mock).mockReturnValue({
http: {
Expand All @@ -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({
Expand Down Expand Up @@ -334,6 +332,7 @@ describe('persistTimeline', () => {

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

(KibanaServices.get as jest.Mock).mockReturnValue({
http: {
Expand All @@ -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);
});
Expand Down Expand Up @@ -474,6 +469,7 @@ describe('persistTimeline', () => {

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

(KibanaServices.get as jest.Mock).mockReturnValue({
http: {
Expand All @@ -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);
});
Expand All @@ -506,3 +498,127 @@ describe('persistTimeline', () => {
});
});
});

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

beforeAll(() => {
jest.resetAllMocks();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the jest resets seem repetitive, happening twice in beforeAll and afterAll... is that necessary? same thing in each describe. can we cut these down?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe a beforeEach would eliminate the need within the describes? idk, give it a try

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I think keep reset in just beforeAll would be enough

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
Expand Up @@ -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);
Expand All @@ -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 ({
Expand Down