Skip to content

Commit

Permalink
[7.x] [Index management] Re-enable index template tests (#60780) (#60891
Browse files Browse the repository at this point in the history
)
  • Loading branch information
sebelga authored Mar 23, 2020
1 parent ef1f00d commit 4da7cd1
Show file tree
Hide file tree
Showing 13 changed files with 1,923 additions and 0 deletions.
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

0 comments on commit 4da7cd1

Please sign in to comment.