[Remote clusters] Refactor tests (#82517)

This commit is contained in:
Alison Goryachev 2020-11-05 09:01:42 -05:00 committed by GitHub
parent 051ed13858
commit ea0736e74a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 142 additions and 80 deletions

View file

@ -3,10 +3,10 @@
* 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 { registerTestBed } from '../../../../../test_utils';
/* eslint-disable @kbn/eslint/no-restricted-paths */
import { RemoteClusterAdd } from '../../../public/application/sections/remote_cluster_add';
import { createRemoteClustersStore } from '../../../public/application/store';
import { registerRouter } from '../../../public/application/services/routing';
@ -24,8 +24,12 @@ export const setup = (props) => {
const testBed = initTestBed(props);
// User actions
const clickSaveForm = () => {
testBed.find('remoteClusterFormSaveButton').simulate('click');
const clickSaveForm = async () => {
await act(async () => {
testBed.find('remoteClusterFormSaveButton').simulate('click');
});
testBed.component.update();
};
return {

View file

@ -3,8 +3,9 @@
* 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 { nextTick, setupEnvironment } from '../helpers';
import { setupEnvironment } from '../helpers';
import { NON_ALPHA_NUMERIC_CHARS, ACCENTED_CHARS } from './special_characters';
import { setup } from './remote_clusters_add.helpers';
@ -15,6 +16,7 @@ describe('Create Remote cluster', () => {
let actions;
let form;
let server;
let component;
beforeAll(() => {
({ server } = setupEnvironment());
@ -24,8 +26,11 @@ describe('Create Remote cluster', () => {
server.restore();
});
beforeEach(() => {
({ form, exists, find, actions } = setup());
beforeEach(async () => {
await act(async () => {
({ form, exists, find, actions, component } = setup());
});
component.update();
});
test('should have the title of the page set correctly', () => {
@ -45,7 +50,11 @@ describe('Create Remote cluster', () => {
false
);
form.toggleEuiSwitch('remoteClusterFormSkipUnavailableFormToggle');
act(() => {
form.toggleEuiSwitch('remoteClusterFormSkipUnavailableFormToggle');
});
component.update();
expect(find('remoteClusterFormSkipUnavailableFormToggle').props()['aria-checked']).toBe(true);
});
@ -56,16 +65,20 @@ describe('Create Remote cluster', () => {
// By default it should be set to "false"
expect(find('remoteClusterFormConnectionModeToggle').props()['aria-checked']).toBe(false);
form.toggleEuiSwitch('remoteClusterFormConnectionModeToggle');
act(() => {
form.toggleEuiSwitch('remoteClusterFormConnectionModeToggle');
});
component.update();
expect(find('remoteClusterFormConnectionModeToggle').props()['aria-checked']).toBe(true);
});
test('should display errors and disable the save button when clicking "save" without filling the form', () => {
test('should display errors and disable the save button when clicking "save" without filling the form', async () => {
expect(exists('remoteClusterFormGlobalError')).toBe(false);
expect(find('remoteClusterFormSaveButton').props().disabled).toBe(false);
actions.clickSaveForm();
await actions.clickSaveForm();
expect(exists('remoteClusterFormGlobalError')).toBe(true);
expect(form.getErrorsMessages()).toEqual([
@ -83,19 +96,22 @@ describe('Create Remote cluster', () => {
let form;
beforeEach(async () => {
({ component, form, actions } = setup());
await act(async () => {
({ component, form, actions } = setup());
});
await nextTick();
component.update();
});
test('should not allow spaces', () => {
test('should not allow spaces', async () => {
form.setInputValue('remoteClusterFormNameInput', 'with space');
actions.clickSaveForm();
await actions.clickSaveForm();
expect(form.getErrorsMessages()).toContain('Spaces are not allowed in the name.');
});
test('should only allow alpha-numeric characters, "-" (dash) and "_" (underscore)', () => {
test('should only allow alpha-numeric characters, "-" (dash) and "_" (underscore)', async () => {
const expectInvalidChar = (char) => {
if (char === '-' || char === '_') {
return;
@ -103,6 +119,7 @@ describe('Create Remote cluster', () => {
try {
form.setInputValue('remoteClusterFormNameInput', `with${char}`);
expect(form.getErrorsMessages()).toContain(
`Remove the character ${char} from the name.`
);
@ -111,7 +128,7 @@ describe('Create Remote cluster', () => {
}
};
actions.clickSaveForm(); // display form errors
await actions.clickSaveForm(); // display form errors
[...NON_ALPHA_NUMERIC_CHARS, ...ACCENTED_CHARS].forEach(expectInvalidChar);
});
@ -120,13 +137,20 @@ describe('Create Remote cluster', () => {
describe('seeds', () => {
let actions;
let form;
let component;
beforeEach(async () => {
({ form, actions } = setup());
await act(async () => {
({ form, actions, component } = setup());
});
component.update();
form.setInputValue('remoteClusterFormNameInput', 'remote_cluster_test');
});
test('should only allow alpha-numeric characters and "-" (dash) in the node "host" part', () => {
actions.clickSaveForm(); // display form errors
test('should only allow alpha-numeric characters and "-" (dash) in the node "host" part', async () => {
await actions.clickSaveForm(); // display form errors
const notInArray = (array) => (value) => array.indexOf(value) < 0;
@ -142,8 +166,8 @@ describe('Create Remote cluster', () => {
.forEach(expectInvalidChar);
});
test('should require a numeric "port" to be set', () => {
actions.clickSaveForm();
test('should require a numeric "port" to be set', async () => {
await actions.clickSaveForm();
form.setComboBoxValue('remoteClusterFormSeedsInput', '192.168.1.1');
expect(form.getErrorsMessages()).toContain('A port is required.');
@ -156,16 +180,25 @@ describe('Create Remote cluster', () => {
describe('proxy address', () => {
let actions;
let form;
let component;
beforeEach(async () => {
({ form, actions } = setup());
await act(async () => {
({ form, actions, component } = setup());
});
// Enable "proxy" mode
form.toggleEuiSwitch('remoteClusterFormConnectionModeToggle');
component.update();
act(() => {
// Enable "proxy" mode
form.toggleEuiSwitch('remoteClusterFormConnectionModeToggle');
});
component.update();
});
test('should only allow alpha-numeric characters and "-" (dash) in the proxy address "host" part', () => {
actions.clickSaveForm(); // display form errors
test('should only allow alpha-numeric characters and "-" (dash) in the proxy address "host" part', async () => {
await actions.clickSaveForm(); // display form errors
const notInArray = (array) => (value) => array.indexOf(value) < 0;
@ -181,8 +214,8 @@ describe('Create Remote cluster', () => {
.forEach(expectInvalidChar);
});
test('should require a numeric "port" to be set', () => {
actions.clickSaveForm();
test('should require a numeric "port" to be set', async () => {
await actions.clickSaveForm();
form.setInputValue('remoteClusterFormProxyAddressInput', '192.168.1.1');
expect(form.getErrorsMessages()).toContain('A port is required.');

View file

@ -6,7 +6,6 @@
import { registerTestBed } from '../../../../../test_utils';
/* eslint-disable @kbn/eslint/no-restricted-paths */
import { RemoteClusterEdit } from '../../../public/application/sections/remote_cluster_edit';
import { createRemoteClustersStore } from '../../../public/application/store';
import { registerRouter } from '../../../public/application/services/routing';

View file

@ -16,28 +16,23 @@ import {
} from './remote_clusters_edit.helpers';
describe('Edit Remote cluster', () => {
let server;
let httpRequestsMockHelpers;
let component;
let find;
let exists;
let waitFor;
beforeAll(() => {
({ server, httpRequestsMockHelpers } = setupEnvironment());
});
const { server, httpRequestsMockHelpers } = setupEnvironment();
afterAll(() => {
server.restore();
});
beforeEach(async () => {
httpRequestsMockHelpers.setLoadRemoteClustersResponse([REMOTE_CLUSTER_EDIT]);
httpRequestsMockHelpers.setLoadRemoteClustersResponse([REMOTE_CLUSTER_EDIT]);
beforeEach(async () => {
await act(async () => {
({ component, find, exists, waitFor } = setup());
await waitFor('remoteClusterForm');
({ component, find, exists } = setup());
});
component.update();
});
test('should have the title of the page set correctly', () => {
@ -59,9 +54,10 @@ describe('Edit Remote cluster', () => {
await act(async () => {
addRemoteClusterTestBed = setupRemoteClustersAdd();
addRemoteClusterTestBed.waitFor('remoteClusterAddPage');
});
addRemoteClusterTestBed.component.update();
const formEdit = component.find(RemoteClusterForm);
const formAdd = addRemoteClusterTestBed.component.find(RemoteClusterForm);

View file

@ -3,20 +3,17 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import axios from 'axios';
import axiosXhrAdapter from 'axios/lib/adapters/xhr';
import {
notificationServiceMock,
fatalErrorsServiceMock,
docLinksServiceMock,
injectedMetadataServiceMock,
} from '../../../../../../src/core/public/mocks';
import { usageCollectionPluginMock } from '../../../../../../src/plugins/usage_collection/public/mocks';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { HttpService } from '../../../../../../src/core/public/http';
/* eslint-disable @kbn/eslint/no-restricted-paths */
import { init as initBreadcrumb } from '../../../public/application/services/breadcrumb';
import { init as initHttp } from '../../../public/application/services/http';
import { init as initNotification } from '../../../public/application/services/notification';
@ -25,10 +22,10 @@ import { init as initDocumentation } from '../../../public/application/services/
import { init as initHttpRequests } from './http_requests';
export const setupEnvironment = () => {
const httpServiceSetupMock = new HttpService().setup({
injectedMetadata: injectedMetadataServiceMock.createSetupContract(),
fatalErrors: fatalErrorsServiceMock.createSetupContract(),
});
// axios has a similar interface to HttpSetup, but we
// flatten out the response.
const mockHttpClient = axios.create({ adapter: axiosXhrAdapter });
mockHttpClient.interceptors.response.use(({ data }) => data);
initBreadcrumb(() => {});
initDocumentation(docLinksServiceMock.createStartContract());
@ -37,7 +34,7 @@ export const setupEnvironment = () => {
notificationServiceMock.createSetupContract().toasts,
fatalErrorsServiceMock.createSetupContract()
);
initHttp(httpServiceSetupMock);
initHttp(mockHttpClient);
const { server, httpRequestsMockHelpers } = initHttpRequests();

View file

@ -3,10 +3,10 @@
* 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 { registerTestBed, findTestSubject } from '../../../../../test_utils';
/* eslint-disable @kbn/eslint/no-restricted-paths */
import { RemoteClusterList } from '../../../public/application/sections/remote_cluster_list';
import { createRemoteClustersStore } from '../../../public/application/store';
import { registerRouter } from '../../../public/application/services/routing';
@ -29,15 +29,26 @@ export const setup = (props) => {
const { rows } = testBed.table.getMetaData(EUI_TABLE);
const row = rows[index];
const checkBox = row.reactWrapper.find('input').hostNodes();
checkBox.simulate('change', { target: { checked: true } });
act(() => {
checkBox.simulate('change', { target: { checked: true } });
});
testBed.component.update();
};
const clickBulkDeleteButton = () => {
testBed.find('remoteClusterBulkDeleteButton').simulate('click');
const { find, component } = testBed;
act(() => {
find('remoteClusterBulkDeleteButton').simulate('click');
});
component.update();
};
const clickRowActionButtonAt = (index = 0, action = 'delete') => {
const { rows } = testBed.table.getMetaData(EUI_TABLE);
const { table, component } = testBed;
const { rows } = table.getMetaData(EUI_TABLE);
const indexLastColumn = rows[index].columns.length - 1;
const tableCellActions = rows[index].columns[indexLastColumn].reactWrapper;
@ -45,32 +56,54 @@ export const setup = (props) => {
if (action === 'delete') {
button = findTestSubject(tableCellActions, 'remoteClusterTableRowRemoveButton');
} else if (action === 'edit') {
findTestSubject(tableCellActions, 'remoteClusterTableRowEditButton');
button = findTestSubject(tableCellActions, 'remoteClusterTableRowEditButton');
}
if (!button) {
throw new Error(`Button for action "${action}" not found.`);
}
button.simulate('click');
act(() => {
button.simulate('click');
});
component.update();
};
const clickConfirmModalDeleteRemoteCluster = () => {
const modal = testBed.find('remoteClustersDeleteConfirmModal');
findTestSubject(modal, 'confirmModalConfirmButton').simulate('click');
const { find, component } = testBed;
const modal = find('remoteClustersDeleteConfirmModal');
act(() => {
findTestSubject(modal, 'confirmModalConfirmButton').simulate('click');
});
component.update();
};
const clickRemoteClusterAt = (index = 0) => {
const { rows } = testBed.table.getMetaData(EUI_TABLE);
const { table, component } = testBed;
const { rows } = table.getMetaData(EUI_TABLE);
const remoteClusterLink = findTestSubject(
rows[index].reactWrapper,
'remoteClustersTableListClusterLink'
);
remoteClusterLink.simulate('click');
act(() => {
remoteClusterLink.simulate('click');
});
component.update();
};
const clickPaginationNextButton = () => {
testBed.find('remoteClusterListTable.pagination-button-next').simulate('click');
const { find, component } = testBed;
act(() => {
find('remoteClusterListTable.pagination-button-next').simulate('click');
});
component.update();
};
return {

View file

@ -10,25 +10,23 @@ import { getRemoteClusterMock } from '../../../fixtures/remote_cluster';
import { PROXY_MODE } from '../../../common/constants';
import { setupEnvironment, nextTick, getRandomString, findTestSubject } from '../helpers';
import { setupEnvironment, getRandomString, findTestSubject } from '../helpers';
import { setup } from './remote_clusters_list.helpers';
describe('<RemoteClusterList />', () => {
let server;
let httpRequestsMockHelpers;
const { server, httpRequestsMockHelpers } = setupEnvironment();
beforeAll(() => {
({ server, httpRequestsMockHelpers } = setupEnvironment());
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
server.restore();
});
beforeEach(() => {
httpRequestsMockHelpers.setLoadRemoteClustersResponse([]);
});
httpRequestsMockHelpers.setLoadRemoteClustersResponse([]);
describe('on component mount', () => {
let exists;
@ -47,9 +45,10 @@ describe('<RemoteClusterList />', () => {
let component;
beforeEach(async () => {
({ exists, component } = setup());
await act(async () => {
({ exists, component } = setup());
});
await nextTick(100); // We need to wait next tick for the mock server response to kick in
component.update();
});
@ -66,7 +65,7 @@ describe('<RemoteClusterList />', () => {
let find;
let table;
let actions;
let waitFor;
let component;
let form;
const remoteClusters = [
@ -87,9 +86,10 @@ describe('<RemoteClusterList />', () => {
httpRequestsMockHelpers.setLoadRemoteClustersResponse(remoteClusters);
await act(async () => {
({ find, table, actions, waitFor, form } = setup());
await waitFor('remoteClusterListTable');
({ find, table, actions, form, component } = setup());
});
component.update();
});
test('pagination works', () => {
@ -117,7 +117,6 @@ describe('<RemoteClusterList />', () => {
let actions;
let tableCellsValues;
let rows;
let waitFor;
// For deterministic tests, we need to make sure that remoteCluster1 comes before remoteCluster2
// in the table list that is rendered. As the table orders alphabetically by index name
@ -151,11 +150,11 @@ describe('<RemoteClusterList />', () => {
httpRequestsMockHelpers.setLoadRemoteClustersResponse(remoteClusters);
await act(async () => {
({ component, find, exists, table, actions, waitFor } = setup());
await waitFor('remoteClusterListTable');
({ component, find, exists, table, actions } = setup());
});
component.update();
// Read the remote clusters list table
({ rows, tableCellsValues } = table.getMetaData('remoteClusterListTable'));
});
@ -282,10 +281,11 @@ describe('<RemoteClusterList />', () => {
actions.clickConfirmModalDeleteRemoteCluster();
await act(async () => {
await nextTick(600); // there is a 500ms timeout in the api action
component.update();
jest.advanceTimersByTime(600); // there is a 500ms timeout in the api action
});
component.update();
({ rows } = table.getMetaData('remoteClusterListTable'));
expect(rows.length).toBe(2);

View file

@ -122,7 +122,7 @@ In order to prevent flakiness in component integration tests, please consider th
- Be **synchronous** as much as possible.
Hooks are delicate when it comes to state updates. Sometimes calling `act()` synchronously works, sometimes it doesn't. The reasoning behind this isn't clear yet. The best approach is to try synchrounsly first and if it fails, because of an `act()` error, then use the async version.
Hooks are delicate when it comes to state updates. Sometimes calling `act()` synchronously works, sometimes it doesn't. The reasoning behind this isn't clear yet. The best approach is to try synchronously first and if it fails, because of an `act()` error, then use the async version.
```js
// First try this