Skip to content

Commit

Permalink
[Ingest Pipelines] Add test coverage for ingest pipelines editor comp…
Browse files Browse the repository at this point in the history
…onent (#69283)

* first iteration of CIT tests

* address pr feedback

- use dot notation where we can
- use string literals instead of + concatentation
  • Loading branch information
jloleysens authored Jun 18, 2020
1 parent 2544daf commit e046029
Show file tree
Hide file tree
Showing 14 changed files with 334 additions and 65 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { act } from 'react-dom/test-utils';
import React from 'react';
import { registerTestBed, TestBed } from '../../../../../../../test_utils';
import { PipelineProcessorsEditor, Props } from '../pipeline_processors_editor.container';

jest.mock('@elastic/eui', () => ({
...jest.requireActual('@elastic/eui'),
// Mocking EuiComboBox, as it utilizes "react-virtualized" for rendering search suggestions,
// which does not produce a valid component wrapper
EuiComboBox: (props: any) => (
<input
data-test-subj={props['data-test-subj'] || 'mockComboBox'}
data-currentvalue={props.selectedOptions}
onChange={async (syntheticEvent: any) => {
props.onChange([syntheticEvent['0']]);
}}
/>
),
// Mocking EuiCodeEditor, which uses React Ace under the hood
EuiCodeEditor: (props: any) => (
<input
data-test-subj={props['data-test-subj'] || 'mockCodeEditor'}
data-currentvalue={props.value}
onChange={(e: any) => {
props.onChange(e.jsonContent);
}}
/>
),
}));

jest.mock('react-virtualized', () => ({
...jest.requireActual('react-virtualized'),
AutoSizer: ({ children }: { children: any }) => (
<div>{children({ height: 500, width: 500 })}</div>
),
}));

const testBedSetup = registerTestBed<TestSubject>(PipelineProcessorsEditor, {
doMountAsync: false,
});

export interface SetupResult extends TestBed<TestSubject> {
actions: ReturnType<typeof createActions>;
}

/**
* We make heavy use of "processorSelector" in these actions. They are a way to uniquely identify
* a processor and are a stringified version of {@link ProcessorSelector}.
*
* @remark
* See also {@link selectorToDataTestSubject}.
*/
const createActions = (testBed: TestBed<TestSubject>) => {
const { find, component } = testBed;

return {
async addProcessor(processorsSelector: string, type: string, options: Record<string, any>) {
find(`${processorsSelector}.addProcessorButton`).simulate('click');
await act(async () => {
find('processorTypeSelector').simulate('change', [{ value: type, label: type }]);
});
component.update();
await act(async () => {
find('processorOptionsEditor').simulate('change', {
jsonContent: JSON.stringify(options),
});
});
await act(async () => {
find('processorSettingsForm.submitButton').simulate('click');
});
},

removeProcessor(processorSelector: string) {
find(`${processorSelector}.moreMenu.button`).simulate('click');
find(`${processorSelector}.moreMenu.deleteButton`).simulate('click');
act(() => {
find('removeProcessorConfirmationModal.confirmModalConfirmButton').simulate('click');
});
},

moveProcessor(processorSelector: string, dropZoneSelector: string) {
act(() => {
find(`${processorSelector}.moveItemButton`).simulate('click');
});
act(() => {
find(dropZoneSelector).last().simulate('click');
});
component.update();
},

async addOnFailureProcessor(
processorSelector: string,
type: string,
options: Record<string, any>
) {
find(`${processorSelector}.moreMenu.button`).simulate('click');
find(`${processorSelector}.moreMenu.addOnFailureButton`).simulate('click');
await act(async () => {
find('processorTypeSelector').simulate('change', [{ value: type, label: type }]);
});
component.update();
await act(async () => {
find('processorOptionsEditor').simulate('change', {
jsonContent: JSON.stringify(options),
});
});
await act(async () => {
find('processorSettingsForm.submitButton').simulate('click');
});
},

duplicateProcessor(processorSelector: string) {
find(`${processorSelector}.moreMenu.button`).simulate('click');
act(() => {
find(`${processorSelector}.moreMenu.duplicateButton`).simulate('click');
});
},

startAndCancelMove(processorSelector: string) {
act(() => {
find(`${processorSelector}.moveItemButton`).simulate('click');
});
component.update();
act(() => {
find(`${processorSelector}.cancelMoveItemButton`).simulate('click');
});
},

toggleOnFailure() {
find('pipelineEditorOnFailureToggle').simulate('click');
},
};
};

export const setup = async (props: Props): Promise<SetupResult> => {
const testBed = await testBedSetup(props);
return {
...testBed,
actions: createActions(testBed),
};
};

type TestSubject =
| 'pipelineEditorDoneButton'
| 'pipelineEditorOnFailureToggle'
| 'addProcessorsButtonLevel1'
| 'processorSettingsForm'
| 'processorSettingsForm.submitButton'
| 'processorOptionsEditor'
| 'processorSettingsFormFlyout'
| 'processorTypeSelector'
| 'pipelineEditorOnFailureTree'
| string;
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { setup } from './pipeline_processors_editor.helpers';
import { setup, SetupResult } from './pipeline_processors_editor.helpers';
import { Pipeline } from '../../../../../common/types';

const testProcessors: Pick<Pipeline, 'processors'> = {
Expand All @@ -25,10 +24,20 @@ const testProcessors: Pick<Pipeline, 'processors'> = {
};

describe('Pipeline Editor', () => {
it('provides the same data out it got in if nothing changes', async () => {
const onUpdate = jest.fn();
let onUpdate: jest.Mock;
let testBed: SetupResult;

beforeAll(() => {
jest.useFakeTimers();
});

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

await setup({
beforeEach(async () => {
onUpdate = jest.fn();
testBed = await setup({
value: {
...testProcessors,
},
Expand All @@ -38,28 +47,144 @@ describe('Pipeline Editor', () => {
onTestPipelineClick: jest.fn(),
esDocsBasePath: 'test',
});
});

it('provides the same data out it got in if nothing changes', () => {
const {
calls: [[arg]],
} = onUpdate.mock;

expect(arg.getData()).toEqual(testProcessors);
});

it('toggles the on-failure processors', async () => {
const { actions, exists } = await setup({
value: {
...testProcessors,
},
onFlyoutOpen: jest.fn(),
onUpdate: jest.fn(),
isTestButtonDisabled: false,
onTestPipelineClick: jest.fn(),
esDocsBasePath: 'test',
});

it('toggles the on-failure processors tree', () => {
const { actions, exists } = testBed;
expect(exists('pipelineEditorOnFailureTree')).toBe(false);
actions.toggleOnFailure();
expect(exists('pipelineEditorOnFailureTree')).toBe(true);
});

describe('processors', () => {
it('adds a new processor', async () => {
const { actions } = testBed;
await actions.addProcessor('processors', 'test', { if: '1 == 1' });
const [onUpdateResult] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1];
const { processors } = onUpdateResult.getData();
expect(processors.length).toBe(3);
const [a, b, c] = processors;
expect(a).toEqual(testProcessors.processors[0]);
expect(b).toEqual(testProcessors.processors[1]);
expect(c).toEqual({ test: { if: '1 == 1' } });
});

it('removes a processor', () => {
const { actions } = testBed;
// processor>0 denotes the first processor in the top-level processors array.
actions.removeProcessor('processors>0');
const [onUpdateResult] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1];
const { processors } = onUpdateResult.getData();
expect(processors.length).toBe(1);
expect(processors[0]).toEqual({
gsub: {
field: '_index',
pattern: '(.monitoring-\\w+-)6(-.+)',
replacement: '$17$2',
},
});
});

it('reorders processors', () => {
const { actions } = testBed;
actions.moveProcessor('processors>0', 'dropButtonBelow-processors>1');
const [onUpdateResult] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1];
const { processors } = onUpdateResult.getData();
expect(processors).toEqual(testProcessors.processors.slice(0).reverse());
});

it('adds an on-failure processor to a processor', async () => {
const { actions, find, exists } = testBed;
const processorSelector = 'processors>1';
await actions.addOnFailureProcessor(processorSelector, 'test', { if: '1 == 2' });
// Assert that the add on failure button has been removed
find(`${processorSelector}.moreMenu.button`).simulate('click');
expect(!exists(`${processorSelector}.moreMenu.addOnFailureButton`));
// Assert that the add processor button is now visible
expect(exists(`${processorSelector}.addProcessor`));
const [onUpdateResult] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1];
const { processors } = onUpdateResult.getData();
expect(processors.length).toBe(2);
expect(processors[0]).toEqual(testProcessors.processors[0]); // should be unchanged
expect(processors[1].gsub).toEqual({
...testProcessors.processors[1].gsub,
on_failure: [{ test: { if: '1 == 2' } }],
});
});

it('moves a processor to a nested dropzone', async () => {
const { actions } = testBed;
await actions.addOnFailureProcessor('processors>1', 'test', { if: '1 == 3' });
actions.moveProcessor('processors>0', 'dropButtonBelow-processors>1>onFailure>0');
const [onUpdateResult] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1];
const { processors } = onUpdateResult.getData();
expect(processors.length).toBe(1);
expect(processors[0].gsub.on_failure).toEqual([
{
test: { if: '1 == 3' },
},
testProcessors.processors[0],
]);
});

it('duplicates a processor', async () => {
const { actions } = testBed;
await actions.addOnFailureProcessor('processors>1', 'test', { if: '1 == 4' });
actions.duplicateProcessor('processors>1');
const [onUpdateResult] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1];
const { processors } = onUpdateResult.getData();
expect(processors.length).toBe(3);
const duplicatedProcessor = {
gsub: {
...testProcessors.processors[1].gsub,
on_failure: [{ test: { if: '1 == 4' } }],
},
};
expect(processors).toEqual([
testProcessors.processors[0],
duplicatedProcessor,
duplicatedProcessor,
]);
});

it('can cancel a move', () => {
const { actions, exists } = testBed;
const processorSelector = 'processors>0';
actions.startAndCancelMove(processorSelector);
// Assert that we have exited move mode for this processor
expect(exists(`moveItemButton-${processorSelector}`));
const [onUpdateResult] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1];
const { processors } = onUpdateResult.getData();
// Assert that nothing has changed
expect(processors).toEqual(testProcessors.processors);
});

it('moves to and from the global on-failure tree', async () => {
const { actions } = testBed;
actions.toggleOnFailure();
await actions.addProcessor('onFailure', 'test', { if: '1 == 5' });
actions.moveProcessor('processors>0', 'dropButtonBelow-onFailure>0');
const [onUpdateResult1] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1];
const data1 = onUpdateResult1.getData();
expect(data1.processors.length).toBe(1);
expect(data1.on_failure.length).toBe(2);
expect(data1.processors).toEqual([testProcessors.processors[1]]);
expect(data1.on_failure).toEqual([{ test: { if: '1 == 5' } }, testProcessors.processors[0]]);
actions.moveProcessor('onFailure>1', 'dropButtonAbove-processors>0');
const [onUpdateResult2] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1];
const data2 = onUpdateResult2.getData();
expect(data2.processors.length).toBe(2);
expect(data2.on_failure.length).toBe(1);
expect(data2.processors).toEqual(testProcessors.processors);
expect(data2.on_failure).toEqual([{ test: { if: '1 == 5' } }]);
});
});
});
Loading

0 comments on commit e046029

Please sign in to comment.