Skip to content
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
Expand Up @@ -5,10 +5,11 @@
* 2.0.
*/

import sinon, { SinonFakeServer } from 'sinon';
import { httpServiceMock } from '../../../../../../src/core/public/mocks';
import { API_BASE_PATH } from '../../../common/constants';

type HttpResponse = Record<string, any> | any[];
type HttpMethod = 'GET' | 'PUT' | 'DELETE' | 'POST';

export interface ResponseError {
statusCode: number;
Expand All @@ -17,139 +18,105 @@ export interface ResponseError {
}

// Register helpers to mock HTTP Requests
const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
const setLoadTemplatesResponse = (response: HttpResponse = []) => {
server.respondWith('GET', `${API_BASE_PATH}/index_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 setReloadIndicesResponse = (response: HttpResponse = []) => {
server.respondWith('POST', `${API_BASE_PATH}/indices/reload`, [
200,
{ 'Content-Type': 'application/json' },
JSON.stringify(response),
]);
};

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

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

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

const setDeleteTemplateResponse = (response: HttpResponse = []) => {
server.respondWith('POST', `${API_BASE_PATH}/delete_index_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}/index_templates/:id`, [
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With sinon fake server we could use wildcards for url params, that is no longer allowed with HttpSetup mock. So we need to directly specify the full url when mocking api endpoints. For this particular case would look something like:

  const setLoadTemplateResponse = (
    templateId: string,
    response?: HttpResponse,
    error?: ResponseError
  ) => mockResponse('GET', `${API_BASE_PATH}/index_templates/${templateId}`, response, error);

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('POST', `${API_BASE_PATH}/index_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}/index_templates/:name`, [
status,
{ 'Content-Type': 'application/json' },
body,
]);
};

const setUpdateIndexSettingsResponse = (response?: HttpResponse, error?: ResponseError) => {
const status = error ? error.statusCode || 400 : 200;
const body = error ?? response;

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

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

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

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

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

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

server.respondWith('GET', `${API_BASE_PATH}/nodes/plugins`, [
status,
{ 'Content-Type': 'application/json' },
JSON.stringify(body),
]);
};
const registerHttpRequestMockHelpers = (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting here to L56 seems to be the same code across the test refactoring PRs. Does it make sense to try to centralize it somewhere so it can be reused?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you agree, I think this can be done as a separate PR

Copy link
Copy Markdown
Member Author

@sabarasaba sabarasaba Mar 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've suggested also to put this somewhere in kbn-test (see this thread), but we ended up agreeing on keeping separate implementations per app for now. I'll create a new issue after on our side to update this later on after I finish updating all the tests.

httpSetup: ReturnType<typeof httpServiceMock.createStartContract>
) => {
const mockResponses = new Map<HttpMethod, Map<string, Promise<unknown>>>(
['GET', 'PUT', 'DELETE', 'POST'].map(
(method) => [method, new Map()] as [HttpMethod, Map<string, Promise<unknown>>]
)
);

const mockMethodImplementation = (method: HttpMethod, path: string) => {
return mockResponses.get(method)?.get(path) ?? Promise.resolve({});
};

httpSetup.get.mockImplementation((path) =>
mockMethodImplementation('GET', path as unknown as string)
);
httpSetup.delete.mockImplementation((path) =>
mockMethodImplementation('DELETE', path as unknown as string)
);
httpSetup.post.mockImplementation((path) =>
mockMethodImplementation('POST', path as unknown as string)
);
httpSetup.put.mockImplementation((path) =>
mockMethodImplementation('PUT', path as unknown as string)
);

const mockResponse = (method: HttpMethod, path: string, response?: unknown, error?: unknown) => {
const defuse = (promise: Promise<unknown>) => {
promise.catch(() => {});
return promise;
};

return mockResponses
.get(method)!
.set(path, error ? defuse(Promise.reject({ body: error })) : Promise.resolve(response));
};

const setLoadTemplatesResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('GET', `${API_BASE_PATH}/index_templates`, response, error);

const setLoadIndicesResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('GET', `${API_BASE_PATH}/indices`, response, error);

const setReloadIndicesResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('POST', `${API_BASE_PATH}/indices/reload`, response, error);

const setLoadDataStreamsResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('GET', `${API_BASE_PATH}/data_streams`, response, error);

const setLoadDataStreamResponse = (
dataStreamId: string,
response?: HttpResponse,
error?: ResponseError
) =>
mockResponse(
'GET',
`${API_BASE_PATH}/data_streams/${encodeURIComponent(dataStreamId)}`,
response,
error
);

const setDeleteDataStreamResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('POST', `${API_BASE_PATH}/delete_data_streams`, response, error);

const setDeleteTemplateResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('POST', `${API_BASE_PATH}/delete_index_templates`, response, error);

const setLoadTemplateResponse = (
templateId: string,
response?: HttpResponse,
error?: ResponseError
) => mockResponse('GET', `${API_BASE_PATH}/index_templates/${templateId}`, response, error);

const setCreateTemplateResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('POST', `${API_BASE_PATH}/index_templates`, response, error);

const setUpdateTemplateResponse = (
templateId: string,
response?: HttpResponse,
error?: ResponseError
) => mockResponse('PUT', `${API_BASE_PATH}/index_templates/${templateId}`, response, error);

const setUpdateIndexSettingsResponse = (
indexName: string,
response?: HttpResponse,
error?: ResponseError
) => mockResponse('PUT', `${API_BASE_PATH}/settings/${indexName}`, response, error);

const setSimulateTemplateResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('POST', `${API_BASE_PATH}/index_templates/simulate`, response, error);

const setLoadComponentTemplatesResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('GET', `${API_BASE_PATH}/component_templates`, response, error);

const setLoadNodesPluginsResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('GET', `${API_BASE_PATH}/nodes/plugins`, response, error);

const setLoadTelemetryResponse = (response?: HttpResponse, error?: ResponseError) =>
mockResponse('GET', '/api/ui_counters/_report', response, error);

return {
setLoadTemplatesResponse,
Expand All @@ -166,22 +133,16 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
setSimulateTemplateResponse,
setLoadComponentTemplatesResponse,
setLoadNodesPluginsResponse,
setLoadTelemetryResponse,
};
};

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);
const httpSetup = httpServiceMock.createSetupContract();
const httpRequestsMockHelpers = registerHttpRequestMockHelpers(httpSetup);

return {
server,
httpSetup,
httpRequestsMockHelpers,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
*/

import React from 'react';
import axios from 'axios';
import axiosXhrAdapter from 'axios/lib/adapters/xhr';
import { merge } from 'lodash';
import SemVer from 'semver/classes/semver';

import { HttpSetup } from 'src/core/public';
import {
notificationServiceMock,
docLinksServiceMock,
Expand All @@ -36,7 +35,6 @@ import {
import { componentTemplatesMockDependencies } from '../../../public/application/components/component_templates/__jest__';
import { init as initHttpRequests } from './http_requests';

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

export const services = {
Expand Down Expand Up @@ -64,30 +62,24 @@ const { Provider: KibanaReactContextProvider } = createKibanaReactContext({
});

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,
};
return initHttpRequests();
};

export const WithAppDependencies =
(Comp: any, overridingDependencies: any = {}) =>
(Comp: any, httpSetup: HttpSetup, overridingDependencies: any = {}) =>
(props: any) => {
httpService.setup(httpSetup);
const mergedDependencies = merge({}, appDependencies, overridingDependencies);

return (
<KibanaReactContextProvider>
<AppContextProvider value={mergedDependencies}>
<MappingsEditorProvider>
<ComponentTemplatesProvider value={componentTemplatesMockDependencies}>
<ComponentTemplatesProvider value={componentTemplatesMockDependencies(httpSetup)}>
<GlobalFlyoutProvider>
<Comp {...props} />
</GlobalFlyoutProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
AsyncTestBedConfig,
findTestSubject,
} from '@kbn/test-jest-helpers';
import { HttpSetup } from 'src/core/public';
import { DataStream } from '../../../common';
import { IndexManagementHome } from '../../../public/application/sections/home';
import { indexManagementStore } from '../../../public/application/store';
Expand Down Expand Up @@ -46,7 +47,10 @@ export interface DataStreamsTabTestBed extends TestBed<TestSubjects> {
findDetailPanelIndexTemplateLink: () => ReactWrapper;
}

export const setup = async (overridingDependencies: any = {}): Promise<DataStreamsTabTestBed> => {
export const setup = async (
httpSetup: HttpSetup,
overridingDependencies: any = {}
): Promise<DataStreamsTabTestBed> => {
const testBedConfig: AsyncTestBedConfig = {
store: () => indexManagementStore(services as any),
memoryRouter: {
Expand All @@ -57,7 +61,7 @@ export const setup = async (overridingDependencies: any = {}): Promise<DataStrea
};

const initTestBed = registerTestBed(
WithAppDependencies(IndexManagementHome, overridingDependencies),
WithAppDependencies(IndexManagementHome, httpSetup, overridingDependencies),
testBedConfig
);
const testBed = await initTestBed();
Expand Down
Loading