Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ILM] TS conversion of policies table #76006

Merged
merged 9 commits into from
Sep 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,62 @@
* you may not use this file except in compliance with the Elastic License.
*/
import moment from 'moment-timezone';
import React from 'react';
import { Provider } from 'react-redux';
// axios has a $http like interface so using it to simulate $http
import axios from 'axios';
import axiosXhrAdapter from 'axios/lib/adapters/xhr';
import sinon from 'sinon';
import React, { ReactElement } from 'react';
import { ReactWrapper } from 'enzyme';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { findTestSubject, takeMountedSnapshot } from '@elastic/eui/lib/test';

import { scopedHistoryMock } from '../../../../../src/core/public/mocks';
import { mountWithIntl } from '../../../../test_utils/enzyme_helpers';
import { fetchedPolicies } from '../../public/application/store/actions';
import { indexLifecycleManagementStore } from '../../public/application/store';
import { PolicyTable } from '../../public/application/sections/policy_table';
import {
fatalErrorsServiceMock,
injectedMetadataServiceMock,
scopedHistoryMock,
} from '../../../../../src/core/public/mocks';
import { HttpService } from '../../../../../src/core/public/http';
import { usageCollectionPluginMock } from '../../../../../src/plugins/usage_collection/public/mocks';

import { PolicyTable } from '../../public/application/sections/policy_table/policy_table';
import { init as initHttp } from '../../public/application/services/http';
import { init as initUiMetric } from '../../public/application/services/ui_metric';
import { PolicyFromES } from '../../public/application/services/policies/types';

initHttp(axios.create({ adapter: axiosXhrAdapter }), (path) => path);
initUiMetric({ reportUiStats: () => {} });

let server = null;
initHttp(
new HttpService().setup({
injectedMetadata: injectedMetadataServiceMock.createSetupContract(),
fatalErrors: fatalErrorsServiceMock.createSetupContract(),
})
);
initUiMetric(usageCollectionPluginMock.createSetupContract());

let store = null;
const policies = [];
const policies: PolicyFromES[] = [];
for (let i = 0; i < 105; i++) {
policies.push({
version: i,
modified_date: moment().subtract(i, 'days').valueOf(),
linkedIndices: i % 2 === 0 ? [`index${i}`] : null,
modified_date: moment().subtract(i, 'days').toISOString(),
linkedIndices: i % 2 === 0 ? [`index${i}`] : undefined,
name: `testy${i}`,
policy: {
name: `testy${i}`,
phases: {},
},
});
}
jest.mock('');
let component = null;
let component: ReactElement;

