Skip to content

Commit

Permalink
Refactored component edit policy tests into separate folders and usin…
Browse files Browse the repository at this point in the history
…g client integration testing setup (elastic#91657)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
yuliacech and kibanamachine committed Feb 19, 2021
1 parent a23cab2 commit f9fbf91
Show file tree
Hide file tree
Showing 19 changed files with 1,370 additions and 1,159 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* 2.0.
*/

import moment from 'moment-timezone';

import { PolicyFromES } from '../../../common/types';

export const POLICY_NAME = 'my_policy';
Expand Down Expand Up @@ -234,3 +236,32 @@ export const POLICY_WITH_KNOWN_AND_UNKNOWN_FIELDS = ({
},
name: POLICY_NAME,
} as any) as PolicyFromES;

export const getGeneratedPolicies = (): PolicyFromES[] => {
const policy = {
phases: {
hot: {
min_age: '0s',
actions: {
rollover: {
max_size: '1gb',
},
},
},
},
};
const policies: PolicyFromES[] = [];
for (let i = 0; i < 105; i++) {
policies.push({
version: i,
modified_date: moment().subtract(i, 'days').toISOString(),
linkedIndices: i % 2 === 0 ? [`index${i}`] : undefined,
name: `testy${i}`,
policy: {
...policy,
name: `testy${i}`,
},
});
}
return policies;
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ import { KibanaContextProvider } from '../../../public/shared_imports';
import { AppServicesContext } from '../../../public/types';
import { createBreadcrumbsMock } from '../../../public/application/services/breadcrumbs.mock';

import { TestSubjects } from '../helpers';
import { POLICY_NAME } from './constants';

type Phases = keyof PolicyPhases;

import { POLICY_NAME } from './constants';
import { TestSubjects } from '../helpers';
window.scrollTo = jest.fn();

jest.mock('@elastic/eui', () => {
const original = jest.requireActual('@elastic/eui');
Expand All @@ -46,14 +48,17 @@ jest.mock('@elastic/eui', () => {
};
});

const testBedConfig: TestBedConfig = {
memoryRouter: {
initialEntries: [`/policies/edit/${POLICY_NAME}`],
componentRoutePath: `/policies/edit/:policyName`,
},
defaultProps: {
getUrlForApp: () => {},
},
const getTestBedConfig = (testBedConfigArgs?: Partial<TestBedConfig>): TestBedConfig => {
return {
memoryRouter: {
initialEntries: [`/policies/edit/${POLICY_NAME}`],
componentRoutePath: `/policies/edit/:policyName`,
},
defaultProps: {
getUrlForApp: () => {},
},
...testBedConfigArgs,
};
};

const breadcrumbService = createBreadcrumbsMock();
Expand All @@ -72,13 +77,22 @@ const MyComponent = ({ appServicesContext, ...rest }: any) => {
);
};

const initTestBed = registerTestBed<TestSubjects>(MyComponent, testBedConfig);
const initTestBed = (arg?: {
appServicesContext?: Partial<AppServicesContext>;
testBedConfig?: Partial<TestBedConfig>;
}) => {
const { testBedConfig: testBedConfigArgs, ...rest } = arg || {};
return registerTestBed<TestSubjects>(MyComponent, getTestBedConfig(testBedConfigArgs))(rest);
};

type SetupReturn = ReturnType<typeof setup>;

export type EditPolicyTestBed = SetupReturn extends Promise<infer U> ? U : SetupReturn;

export const setup = async (arg?: { appServicesContext: Partial<AppServicesContext> }) => {
export const setup = async (arg?: {
appServicesContext?: Partial<AppServicesContext>;
testBedConfig?: Partial<TestBedConfig>;
}) => {
const testBed = await initTestBed(arg);

const { find, component, form, exists } = testBed;
Expand Down Expand Up @@ -169,34 +183,15 @@ export const setup = async (arg?: { appServicesContext: Partial<AppServicesConte

const enable = (phase: Phases) => createFormToggleAction(`enablePhaseSwitch-${phase}`);

const setMinAgeValue = (phase: Phases) => createFormSetValueAction(`${phase}-selectedMinimumAge`);

const setMinAgeUnits = (phase: Phases) =>
createFormSetValueAction(`${phase}-selectedMinimumAgeUnits`);

const setDataAllocation = (phase: Phases) => async (value: DataTierAllocationType) => {
act(() => {
find(`${phase}-dataTierAllocationControls.dataTierSelect`).simulate('click');
});
component.update();
await act(async () => {
switch (value) {
case 'node_roles':
find(`${phase}-dataTierAllocationControls.defaultDataAllocationOption`).simulate('click');
break;
case 'node_attrs':
find(`${phase}-dataTierAllocationControls.customDataAllocationOption`).simulate('click');
break;
default:
find(`${phase}-dataTierAllocationControls.noneDataAllocationOption`).simulate('click');
}
});
component.update();
const createMinAgeActions = (phase: Phases) => {
return {
hasMinAgeInput: () => exists(`${phase}-selectedMinimumAge`),
setMinAgeValue: createFormSetValueAction(`${phase}-selectedMinimumAge`),
setMinAgeUnits: createFormSetValueAction(`${phase}-selectedMinimumAgeUnits`),
hasRolloverTipOnMinAge: () => exists(`${phase}-rolloverMinAgeInputIconTip`),
};
};

const setSelectedNodeAttribute = (phase: Phases) =>
createFormSetValueAction(`${phase}-selectedNodeAttrs`);

const setReplicas = (phase: Phases) => async (value: string) => {
if (!exists(`${phase}-selectedReplicaCount`)) {
await createFormToggleAction(`${phase}-setReplicasSwitch`)(true);
Expand All @@ -216,8 +211,12 @@ export const setup = async (arg?: { appServicesContext: Partial<AppServicesConte
const setFreeze = createFormToggleAction('freezeSwitch');
const freezeExists = () => exists('freezeSwitch');

const setReadonly = (phase: Phases) => async (value: boolean) => {
await createFormToggleAction(`${phase}-readonlySwitch`)(value);
const createReadonlyActions = (phase: Phases) => {
const toggleSelector = `${phase}-readonlySwitch`;
return {
readonlyExists: () => exists(toggleSelector),
toggleReadonly: createFormToggleAction(toggleSelector),
};
};

const createSearchableSnapshotActions = (phase: Phases) => {
Expand Down Expand Up @@ -271,17 +270,93 @@ export const setup = async (arg?: { appServicesContext: Partial<AppServicesConte
};
};

const hasRolloverTipOnMinAge = (phase: Phases) => (): boolean =>
exists(`${phase}-rolloverMinAgeInputIconTip`);
const hasRolloverSettingRequiredCallout = (): boolean => exists('rolloverSettingsRequired');

const createNodeAllocationActions = (phase: Phases) => {
const controlsSelector = `${phase}-dataTierAllocationControls`;
const dataTierSelector = `${controlsSelector}.dataTierSelect`;
const nodeAttrsSelector = `${phase}-selectedNodeAttrs`;

return {
hasDataTierAllocationControls: () => exists(controlsSelector),
openNodeAttributesSection: async () => {
await act(async () => {
find(dataTierSelector).simulate('click');
});
component.update();
},
hasNodeAttributesSelect: (): boolean => exists(nodeAttrsSelector),
getNodeAttributesSelectOptions: () => find(nodeAttrsSelector).find('option'),
setDataAllocation: async (value: DataTierAllocationType) => {
act(() => {
find(dataTierSelector).simulate('click');
});
component.update();
await act(async () => {
switch (value) {
case 'node_roles':
find(`${controlsSelector}.defaultDataAllocationOption`).simulate('click');
break;
case 'node_attrs':
find(`${controlsSelector}.customDataAllocationOption`).simulate('click');
break;
default:
find(`${controlsSelector}.noneDataAllocationOption`).simulate('click');
}
});
component.update();
},
setSelectedNodeAttribute: createFormSetValueAction(nodeAttrsSelector),
hasNoNodeAttrsWarning: () => exists('noNodeAttributesWarning'),
hasDefaultAllocationWarning: () => exists('defaultAllocationWarning'),
hasDefaultAllocationNotice: () => exists('defaultAllocationNotice'),
hasNodeDetailsFlyout: () => exists(`${phase}-viewNodeDetailsFlyoutButton`),
openNodeDetailsFlyout: async () => {
await act(async () => {
find(`${phase}-viewNodeDetailsFlyoutButton`).simulate('click');
});
component.update();
},
};
};

const expectErrorMessages = (expectedMessages: string[]) => {
const errorMessages = component.find('.euiFormErrorText');
expect(errorMessages.length).toBe(expectedMessages.length);
expectedMessages.forEach((expectedErrorMessage) => {
let foundErrorMessage;
for (let i = 0; i < errorMessages.length; i++) {
if (errorMessages.at(i).text() === expectedErrorMessage) {
foundErrorMessage = true;
}
}
expect(foundErrorMessage).toBe(true);
});
};

/*
* For new we rely on a setTimeout to ensure that error messages have time to populate
* the form object before we look at the form object. See:
* x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/form_errors_context.tsx
* for where this logic lives.
*/
const runTimers = () => {
act(() => {
jest.runAllTimers();
});
component.update();
};

return {
...testBed,
runTimers,
actions: {
saveAsNewPolicy: createFormToggleAction('saveAsNewSwitch'),
setPolicyName: createFormSetValueAction('policyNameField'),
setWaitForSnapshotPolicy,
savePolicy,
hasGlobalErrorCallout: () => exists('policyFormErrorsCallout'),
expectErrorMessages,
timeline: {
hasHotPhase: () => exists('ilmTimelineHotPhase'),
hasWarmPhase: () => exists('ilmTimelineWarmPhase'),
Expand All @@ -294,46 +369,40 @@ export const setup = async (arg?: { appServicesContext: Partial<AppServicesConte
setMaxAge,
toggleRollover,
toggleDefaultRollover,
hasRolloverSettingRequiredCallout,
hasErrorIndicator: () => exists('phaseErrorIndicator-hot'),
...createForceMergeActions('hot'),
...createIndexPriorityActions('hot'),
...createShrinkActions('hot'),
setReadonly: setReadonly('hot'),
...createReadonlyActions('hot'),
...createSearchableSnapshotActions('hot'),
},
warm: {
enable: enable('warm'),
setMinAgeValue: setMinAgeValue('warm'),
setMinAgeUnits: setMinAgeUnits('warm'),
setDataAllocation: setDataAllocation('warm'),
setSelectedNodeAttribute: setSelectedNodeAttribute('warm'),
...createMinAgeActions('warm'),
setReplicas: setReplicas('warm'),
hasErrorIndicator: () => exists('phaseErrorIndicator-warm'),
hasRolloverTipOnMinAge: hasRolloverTipOnMinAge('warm'),
...createShrinkActions('warm'),
...createForceMergeActions('warm'),
setReadonly: setReadonly('warm'),
...createReadonlyActions('warm'),
...createIndexPriorityActions('warm'),
...createNodeAllocationActions('warm'),
},
cold: {
enable: enable('cold'),
setMinAgeValue: setMinAgeValue('cold'),
setMinAgeUnits: setMinAgeUnits('cold'),
setDataAllocation: setDataAllocation('cold'),
setSelectedNodeAttribute: setSelectedNodeAttribute('cold'),
...createMinAgeActions('cold'),
setReplicas: setReplicas('cold'),
setFreeze,
freezeExists,
hasErrorIndicator: () => exists('phaseErrorIndicator-cold'),
hasRolloverTipOnMinAge: hasRolloverTipOnMinAge('cold'),
...createIndexPriorityActions('cold'),
...createSearchableSnapshotActions('cold'),
...createNodeAllocationActions('cold'),
},
delete: {
isShown: () => exists('delete-phaseContent'),
...createToggleDeletePhaseActions(),
hasRolloverTipOnMinAge: hasRolloverTipOnMinAge('delete'),
setMinAgeValue: setMinAgeValue('delete'),
setMinAgeUnits: setMinAgeUnits('delete'),
...createMinAgeActions('delete'),
},
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import {
getDefaultHotPhasePolicy,
} from './constants';

window.scrollTo = jest.fn();

describe('<EditPolicy />', () => {
let testBed: EditPolicyTestBed;
const { server, httpRequestsMockHelpers } = setupEnvironment();
Expand Down Expand Up @@ -127,7 +125,7 @@ describe('<EditPolicy />', () => {
await actions.hot.setBestCompression(true);
await actions.hot.toggleShrink(true);
await actions.hot.setShrink('2');
await actions.hot.setReadonly(true);
await actions.hot.toggleReadonly(true);
await actions.hot.toggleIndexPriority(true);
await actions.hot.setIndexPriority('123');

Expand Down Expand Up @@ -271,7 +269,7 @@ describe('<EditPolicy />', () => {
await actions.warm.toggleForceMerge(true);
await actions.warm.setForcemergeSegmentsCount('123');
await actions.warm.setBestCompression(true);
await actions.warm.setReadonly(true);
await actions.warm.toggleReadonly(true);
await actions.warm.setIndexPriority('123');
await actions.savePolicy();
const latestRequest = server.requests[server.requests.length - 1];
Expand Down Expand Up @@ -918,13 +916,15 @@ describe('<EditPolicy />', () => {
});

describe('policy error notifications', () => {
let runTimers: () => void;
beforeAll(() => {
jest.useFakeTimers();
});

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

beforeEach(async () => {
httpRequestsMockHelpers.setLoadPolicies([getDefaultHotPhasePolicy('my_policy')]);
httpRequestsMockHelpers.setListNodes({
Expand All @@ -940,19 +940,9 @@ describe('<EditPolicy />', () => {

const { component } = testBed;
component.update();
});

// For new we rely on a setTimeout to ensure that error messages have time to populate
// the form object before we look at the form object. See:
// x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/form_errors_context.tsx
// for where this logic lives.
const runTimers = () => {
const { component } = testBed;
act(() => {
jest.runAllTimers();
});
component.update();
};
({ runTimers } = testBed);
});

test('shows phase error indicators correctly', async () => {
// This test simulates a user configuring a policy phase by phase. The flow is the following:
Expand Down
Loading

0 comments on commit f9fbf91

Please sign in to comment.