Skip to content

Commit

Permalink
[Runtime field editor] Component integration test for preview (#106410)
Browse files Browse the repository at this point in the history
  • Loading branch information
sebelga authored Aug 2, 2021
1 parent a10369a commit 3954533
Show file tree
Hide file tree
Showing 21 changed files with 1,523 additions and 233 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,9 @@ describe('<FieldEditor />', () => {

const { form, component, actions } = testBed;

await act(async () => {
actions.toggleFormRow('value');
});

await act(async () => {
form.setInputValue('nameField.input', existingFields[0]);
form.setInputValue('scriptField', 'echo("hello")');
});
await actions.toggleFormRow('value');
await actions.fields.updateName(existingFields[0]);
await actions.fields.updateScript('echo("hello")');

await act(async () => {
jest.advanceTimersByTime(1000); // Make sure our debounced error message is in the DOM
Expand Down Expand Up @@ -180,6 +175,7 @@ describe('<FieldEditor />', () => {
const lastState = getLastStateUpdate();
await submitFormAndGetData(lastState);
component.update();

expect(getLastStateUpdate().isValid).toBe(true);
expect(form.getErrorsMessages()).toEqual([]);
});
Expand Down Expand Up @@ -237,7 +233,7 @@ describe('<FieldEditor />', () => {
form,
component,
find,
actions: { changeFieldType },
actions: { fields },
} = testBed;

// We set some dummy painless error
Expand All @@ -249,7 +245,7 @@ describe('<FieldEditor />', () => {
expect(form.getErrorsMessages()).toEqual(['Awwww! Painless syntax error']);

// We change the type and expect the form error to not be there anymore
await changeFieldType('keyword');
await fields.updateType('keyword');
expect(form.getErrorsMessages()).toEqual([]);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,16 @@ const defaultProps: Props = {
isSavingField: false,
};

const getActions = (testBed: TestBed) => {
return {
...getCommonActions(testBed),
};
};

export const setup = async (props?: Partial<Props>, deps?: Partial<Context>) => {
let testBed: TestBed;

// Setup testbed
await act(async () => {
testBed = await registerTestBed(WithFieldEditorDependencies(FieldEditorFlyoutContent, deps), {
memoryRouter: {
Expand All @@ -35,9 +42,5 @@ export const setup = async (props?: Partial<Props>, deps?: Partial<Context>) =>

testBed!.component.update();

const actions = {
...getCommonActions(testBed!),
};

return { ...testBed!, actions };
return { ...testBed!, actions: getActions(testBed!) };
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ describe('<FieldEditorFlyoutContent />', () => {
const { server, httpRequestsMockHelpers } = setupEnvironment();

beforeAll(() => {
httpRequestsMockHelpers.setFieldPreviewResponse({ values: ['foo'] });
jest.useFakeTimers();
});

Expand All @@ -23,10 +24,6 @@ describe('<FieldEditorFlyoutContent />', () => {
server.restore();
});

beforeEach(() => {
httpRequestsMockHelpers.setFieldPreviewResponse({ values: ['Set by Jest test'] });
});

test('should have the correct title', async () => {
const { exists, find } = await setup();
expect(exists('flyoutTitle')).toBe(true);
Expand Down Expand Up @@ -114,20 +111,12 @@ describe('<FieldEditorFlyoutContent />', () => {

const {
find,
component,
form,
actions: { toggleFormRow, changeFieldType },
actions: { toggleFormRow, fields },
} = await setup({ onSave });

act(() => {
form.setInputValue('nameField.input', 'someName');
toggleFormRow('value');
});
component.update();

await act(async () => {
form.setInputValue('scriptField', 'echo("hello")');
});
await fields.updateName('someName');
await toggleFormRow('value');
await fields.updateScript('echo("hello")');

await act(async () => {
// Let's make sure that validation has finished running
Expand All @@ -149,7 +138,7 @@ describe('<FieldEditorFlyoutContent />', () => {
});

// Change the type and make sure it is forwarded
await changeFieldType('other_type', 'Other type');
await fields.updateType('other_type', 'Other type');

await act(async () => {
find('fieldSaveButton').simulate('click');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { act } from 'react-dom/test-utils';
import { ReactWrapper } from 'enzyme';
import { registerTestBed, TestBed } from '@kbn/test/jest';

import { API_BASE_PATH } from '../../common/constants';
import { Context } from '../../public/components/field_editor_context';
import {
FieldEditorFlyoutContent,
Props,
} from '../../public/components/field_editor_flyout_content';
import {
WithFieldEditorDependencies,
getCommonActions,
spyIndexPatternGetAllFields,
spySearchQuery,
spySearchQueryResponse,
} from './helpers';

const defaultProps: Props = {
onSave: () => {},
onCancel: () => {},
runtimeFieldValidator: () => Promise.resolve(null),
isSavingField: false,
};

/**
* This handler lets us mock the fields present on the index pattern during our test
* @param fields The fields of the index pattern
*/
export const setIndexPatternFields = (fields: Array<{ name: string; displayName: string }>) => {
spyIndexPatternGetAllFields.mockReturnValue(fields);
};

export interface TestDoc {
title: string;
subTitle: string;
description: string;
}

export const getSearchCallMeta = () => {
const totalCalls = spySearchQuery.mock.calls.length;
const lastCall = spySearchQuery.mock.calls[totalCalls - 1] ?? null;
let lastCallParams = null;

if (lastCall) {
lastCallParams = lastCall[0];
}

return {
totalCalls,
lastCall,
lastCallParams,
};
};

export const setSearchResponse = (
documents: Array<{ _id: string; _index: string; _source: TestDoc }>
) => {
spySearchQueryResponse.mockResolvedValue({
rawResponse: {
hits: {
total: documents.length,
hits: documents,
},
},
});
};

const getActions = (testBed: TestBed) => {
const getWrapperRenderedIndexPatternFields = (): ReactWrapper | null => {
if (testBed.find('indexPatternFieldList').length === 0) {
return null;
}
return testBed.find('indexPatternFieldList.listItem');
};

const getRenderedIndexPatternFields = (): Array<{ key: string; value: string }> => {
const allFields = getWrapperRenderedIndexPatternFields();

if (allFields === null) {
return [];
}

return allFields.map((field) => {
const key = testBed.find('key', field).text();
const value = testBed.find('value', field).text();
return { key, value };
});
};

const getRenderedFieldsPreview = () => {
if (testBed.find('fieldPreviewItem').length === 0) {
return [];
}

const previewFields = testBed.find('fieldPreviewItem.listItem');

return previewFields.map((field) => {
const key = testBed.find('key', field).text();
const value = testBed.find('value', field).text();
return { key, value };
});
};

const setFilterFieldsValue = async (value: string) => {
await act(async () => {
testBed.form.setInputValue('filterFieldsInput', value);
});

testBed.component.update();
};

// Need to set "server: any" (instead of SinonFakeServer) to avoid a TS error :(
// Error: Exported variable 'setup' has or is using name 'Document' from external module "/dev/shm/workspace/parallel/14/kibana/node_modules/@types/sinon/ts3.1/index"
const getLatestPreviewHttpRequest = (server: any) => {
let i = server.requests.length - 1;

while (i >= 0) {
const request = server.requests[i];
if (request.method === 'POST' && request.url === `${API_BASE_PATH}/field_preview`) {
return {
...request,
requestBody: JSON.parse(JSON.parse(request.requestBody).body),
};
}
i--;
}

throw new Error(`Can't access the latest preview HTTP request as it hasn't been called.`);
};

const goToNextDocument = async () => {
await act(async () => {
testBed.find('goToNextDocButton').simulate('click');
});
testBed.component.update();
};

const goToPreviousDocument = async () => {
await act(async () => {
testBed.find('goToPrevDocButton').simulate('click');
});
testBed.component.update();
};

const loadCustomDocument = (docId: string) => {};

return {
...getCommonActions(testBed),
getWrapperRenderedIndexPatternFields,
getRenderedIndexPatternFields,
getRenderedFieldsPreview,
setFilterFieldsValue,
getLatestPreviewHttpRequest,
goToNextDocument,
goToPreviousDocument,
loadCustomDocument,
};
};

export const setup = async (props?: Partial<Props>, deps?: Partial<Context>) => {
let testBed: TestBed;

// Setup testbed
await act(async () => {
testBed = await registerTestBed(WithFieldEditorDependencies(FieldEditorFlyoutContent, deps), {
memoryRouter: {
wrapComponent: false,
},
})({ ...defaultProps, ...props });
});

testBed!.component.update();

return { ...testBed!, actions: getActions(testBed!) };
};

export type FieldEditorFlyoutContentTestBed = TestBed & { actions: ReturnType<typeof getActions> };
Loading

0 comments on commit 3954533

Please sign in to comment.