const snapshot = (rendered) => {
const snapshot = (rendered: string[]) => {
expect(rendered).toMatchSnapshot();
};
const mountedSnapshot = (rendered) => {
const mountedSnapshot = (rendered: ReactWrapper) => {
expect(takeMountedSnapshot(rendered)).toMatchSnapshot();
};
const names = (rendered) => {
const names = (rendered: ReactWrapper) => {
return findTestSubject(rendered, 'policyTablePolicyNameLink');
};
const namesText = (rendered) => {
return names(rendered).map((button) => button.text());
const namesText = (rendered: ReactWrapper): string[] => {
return (names(rendered) as ReactWrapper).map((button) => button.text());
};

const testSort = (headerName) => {
const testSort = (headerName: string) => {
const rendered = mountWithIntl(component);
const nameHeader = findTestSubject(rendered, `policyTableHeaderCell-${headerName}`).find(
'button'
Expand All @@ -63,7 +71,7 @@ const testSort = (headerName) => {
rendered.update();
snapshot(namesText(rendered));
};
const openContextMenu = (buttonIndex) => {
const openContextMenu = (buttonIndex: number) => {
const rendered = mountWithIntl(component);
const actionsButton = findTestSubject(rendered, 'policyActionsContextMenuButton');
actionsButton.at(buttonIndex).simulate('click');
Expand All @@ -73,33 +81,26 @@ const openContextMenu = (buttonIndex) => {

describe('policy table', () => {
beforeEach(() => {
store = indexLifecycleManagementStore();
component = (
<Provider store={store}>
<PolicyTable history={scopedHistoryMock.create()} navigateToApp={() => {}} />
</Provider>
<PolicyTable
policies={policies}
history={scopedHistoryMock.create()}
navigateToApp={jest.fn()}
updatePolicies={jest.fn()}
/>
);
store.dispatch(fetchedPolicies(policies));
server = sinon.fakeServer.create();
server.respondWith('/api/index_lifecycle_management/policies', [
200,
{ 'Content-Type': 'application/json' },
JSON.stringify(policies),
]);
});
test('should show spinner when policies are loading', () => {
store = indexLifecycleManagementStore();

test('should show empty state when there are not any policies', () => {
component = (
<Provider store={store}>
<PolicyTable history={scopedHistoryMock.create()} navigateToApp={() => {}} />
</Provider>
<PolicyTable
policies={[]}
history={scopedHistoryMock.create()}
navigateToApp={jest.fn()}
updatePolicies={jest.fn()}
/>
);
const rendered = mountWithIntl(component);
expect(rendered.find('.euiLoadingSpinner').exists()).toBeTruthy();
});
test('should show empty state when there are not any policies', () => {
store.dispatch(fetchedPolicies([]));
const rendered = mountWithIntl(component);
mountedSnapshot(rendered);
});
test('should change pages when a pagination link is clicked on', () => {
Expand All @@ -123,7 +124,7 @@ describe('policy table', () => {
test('should filter based on content of search input', () => {
const rendered = mountWithIntl(component);
const searchInput = rendered.find('.euiFieldSearch').first();
searchInput.instance().value = 'testy0';
((searchInput.instance() as unknown) as HTMLInputElement).value = 'testy0';
searchInput.simulate('keyup', { key: 'Enter', keyCode: 13, which: 13 });
rendered.update();
snapshot(namesText(rendered));
Expand All @@ -147,15 +148,15 @@ describe('policy table', () => {
expect(buttons.at(0).text()).toBe('View indices linked to policy');
expect(buttons.at(1).text()).toBe('Add policy to index template');
expect(buttons.at(2).text()).toBe('Delete policy');
expect(buttons.at(2).getDOMNode().disabled).toBeTruthy();
expect((buttons.at(2).getDOMNode() as HTMLButtonElement).disabled).toBeTruthy();
});
test('should have proper actions in context menu when there are not linked indices', () => {
const rendered = openContextMenu(1);
const buttons = rendered.find('button.euiContextMenuItem');
expect(buttons.length).toBe(2);
expect(buttons.at(0).text()).toBe('Add policy to index template');
expect(buttons.at(1).text()).toBe('Delete policy');
expect(buttons.at(1).getDOMNode().disabled).toBeFalsy();
expect((buttons.at(1).getDOMNode() as HTMLButtonElement).disabled).toBeFalsy();
});
test('confirmation modal should show when delete button is pressed', () => {
const rendered = openContextMenu(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@

import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { Provider } from 'react-redux';
import { I18nStart, ScopedHistory, ApplicationStart } from 'kibana/public';
import { UnmountCallback } from 'src/core/public';

import { App } from './app';
import { indexLifecycleManagementStore } from './store';

export const renderApp = (
element: Element,
Expand All @@ -22,9 +20,7 @@ export const renderApp = (
): UnmountCallback => {
render(
<I18nContext>
<Provider store={indexLifecycleManagementStore()}>
<App history={history} navigateToApp={navigateToApp} getUrlForApp={getUrlForApp} />
</Provider>
<App history={history} navigateToApp={navigateToApp} getUrlForApp={getUrlForApp} />
</I18nContext>,
element
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { EuiButton, EuiCallOut, EuiEmptyPrompt, EuiLoadingSpinner } from '@elastic/eui';
import { EuiButton, EuiEmptyPrompt, EuiLoadingSpinner } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { useLoadPoliciesList } from '../../services/api';

Expand Down Expand Up @@ -50,25 +50,29 @@ export const EditPolicy: React.FunctionComponent<Props & RouteComponentProps<Rou
if (error || !policies) {
const { statusCode, message } = error ? error : { statusCode: '', message: '' };
return (
<EuiCallOut
<EuiEmptyPrompt
title={
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.lifecyclePoliciesLoadingFailedTitle"
defaultMessage="Unable to load existing lifecycle policies"
/>
<h2>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.lifecyclePoliciesLoadingFailedTitle"
defaultMessage="Unable to load existing lifecycle policies"
/>
</h2>
}
color="danger"
>
<p>
{message} ({statusCode})
</p>
<EuiButton onClick={sendRequest} iconType="refresh" color="danger">
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.lifecyclePoliciesReloadButton"
defaultMessage="Try again"
/>
</EuiButton>
</EuiCallOut>
body={
<p>
{message} ({statusCode})
</p>
}
actions={
<EuiButton onClick={sendRequest} iconType="refresh" color="danger">
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.lifecyclePoliciesReloadButton"
defaultMessage="Try again"
/>
</EuiButton>
}
/>
);
}

Expand Down
Loading