[8.6] [Form lib] Fix validate method returning wrong state when called from onChange handler (#146371) (#147303)

# Backport

This will backport the following commits from `main` to `8.6`:
- [[Form lib] Fix validate method returning wrong state when called from
onChange handler
(#146371)](https://github.com/elastic/kibana/pull/146371)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Muhammad
Ibragimov","email":"53621505+mibragimov@users.noreply.github.com"},"sourceCommit":{"committedDate":"2022-12-09T04:44:51Z","message":"[Form
lib] Fix validate method returning wrong state when called from onChange
handler (#146371)\n\nFixes
https://github.com/elastic/kibana/issues/145846\r\n\r\n###
Summary\r\nThis PR fixes a bug in the form lib where the `validate`
method would\r\nreturn the wrong state when called from an `onChange`
handler.
See\r\nhttps://github.com/elastic/kibana/issues/145846#issue-1457815157
for\r\nmore details.\r\n\r\nCo-authored-by: Muhammad Ibragimov
<muhammad.ibragimov@elastic.co>\r\nCo-authored-by: Yaroslav Kuznietsov
<kuznetsov.yaroslav.yk@gmail.com>\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"6814ed7e9109f0a5f90cc5e261eb0619312c78a2","branchLabelMapping":{"^v8.7.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Deployment
Management","release_note:skip","backport:prev-minor","v8.7.0"],"number":146371,"url":"https://github.com/elastic/kibana/pull/146371","mergeCommit":{"message":"[Form
lib] Fix validate method returning wrong state when called from onChange
handler (#146371)\n\nFixes
https://github.com/elastic/kibana/issues/145846\r\n\r\n###
Summary\r\nThis PR fixes a bug in the form lib where the `validate`
method would\r\nreturn the wrong state when called from an `onChange`
handler.
See\r\nhttps://github.com/elastic/kibana/issues/145846#issue-1457815157
for\r\nmore details.\r\n\r\nCo-authored-by: Muhammad Ibragimov
<muhammad.ibragimov@elastic.co>\r\nCo-authored-by: Yaroslav Kuznietsov
<kuznetsov.yaroslav.yk@gmail.com>\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"6814ed7e9109f0a5f90cc5e261eb0619312c78a2"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.7.0","labelRegex":"^v8.7.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/146371","number":146371,"mergeCommit":{"message":"[Form
lib] Fix validate method returning wrong state when called from onChange
handler (#146371)\n\nFixes
https://github.com/elastic/kibana/issues/145846\r\n\r\n###
Summary\r\nThis PR fixes a bug in the form lib where the `validate`
method would\r\nreturn the wrong state when called from an `onChange`
handler.
See\r\nhttps://github.com/elastic/kibana/issues/145846#issue-1457815157
for\r\nmore details.\r\n\r\nCo-authored-by: Muhammad Ibragimov
<muhammad.ibragimov@elastic.co>\r\nCo-authored-by: Yaroslav Kuznietsov
<kuznetsov.yaroslav.yk@gmail.com>\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"6814ed7e9109f0a5f90cc5e261eb0619312c78a2"}}]}]
BACKPORT-->

Co-authored-by: Muhammad Ibragimov <53621505+mibragimov@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2022-12-09 01:48:05 -05:00 committed by GitHub
parent c899dda360
commit 03db76d9a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 181 additions and 24 deletions

View file

@ -139,6 +139,7 @@ describe('<FieldEditorFlyoutContent />', () => {
await act(async () => {
find('fieldSaveButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
expect(onSave).toHaveBeenCalled();
@ -158,6 +159,7 @@ describe('<FieldEditorFlyoutContent />', () => {
await act(async () => {
find('fieldSaveButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
fieldReturned = onSave.mock.calls[onSave.mock.calls.length - 1][0];
@ -195,6 +197,7 @@ describe('<FieldEditorFlyoutContent />', () => {
await act(async () => {
find('fieldSaveButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
expect(onSave).toBeCalled();

View file

@ -53,6 +53,7 @@ export const getCommonActions = (testBed: TestBed) => {
await act(async () => {
testBed.form.toggleEuiSwitch(testSubj);
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
testBed.component.update();
@ -62,6 +63,7 @@ export const getCommonActions = (testBed: TestBed) => {
const updateName = async (value: string) => {
await act(async () => {
testBed.form.setInputValue('nameField.input', value);
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
testBed.component.update();
@ -70,6 +72,7 @@ export const getCommonActions = (testBed: TestBed) => {
const updateScript = async (value: string) => {
await act(async () => {
testBed.form.setInputValue('scriptField', value);
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
testBed.component.update();
@ -83,6 +86,7 @@ export const getCommonActions = (testBed: TestBed) => {
label: label ?? value,
},
]);
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
testBed.component.update();

View file

@ -346,7 +346,9 @@ describe('<UseField />', () => {
expect(isValid).toBeUndefined(); // Initially the form validity is undefined...
await act(async () => {
await formHook!.validate(); // ...until we validate the form
const validatePromise = formHook!.validate(); // ...until we validate the form
jest.advanceTimersByTime(0);
await validatePromise;
});
({ isValid } = formHook);

View file

@ -83,6 +83,7 @@ describe('useForm() hook', () => {
await act(async () => {
setInputValue('usernameField', 'John');
component.find('button').simulate('click');
jest.advanceTimersByTime(0);
});
const [formData, isValid] = onFormData.mock.calls[onFormData.mock.calls.length - 1];
@ -134,6 +135,7 @@ describe('useForm() hook', () => {
setInputValue('tagField2', expectedData.tags[1]);
component.find('button').simulate('click');
jest.advanceTimersByTime(0);
});
const [formData] = onFormData.mock.calls[onFormData.mock.calls.length - 1];
@ -184,16 +186,20 @@ describe('useForm() hook', () => {
let isValid;
await act(async () => {
({ data, isValid } = await formHook!.submit());
const submitPromise = formHook!.submit();
jest.advanceTimersByTime(0);
({ data, isValid } = await submitPromise);
});
expect(isValid).toBe(true);
expect(data).toEqual({ username: 'initialValue' });
setInputValue('myField', 'wrongValue'); // Validation will fail
await act(async () => {
({ data, isValid } = await formHook!.submit());
setInputValue('myField', 'wrongValue'); // Validation will fail
const submitPromise = formHook!.submit();
jest.advanceTimersByTime(0);
({ data, isValid } = await submitPromise);
});
expect(isValid).toBe(false);
@ -254,6 +260,7 @@ describe('useForm() hook', () => {
// Make some changes to the form fields
await act(async () => {
setInputValue('usernameField', 'John');
jest.advanceTimersByTime(0);
});
[{ data, isValid }] = onFormData.mock.calls[
@ -577,7 +584,9 @@ describe('useForm() hook', () => {
let isValid: boolean = false;
await act(async () => {
isValid = await formHook!.validate();
const validatePromise = formHook!.validate();
jest.advanceTimersByTime(0);
isValid = await validatePromise;
});
expect(isValid).toBe(true);
@ -624,11 +633,65 @@ describe('useForm() hook', () => {
});
await act(async () => {
isValid = await formHook!.validate();
const validatePromise = formHook!.validate();
jest.advanceTimersByTime(0);
isValid = await validatePromise;
});
expect(isValid).toBe(false);
});
test('should return correct state when validating a form field (combo box)', async () => {
let fieldHook: FieldHook<string[], unknown>;
const TestComp = () => {
const { form } = useForm();
formHook = form;
return (
<Form form={form}>
<UseField
path="test-path"
defaultValue={['foo']}
config={{
validations: [
{
validator: emptyField('error-message'),
},
],
}}
>
{(field) => {
fieldHook = field;
return <ComboBoxField field={field as FieldHook} />;
}}
</UseField>
</Form>
);
};
registerTestBed(TestComp)();
let isValid: boolean = false;
await act(async () => {
fieldHook.setValue([]);
const validatePromise = formHook!.validate();
jest.advanceTimersByTime(0);
isValid = await validatePromise;
});
expect(isValid).toBe(false);
await act(async () => {
fieldHook.setValue(['bar']);
const validatePromise = formHook!.validate();
jest.advanceTimersByTime(0);
isValid = await validatePromise;
});
expect(isValid).toBe(true);
});
});
describe('form.getErrors()', () => {
@ -679,7 +742,9 @@ describe('useForm() hook', () => {
expect(errors).toEqual([]);
await act(async () => {
await formHook!.submit();
const submitPromise = formHook!.submit();
jest.advanceTimersByTime(0);
await submitPromise;
});
errors = formHook!.getErrors();
expect(errors).toEqual(['Field1 can not be empty']);

View file

@ -233,19 +233,30 @@ export function useForm<T extends FormData = FormData, I extends FormData = T>(
const waitForFieldsToFinishValidating = useCallback(async () => {
let areSomeFieldValidating = fieldsToArray().some((field) => field.isValidating);
if (!areSomeFieldValidating) {
return;
}
return new Promise<void>((resolve) => {
setTimeout(() => {
areSomeFieldValidating = fieldsToArray().some((field) => field.isValidating);
if (areSomeFieldValidating) {
// Recursively wait for all the fields to finish validating.
return waitForFieldsToFinishValidating().then(resolve);
}
resolve();
}, 100);
if (areSomeFieldValidating) {
setTimeout(() => {
areSomeFieldValidating = fieldsToArray().some((field) => field.isValidating);
if (areSomeFieldValidating) {
// Recursively wait for all the fields to finish validating.
return waitForFieldsToFinishValidating().then(resolve);
}
resolve();
}, 100);
} else {
/*
* We need to use "setTimeout()" to ensure that the "validate()" method
* returns a Promise that is resolved on the next tick. This is important
* because the "validate()" method is often called in a "useEffect()" hook
* and we want the "useEffect()" to be triggered on the next tick. If we
* don't use "setTimeout()" the "useEffect()" would be triggered on the same
* tick and would not have access to the latest form state.
* This is also why we don't use "Promise.resolve()" here. It would resolve
* the Promise on the same tick.
*/
setTimeout(resolve, 0);
}
});
}, [fieldsToArray]);

View file

@ -34,8 +34,12 @@ describe('<EditPolicy /> request flyout', () => {
});
test('renders a json in flyout for a default policy', async () => {
const { actions } = testBed;
const { actions, component } = testBed;
await actions.openRequestFlyout();
await act(async () => {
jest.advanceTimersByTime(0); // allow the flyout to open and form validation to run
});
component.update();
const json = actions.getRequestJson();
const expected = `PUT _ilm/policy/my_policy\n${JSON.stringify(

View file

@ -15,6 +15,7 @@ export const createRequestFlyoutActions = (testBed: TestBed) => {
const openRequestFlyout = async () => {
await act(async () => {
find('requestButton').simulate('click');
jest.advanceTimersByTime(0); // Wait for the flyout to open and form validation to run
});
component.update();
};

View file

@ -13,6 +13,7 @@ export const createSavePolicyAction = (testBed: TestBed) => async () => {
await act(async () => {
find('savePolicyButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();

View file

@ -120,6 +120,7 @@ describe('<TemplateCreate />', () => {
await act(async () => {
actions.clickNextButton();
jest.advanceTimersByTime(0);
});
component.update();
@ -159,6 +160,7 @@ describe('<TemplateCreate />', () => {
beforeEach(async () => {
const { actions } = testBed;
await actions.completeStepOne({ name: TEMPLATE_NAME, indexPatterns: ['index1'] });
jest.advanceTimersByTime(0);
});
it('should set the correct page title', async () => {

View file

@ -195,6 +195,7 @@ describe('<TemplateEdit />', () => {
// Make some changes to the mappings
await act(async () => {
actions.clickEditButtonAtField(0); // Select the first field to edit
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
@ -204,6 +205,7 @@ describe('<TemplateEdit />', () => {
// Change the field name
await act(async () => {
form.setInputValue('nameParameterInput', UPDATED_MAPPING_TEXT_FIELD_NAME);
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
// Save changes on the field

View file

@ -26,6 +26,7 @@ export const formSetup = async (initTestBed: SetupFunc<TestSubjects>) => {
// User actions
const clickNextButton = () => {
testBed.find('nextButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
};
const clickBackButton = () => {
@ -38,6 +39,7 @@ export const formSetup = async (initTestBed: SetupFunc<TestSubjects>) => {
const clickEditFieldUpdateButton = () => {
testBed.find('editFieldUpdateButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
};
const deleteMappingsFieldAt = (index: number) => {
@ -102,6 +104,7 @@ export const formSetup = async (initTestBed: SetupFunc<TestSubjects>) => {
await act(async () => {
form.setInputValue('nameParameterInput', name);
jest.advanceTimersByTime(0);
find('createFieldForm.mockComboBox').simulate('change', [
{
label: type,
@ -112,6 +115,7 @@ export const formSetup = async (initTestBed: SetupFunc<TestSubjects>) => {
await act(async () => {
find('createFieldForm.addButton').simulate('click');
jest.advanceTimersByTime(0);
});
component.update();
@ -155,6 +159,7 @@ export const formSetup = async (initTestBed: SetupFunc<TestSubjects>) => {
act(() => {
find('mockComboBox').simulate('change', indexPatternsFormatted); // Using mocked EuiComboBox
jest.advanceTimersByTime(0);
});
}
@ -201,6 +206,7 @@ export const formSetup = async (initTestBed: SetupFunc<TestSubjects>) => {
await act(async () => {
clickNextButton();
jest.advanceTimersByTime(0);
});
component.update();
@ -214,11 +220,13 @@ export const formSetup = async (initTestBed: SetupFunc<TestSubjects>) => {
find('settingsEditor').simulate('change', {
jsonString: settings,
}); // Using mocked EuiCodeEditor
jest.advanceTimersByTime(0);
}
});
await act(async () => {
clickNextButton();
jest.advanceTimersByTime(0);
});
component.update();
@ -236,6 +244,7 @@ export const formSetup = async (initTestBed: SetupFunc<TestSubjects>) => {
await act(async () => {
clickNextButton();
jest.advanceTimersByTime(0);
});
component.update();
@ -249,6 +258,7 @@ export const formSetup = async (initTestBed: SetupFunc<TestSubjects>) => {
find('aliasesEditor').simulate('change', {
jsonString: aliases,
}); // Using mocked EuiCodeEditor
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
}

View file

@ -113,12 +113,14 @@ const createActions = (testBed: TestBed<TestSubjects>) => {
const addField = async (name: string, type: string, subType?: string) => {
await act(async () => {
form.setInputValue('nameParameterInput', name);
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
find('createFieldForm.fieldType').simulate('change', [
{
label: type,
value: type,
},
]);
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
@ -127,11 +129,13 @@ const createActions = (testBed: TestBed<TestSubjects>) => {
await act(async () => {
// subType is a text input
form.setInputValue('createFieldForm.fieldSubType', subType);
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
}
await act(async () => {
find('createFieldForm.addButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
@ -141,6 +145,7 @@ const createActions = (testBed: TestBed<TestSubjects>) => {
const { testSubject } = getFieldAt(path);
await act(async () => {
find(`${testSubject}.editFieldButton` as TestSubjects).simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
};
@ -148,6 +153,7 @@ const createActions = (testBed: TestBed<TestSubjects>) => {
const updateFieldAndCloseFlyout = async () => {
await act(async () => {
find('mappingsEditorFieldEdit.editFieldUpdateButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
};
@ -187,7 +193,9 @@ const createActions = (testBed: TestBed<TestSubjects>) => {
await act(async () => {
form.setInputValue('runtimeFieldEditor.nameField.input', field.name);
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
form.setInputValue('runtimeFieldEditor.scriptField', field.script.source);
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
find('typeField').simulate('change', [
{
label: valueToLabelMap[field.type],
@ -219,6 +227,7 @@ const createActions = (testBed: TestBed<TestSubjects>) => {
await act(async () => {
find('runtimeFieldEditor.saveFieldButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
};
@ -269,12 +278,14 @@ const createActions = (testBed: TestBed<TestSubjects>) => {
await act(async () => {
tabElement.simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
};
const updateJsonEditor = (testSubject: TestSubjects, value: object) => {
find(testSubject).simulate('change', { jsonString: JSON.stringify(value) });
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
};
const getJsonEditorValue = (testSubject: TestSubjects) => {
@ -367,7 +378,9 @@ export const getMappingsEditorDataFactory = (onChangeHandler: jest.MockedFunctio
if (isMappingsValid === undefined) {
await act(async () => {
isMappingsValid = await validate();
const validatePromise = validate();
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
isMappingsValid = await validatePromise;
});
component.update();
}

View file

@ -374,6 +374,7 @@ describe('Mappings editor: core', () => {
*/
await act(async () => {
find('addFieldButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
@ -418,6 +419,7 @@ describe('Mappings editor: core', () => {
// Disbable dynamic mappings
await act(async () => {
form.toggleEuiSwitch('advancedConfiguration.dynamicMappingsToggle.input');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
({ data } = await getMappingsEditorData(component));

View file

@ -221,6 +221,7 @@ describe('Mappings editor: runtime fields', () => {
await act(async () => {
find('runtimeFieldEditor.saveFieldButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();

View file

@ -85,16 +85,19 @@ const createActions = (testBed: TestBed<TestSubject>) => {
find(`${processorsSelector}.addProcessorButton`).simulate('click');
await act(async () => {
find('processorTypeSelector.input').simulate('change', [{ value: type, label: type }]);
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
await act(async () => {
find('processorOptionsEditor').simulate('change', {
jsonContent: JSON.stringify(options),
});
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
await act(async () => {
find('addProcessorForm.submitButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
},
@ -134,15 +137,18 @@ const createActions = (testBed: TestBed<TestSubject>) => {
find(`${processorSelector}.moreMenu.addOnFailureButton`).simulate('click');
await act(async () => {
find('processorTypeSelector.input').simulate('change', [{ value: type, label: type }]);
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
await act(async () => {
find('processorOptionsEditor').simulate('change', {
jsonContent: JSON.stringify(options),
});
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
await act(async () => {
find('addProcessorForm.submitButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
},
@ -172,6 +178,7 @@ const createActions = (testBed: TestBed<TestSubject>) => {
submitProcessorForm: async () => {
await act(async () => {
find('editProcessorForm.submitButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
},
};

View file

@ -102,6 +102,7 @@ describe('Pipeline Editor', () => {
actions.openProcessorEditor('processors>2');
expect(exists('editProcessorForm')).toBeTruthy();
form.setInputValue('editProcessorForm.valueFieldInput', 'test44');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
await actions.submitProcessorForm();
const [onUpdateResult] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1];
const {

View file

@ -84,6 +84,7 @@ const createActions = (testBed: TestBed<TestSubject>) => {
async saveNewProcessor() {
await act(async () => {
find('addProcessorForm.submitButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
},

View file

@ -98,6 +98,7 @@ const createActions = (testBed: TestBed<TestSubject>) => {
async clickProcessorOutputTab() {
await act(async () => {
find('outputTab').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
},
@ -112,6 +113,7 @@ const createActions = (testBed: TestBed<TestSubject>) => {
async clickRunPipelineButton() {
await act(async () => {
find('runPipelineButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
},
@ -119,6 +121,7 @@ const createActions = (testBed: TestBed<TestSubject>) => {
async toggleVerboseSwitch() {
await act(async () => {
form.toggleEuiSwitch('verboseOutputToggle');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
},
@ -127,6 +130,7 @@ const createActions = (testBed: TestBed<TestSubject>) => {
find('documentsEditor').simulate('change', {
jsonString,
});
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
},
clickDocumentsDropdown() {
@ -181,6 +185,7 @@ const createActions = (testBed: TestBed<TestSubject>) => {
async clickAddDocumentButton() {
await act(async () => {
find('addDocumentButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
},

View file

@ -220,7 +220,9 @@ describe('Test pipeline', () => {
// Add required fields, and click run
form.setInputValue('indexField.input', index);
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
form.setInputValue('idField.input', documentId);
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
await actions.clickAddDocumentButton();
// Verify request

View file

@ -72,7 +72,9 @@ describe('Runtime field editor', () => {
let data;
await act(async () => {
({ data } = await lastState.submit());
const submit = lastState.submit();
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
({ data } = await submit);
});
expect(data).toEqual(defaultValue);
@ -93,6 +95,7 @@ describe('Runtime field editor', () => {
await act(async () => {
form.setInputValue('nameField.input', existingConcreteFields[0].name);
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
@ -117,7 +120,9 @@ describe('Runtime field editor', () => {
});
await act(async () => {
await lastOnChangeCall()[0].submit();
const submit = lastOnChangeCall()[0].submit();
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
await submit;
});
component.update();
@ -145,7 +150,9 @@ describe('Runtime field editor', () => {
const { form } = testBed;
await act(async () => {
await lastOnChangeCall()[0].submit();
const submit = lastOnChangeCall()[0].submit();
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
await submit;
});
expect(lastOnChangeCall()[0].isValid).toBe(true);

View file

@ -33,6 +33,14 @@ const noop = () => {};
const defaultProps = { onSave: noop, onCancel: noop, docLinks };
describe('Runtime field editor flyout', () => {
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
test('should have a flyout title', () => {
const { exists, find } = setup(defaultProps);
@ -67,6 +75,7 @@ describe('Runtime field editor flyout', () => {
await act(async () => {
find('saveFieldButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
expect(onSave).toHaveBeenCalled();
@ -93,6 +102,7 @@ describe('Runtime field editor flyout', () => {
await act(async () => {
find('saveFieldButton').simulate('click');
jest.advanceTimersByTime(0); // advance timers to allow the form to validate
});
component.update();
@ -115,6 +125,7 @@ describe('Runtime field editor flyout', () => {
await act(async () => {
find('saveFieldButton').simulate('click');
jest.advanceTimersByTime(0);
});
expect(onSave).toHaveBeenCalled();
@ -133,9 +144,11 @@ describe('Runtime field editor flyout', () => {
value: 'other_type',
},
]);
jest.advanceTimersByTime(0);
});
await act(async () => {
find('saveFieldButton').simulate('click');
jest.advanceTimersByTime(0);
});
fieldReturned = onSave.mock.calls[onSave.mock.calls.length - 1][0];
expect(fieldReturned).toEqual({