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

[7.x] [Index management] Re-enable index template tests (#60780) #60891

Merged
merged 2 commits into from
Mar 23, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export const TEMPLATE_NAME = 'my_template';

export const INDEX_PATTERNS = ['my_index_pattern'];

export const SETTINGS = {
number_of_shards: 1,
index: {
lifecycle: {
name: 'my_policy',
},
},
};

export const ALIASES = {
alias: {
filter: {
term: { user: 'my_user' },
},
},
};

export const MAPPINGS = {
_source: {},
_meta: {},
properties: {},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { ReactWrapper } from 'enzyme';
import { act } from 'react-dom/test-utils';
import {
registerTestBed,
TestBed,
TestBedConfig,
findTestSubject,
nextTick,
} from '../../../../../test_utils';
import { IndexManagementHome } from '../../../public/application/sections/home'; // eslint-disable-line @kbn/eslint/no-restricted-paths
import { BASE_PATH } from '../../../common/constants';
import { indexManagementStore } from '../../../public/application/store'; // eslint-disable-line @kbn/eslint/no-restricted-paths
import { Template } from '../../../common/types';
import { WithAppDependencies, services } from './setup_environment';

const testBedConfig: TestBedConfig = {
store: () => indexManagementStore(services as any),
memoryRouter: {
initialEntries: [`${BASE_PATH}indices`],
componentRoutePath: `${BASE_PATH}:section(indices|templates)`,
},
doMountAsync: true,
};

const initTestBed = registerTestBed(WithAppDependencies(IndexManagementHome), testBedConfig);

export interface IdxMgmtHomeTestBed extends TestBed<IdxMgmtTestSubjects> {
findAction: (action: 'edit' | 'clone' | 'delete') => ReactWrapper;
actions: {
selectHomeTab: (tab: 'indicesTab' | 'templatesTab') => void;
selectDetailsTab: (tab: 'summary' | 'settings' | 'mappings' | 'aliases') => void;
clickReloadButton: () => void;
clickTemplateAction: (name: Template['name'], action: 'edit' | 'clone' | 'delete') => void;
clickTemplateAt: (index: number) => void;
clickCloseDetailsButton: () => void;
clickActionMenu: (name: Template['name']) => void;
};
}

export const setup = async (): Promise<IdxMgmtHomeTestBed> => {
const testBed = await initTestBed();

/**
* Additional helpers
*/
const findAction = (action: 'edit' | 'clone' | 'delete') => {
const actions = ['edit', 'clone', 'delete'];
const { component } = testBed;

return component.find('.euiContextMenuItem').at(actions.indexOf(action));
};

/**
* User Actions
*/

const selectHomeTab = (tab: 'indicesTab' | 'templatesTab') => {
testBed.find(tab).simulate('click');
};

const selectDetailsTab = (tab: 'summary' | 'settings' | 'mappings' | 'aliases') => {
const tabs = ['summary', 'settings', 'mappings', 'aliases'];

testBed
.find('templateDetails.tab')
.at(tabs.indexOf(tab))
.simulate('click');
};

const clickReloadButton = () => {
const { find } = testBed;
find('reloadButton').simulate('click');
};

const clickActionMenu = async (templateName: Template['name']) => {
const { component } = testBed;

// When a table has > 2 actions, EUI displays an overflow menu with an id "<template_name>-actions"
// The template name may contain a period (.) so we use bracket syntax for selector
component.find(`div[id="${templateName}-actions"] button`).simulate('click');
};

const clickTemplateAction = (
templateName: Template['name'],
action: 'edit' | 'clone' | 'delete'
) => {
const actions = ['edit', 'clone', 'delete'];
const { component } = testBed;

clickActionMenu(templateName);

component
.find('.euiContextMenuItem')
.at(actions.indexOf(action))
.simulate('click');
};

const clickTemplateAt = async (index: number) => {
const { component, table, router } = testBed;
const { rows } = table.getMetaData('templateTable');
const templateLink = findTestSubject(rows[index].reactWrapper, 'templateDetailsLink');

await act(async () => {
const { href } = templateLink.props();
router.navigateTo(href!);
await nextTick();
component.update();
});
};

const clickCloseDetailsButton = () => {
const { find } = testBed;

find('closeDetailsButton').simulate('click');
};

return {
...testBed,
findAction,
actions: {
selectHomeTab,
selectDetailsTab,
clickReloadButton,
clickTemplateAction,
clickTemplateAt,
clickCloseDetailsButton,
clickActionMenu,
},
};
};

type IdxMgmtTestSubjects = TestSubjects;

export type TestSubjects =
| 'aliasesTab'
| 'appTitle'
| 'cell'
| 'closeDetailsButton'
| 'createTemplateButton'
| 'deleteSystemTemplateCallOut'
| 'deleteTemplateButton'
| 'deleteTemplatesConfirmation'
| 'documentationLink'
| 'emptyPrompt'
| 'manageTemplateButton'
| 'mappingsTab'
| 'noAliasesCallout'
| 'noMappingsCallout'
| 'noSettingsCallout'
| 'indicesList'
| 'indicesTab'
| 'reloadButton'
| 'row'
| 'sectionError'
| 'sectionLoading'
| 'settingsTab'
| 'summaryTab'
| 'summaryTitle'
| 'systemTemplatesSwitch'
| 'templateDetails'
| 'templateDetails.manageTemplateButton'
| 'templateDetails.sectionLoading'
| 'templateDetails.tab'
| 'templateDetails.title'
| 'templateList'
| 'templateTable'
| 'templatesTab';
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import sinon, { SinonFakeServer } from 'sinon';
import { API_BASE_PATH } from '../../../common/constants';

type HttpResponse = Record<string, any> | any[];

// Register helpers to mock HTTP Requests
const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
const setLoadTemplatesResponse = (response: HttpResponse = []) => {
server.respondWith('GET', `${API_BASE_PATH}/templates`, [
200,
{ 'Content-Type': 'application/json' },
JSON.stringify(response),
]);
};

const setLoadIndicesResponse = (response: HttpResponse = []) => {
server.respondWith('GET', `${API_BASE_PATH}/indices`, [
200,
{ 'Content-Type': 'application/json' },
JSON.stringify(response),
]);
};

const setDeleteTemplateResponse = (response: HttpResponse = []) => {
server.respondWith('DELETE', `${API_BASE_PATH}/templates`, [
200,
{ 'Content-Type': 'application/json' },
JSON.stringify(response),
]);
};

const setLoadTemplateResponse = (response?: HttpResponse, error?: any) => {
const status = error ? error.status || 400 : 200;
const body = error ? error.body : response;

server.respondWith('GET', `${API_BASE_PATH}/templates/:id`, [
status,
{ 'Content-Type': 'application/json' },
JSON.stringify(body),
]);
};

const setCreateTemplateResponse = (response?: HttpResponse, error?: any) => {
const status = error ? error.body.status || 400 : 200;
const body = error ? JSON.stringify(error.body) : JSON.stringify(response);

server.respondWith('PUT', `${API_BASE_PATH}/templates`, [
status,
{ 'Content-Type': 'application/json' },
body,
]);
};

const setUpdateTemplateResponse = (response?: HttpResponse, error?: any) => {
const status = error ? error.status || 400 : 200;
const body = error ? JSON.stringify(error.body) : JSON.stringify(response);

server.respondWith('PUT', `${API_BASE_PATH}/templates/:name`, [
status,
{ 'Content-Type': 'application/json' },
body,
]);
};

return {
setLoadTemplatesResponse,
setLoadIndicesResponse,
setDeleteTemplateResponse,
setLoadTemplateResponse,
setCreateTemplateResponse,
setUpdateTemplateResponse,
};
};

export const init = () => {
const server = sinon.fakeServer.create();
server.respondImmediately = true;

// Define default response for unhandled requests.
// We make requests to APIs which don't impact the component under test, e.g. UI metric telemetry,
// and we can mock them all with a 200 instead of mocking each one individually.
server.respondWith([200, {}, 'DefaultSinonMockServerResponse']);

const httpRequestsMockHelpers = registerHttpRequestMockHelpers(server);

return {
server,
httpRequestsMockHelpers,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { setup as homeSetup } from './home.helpers';
import { setup as templateCreateSetup } from './template_create.helpers';
import { setup as templateCloneSetup } from './template_clone.helpers';
import { setup as templateEditSetup } from './template_edit.helpers';

export { nextTick, getRandomString, findTestSubject, TestBed } from '../../../../../test_utils';

export { setupEnvironment } from './setup_environment';

export const pageHelpers = {
home: { setup: homeSetup },
templateCreate: { setup: templateCreateSetup },
templateClone: { setup: templateCloneSetup },
templateEdit: { setup: templateEditSetup },
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
/* eslint-disable @kbn/eslint/no-restricted-paths */
import React from 'react';
import axios from 'axios';
import axiosXhrAdapter from 'axios/lib/adapters/xhr';

import {
notificationServiceMock,
docLinksServiceMock,
} from '../../../../../../src/core/public/mocks';
import { AppContextProvider } from '../../../public/application/app_context';
import { httpService } from '../../../public/application/services/http';
import { breadcrumbService } from '../../../public/application/services/breadcrumbs';
import { documentationService } from '../../../public/application/services/documentation';
import { notificationService } from '../../../public/application/services/notification';
import { ExtensionsService } from '../../../public/services';
import { UiMetricService } from '../../../public/application/services/ui_metric';
import { setUiMetricService } from '../../../public/application/services/api';
import { setExtensionsService } from '../../../public/application/store/selectors';
import { init as initHttpRequests } from './http_requests';

const mockHttpClient = axios.create({ adapter: axiosXhrAdapter });

export const services = {
extensionsService: new ExtensionsService(),
uiMetricService: new UiMetricService('index_management'),
};
services.uiMetricService.setup({ reportUiStats() {} } as any);
setExtensionsService(services.extensionsService);
setUiMetricService(services.uiMetricService);
const appDependencies = { services, core: {}, plugins: {} } as any;

export const setupEnvironment = () => {
// Mock initialization of services
// @ts-ignore
httpService.setup(mockHttpClient);
breadcrumbService.setup(() => undefined);
documentationService.setup(docLinksServiceMock.createStartContract());
notificationService.setup(notificationServiceMock.createSetupContract());

const { server, httpRequestsMockHelpers } = initHttpRequests();

return {
server,
httpRequestsMockHelpers,
};
};

export const WithAppDependencies = (Comp: any) => (props: any) => (
<AppContextProvider value={appDependencies}>
<Comp {...props} />
</AppContextProvider>
);
Loading