mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
[Enterprise Search] Switch to React Testing Library for Enterprise Search (#170514)
## Summary Added a test helper to create required providers. This way now we can mount all of our logics and test the changes. It also does a lot of heavy lifting and simplifies tests. Full mocking should still be available with previous helpers and a bit of working around. ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
cd490eeabe
commit
4e065dac28
9 changed files with 265 additions and 39 deletions
|
@ -8,8 +8,8 @@
|
|||
import { httpServiceMock } from '@kbn/core/public/mocks';
|
||||
|
||||
export const mockHttpValues = {
|
||||
http: httpServiceMock.createSetupContract(),
|
||||
errorConnectingMessage: '',
|
||||
http: httpServiceMock.createSetupContract(),
|
||||
readOnlyMode: false,
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { mockHttpValues } from '../../../__mocks__/kea_logic';
|
||||
|
||||
import { nextTick } from '@kbn/test-jest-helpers';
|
||||
|
||||
import { startAccessControlSync } from './start_access_control_sync_api_logic';
|
||||
|
||||
describe('startAccessControlSyncApiLogic', () => {
|
||||
describe('startAccessControlSync', () => {
|
||||
const { http } = mockHttpValues;
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('calls correct api', async () => {
|
||||
const promise = Promise.resolve('result');
|
||||
http.post.mockReturnValue(promise);
|
||||
const connectorId = 'test-connector-id-123';
|
||||
|
||||
const result = startAccessControlSync({ connectorId });
|
||||
await nextTick();
|
||||
expect(http.post).toHaveBeenCalledWith(
|
||||
`/internal/enterprise_search/connectors/${connectorId}/start_access_control_sync`
|
||||
);
|
||||
await expect(result).resolves.toEqual('result');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { screen } from '@testing-library/react';
|
||||
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
import { HttpSetupMock } from '@kbn/core-http-browser-mocks';
|
||||
|
||||
import { HttpLogic } from '../../../shared/http';
|
||||
import { TestHelper } from '../../../test_helpers/test_utils.test_helper';
|
||||
import { FetchDefaultPipelineApiLogic } from '../../api/connector/get_default_pipeline_api_logic';
|
||||
|
||||
import { Settings } from './settings';
|
||||
|
||||
test('displays Settings Save-Reset buttons disabled by default', async () => {
|
||||
TestHelper.prepare();
|
||||
TestHelper.mountLogic(FetchDefaultPipelineApiLogic);
|
||||
TestHelper.appendCallback(() => {
|
||||
const http = HttpLogic.values.http as HttpSetupMock;
|
||||
http.get.mockImplementationOnce(() =>
|
||||
Promise.resolve({
|
||||
extract_binary_content: true,
|
||||
name: 'test',
|
||||
reduce_whitespace: true,
|
||||
run_ml_inference: true,
|
||||
})
|
||||
);
|
||||
});
|
||||
TestHelper.render(<Settings />);
|
||||
|
||||
const saveButton = screen.getByTestId('entSearchContentSettingsSaveButton');
|
||||
const resetButton = screen.getByTestId('entSearchContentSettingsResetButton');
|
||||
|
||||
expect(saveButton).toHaveTextContent('Save');
|
||||
expect(saveButton).toBeDisabled();
|
||||
expect(resetButton).toHaveTextContent('Reset');
|
||||
expect(resetButton).toBeDisabled();
|
||||
});
|
|
@ -23,7 +23,6 @@ import { SettingsPanel } from './settings_panel';
|
|||
export const Settings: React.FC = () => {
|
||||
const { makeRequest, setPipeline } = useActions(SettingsLogic);
|
||||
const { defaultPipeline, hasNoChanges, isLoading, pipelineState } = useValues(SettingsLogic);
|
||||
|
||||
const {
|
||||
extract_binary_content: extractBinaryContent,
|
||||
reduce_whitespace: reduceWhitespace,
|
||||
|
@ -62,6 +61,7 @@ export const Settings: React.FC = () => {
|
|||
disabled={hasNoChanges}
|
||||
isLoading={isLoading}
|
||||
onClick={() => makeRequest(pipelineState)}
|
||||
data-test-subj={'entSearchContentSettingsSaveButton'}
|
||||
>
|
||||
{i18n.translate('xpack.enterpriseSearch.content.settings.saveButtonLabel', {
|
||||
defaultMessage: 'Save',
|
||||
|
@ -71,6 +71,7 @@ export const Settings: React.FC = () => {
|
|||
disabled={hasNoChanges}
|
||||
isLoading={isLoading}
|
||||
onClick={() => setPipeline(defaultPipeline)}
|
||||
data-test-subj={'entSearchContentSettingsResetButton'}
|
||||
>
|
||||
{i18n.translate('xpack.enterpriseSearch.content.settings.resetButtonLabel', {
|
||||
defaultMessage: 'Reset',
|
||||
|
|
|
@ -7,14 +7,14 @@
|
|||
|
||||
import { kea, MakeLogicType } from 'kea';
|
||||
|
||||
import { HttpSetup, HttpInterceptorResponseError, HttpResponse } from '@kbn/core/public';
|
||||
import { HttpInterceptorResponseError, HttpResponse, HttpSetup } from '@kbn/core/public';
|
||||
|
||||
import { ERROR_CONNECTING_HEADER, READ_ONLY_MODE_HEADER } from '../../../../common/constants';
|
||||
|
||||
export interface HttpValues {
|
||||
errorConnectingMessage: string;
|
||||
http: HttpSetup;
|
||||
httpInterceptors: Function[];
|
||||
errorConnectingMessage: string;
|
||||
readOnlyMode: boolean;
|
||||
}
|
||||
|
||||
|
@ -26,33 +26,21 @@ interface HttpActions {
|
|||
}
|
||||
|
||||
export const HttpLogic = kea<MakeLogicType<HttpValues, HttpActions>>({
|
||||
path: ['enterprise_search', 'http_logic'],
|
||||
actions: {
|
||||
initializeHttpInterceptors: () => null,
|
||||
onConnectionError: (errorConnectingMessage) => ({ errorConnectingMessage }),
|
||||
setHttpInterceptors: (httpInterceptors) => ({ httpInterceptors }),
|
||||
setReadOnlyMode: (readOnlyMode) => ({ readOnlyMode }),
|
||||
},
|
||||
reducers: ({ props }) => ({
|
||||
http: [props.http, {}],
|
||||
httpInterceptors: [
|
||||
[],
|
||||
{
|
||||
setHttpInterceptors: (_, { httpInterceptors }) => httpInterceptors,
|
||||
},
|
||||
],
|
||||
errorConnectingMessage: [
|
||||
props.errorConnectingMessage || '',
|
||||
{
|
||||
onConnectionError: (_, { errorConnectingMessage }) => errorConnectingMessage,
|
||||
},
|
||||
],
|
||||
readOnlyMode: [
|
||||
props.readOnlyMode || false,
|
||||
{
|
||||
setReadOnlyMode: (_, { readOnlyMode }) => readOnlyMode,
|
||||
},
|
||||
],
|
||||
events: ({ values, actions }) => ({
|
||||
afterMount: () => {
|
||||
actions.initializeHttpInterceptors();
|
||||
},
|
||||
beforeUnmount: () => {
|
||||
values.httpInterceptors.forEach((removeInterceptorFn?: Function) => {
|
||||
if (removeInterceptorFn) removeInterceptorFn();
|
||||
});
|
||||
},
|
||||
}),
|
||||
listeners: ({ values, actions }) => ({
|
||||
initializeHttpInterceptors: () => {
|
||||
|
@ -94,15 +82,27 @@ export const HttpLogic = kea<MakeLogicType<HttpValues, HttpActions>>({
|
|||
actions.setHttpInterceptors(httpInterceptors);
|
||||
},
|
||||
}),
|
||||
events: ({ values, actions }) => ({
|
||||
afterMount: () => {
|
||||
actions.initializeHttpInterceptors();
|
||||
},
|
||||
beforeUnmount: () => {
|
||||
values.httpInterceptors.forEach((removeInterceptorFn?: Function) => {
|
||||
if (removeInterceptorFn) removeInterceptorFn();
|
||||
});
|
||||
},
|
||||
path: ['enterprise_search', 'http_logic'],
|
||||
reducers: ({ props }) => ({
|
||||
errorConnectingMessage: [
|
||||
props.errorConnectingMessage || '',
|
||||
{
|
||||
onConnectionError: (_, { errorConnectingMessage }) => errorConnectingMessage,
|
||||
},
|
||||
],
|
||||
http: [props.http, {}],
|
||||
httpInterceptors: [
|
||||
[],
|
||||
{
|
||||
setHttpInterceptors: (_, { httpInterceptors }) => httpInterceptors,
|
||||
},
|
||||
],
|
||||
readOnlyMode: [
|
||||
props.readOnlyMode || false,
|
||||
{
|
||||
setReadOnlyMode: (_, { readOnlyMode }) => readOnlyMode,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
|
@ -110,14 +110,14 @@ export const HttpLogic = kea<MakeLogicType<HttpValues, HttpActions>>({
|
|||
* Mount/props helper
|
||||
*/
|
||||
interface HttpLogicProps {
|
||||
http: HttpSetup;
|
||||
errorConnectingMessage?: string;
|
||||
http: HttpSetup;
|
||||
readOnlyMode?: boolean;
|
||||
}
|
||||
|
||||
export const mountHttpLogic = (props: HttpLogicProps) => {
|
||||
HttpLogic(props);
|
||||
const unmount = HttpLogic.mount();
|
||||
return unmount;
|
||||
return HttpLogic.mount();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
*/
|
||||
|
||||
export { KibanaLogic, mountKibanaLogic } from './kibana_logic';
|
||||
export type { KibanaLogicProps } from './kibana_logic';
|
||||
|
|
|
@ -33,7 +33,7 @@ import { createHref, CreateHrefOptions } from '../react_router_helpers';
|
|||
type RequiredFieldsOnly<T> = {
|
||||
[K in keyof T as T[K] extends Required<T>[K] ? K : never]: T[K];
|
||||
};
|
||||
interface KibanaLogicProps {
|
||||
export interface KibanaLogicProps {
|
||||
application: ApplicationStart;
|
||||
capabilities: Capabilities;
|
||||
charts: ChartsPluginStart;
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { mockHistory } from '../__mocks__/react_router';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { render as testingLibraryRender } from '@testing-library/react';
|
||||
|
||||
import { LogicWrapper, Provider, resetContext } from 'kea';
|
||||
|
||||
import { chartPluginMock } from '@kbn/charts-plugin/public/mocks';
|
||||
import { cloudMock } from '@kbn/cloud-plugin/public/mocks';
|
||||
import { ApplicationStart } from '@kbn/core-application-browser';
|
||||
import { Capabilities } from '@kbn/core-capabilities-common';
|
||||
import { httpServiceMock } from '@kbn/core-http-browser-mocks';
|
||||
import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks';
|
||||
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { LensPublicStart } from '@kbn/lens-plugin/public';
|
||||
import { mlPluginMock } from '@kbn/ml-plugin/public/mocks';
|
||||
import { securityMock } from '@kbn/security-plugin/public/mocks';
|
||||
import { sharePluginMock } from '@kbn/share-plugin/public/mocks';
|
||||
|
||||
import { mountHttpLogic } from '../shared/http';
|
||||
import { mountKibanaLogic, KibanaLogicProps } from '../shared/kibana';
|
||||
|
||||
export const mockKibanaProps: KibanaLogicProps = {
|
||||
application: {
|
||||
getUrlForApp: jest.fn(
|
||||
(appId: string, options?: { path?: string }) => `/app/${appId}${options?.path}`
|
||||
),
|
||||
} as unknown as ApplicationStart,
|
||||
capabilities: {} as Capabilities,
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
cloud: {
|
||||
...cloudMock.createSetup(),
|
||||
isCloudEnabled: false,
|
||||
},
|
||||
config: {
|
||||
canDeployEntSearch: true,
|
||||
host: 'http://localhost:3002',
|
||||
ui: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
data: dataPluginMock.createStartContract(),
|
||||
guidedOnboarding: {},
|
||||
history: mockHistory,
|
||||
isSidebarEnabled: true,
|
||||
lens: {
|
||||
EmbeddableComponent: jest.fn(),
|
||||
stateHelperApi: jest.fn().mockResolvedValue({
|
||||
formula: jest.fn(),
|
||||
}),
|
||||
} as unknown as LensPublicStart,
|
||||
ml: mlPluginMock.createStartContract(),
|
||||
navigateToUrl: jest.fn(),
|
||||
productAccess: {
|
||||
hasAppSearchAccess: true,
|
||||
hasWorkplaceSearchAccess: true,
|
||||
},
|
||||
productFeatures: {
|
||||
hasConnectors: true,
|
||||
hasDefaultIngestPipeline: true,
|
||||
hasDocumentLevelSecurityEnabled: true,
|
||||
hasIncrementalSyncEnabled: true,
|
||||
hasNativeConnectors: true,
|
||||
hasWebCrawler: true,
|
||||
},
|
||||
renderHeaderActions: jest.fn(),
|
||||
security: securityMock.createStart(),
|
||||
setBreadcrumbs: jest.fn(),
|
||||
setChromeIsVisible: jest.fn(),
|
||||
setDocTitle: jest.fn(),
|
||||
share: sharePluginMock.createStartContract(),
|
||||
uiSettings: uiSettingsServiceMock.createStartContract(),
|
||||
};
|
||||
|
||||
type LogicFile = LogicWrapper<any>;
|
||||
const DEFAULT_VALUES = {
|
||||
httpLogicValues: {
|
||||
http: httpServiceMock.createSetupContract(),
|
||||
},
|
||||
kibanaLogicValues: mockKibanaProps,
|
||||
};
|
||||
|
||||
interface PrepareOptions {
|
||||
mockValues: typeof DEFAULT_VALUES;
|
||||
noDefaultActions: boolean;
|
||||
}
|
||||
|
||||
interface TestHelper {
|
||||
actionsToRun: Array<() => void>;
|
||||
appendCallback: (callback: () => void) => void;
|
||||
defaultActions: () => TestHelper['actionsToRun'];
|
||||
defaultMockValues: typeof DEFAULT_VALUES;
|
||||
mountLogic: (logicFile: LogicFile, props?: object) => void;
|
||||
prepare: (options?: PrepareOptions) => void;
|
||||
render: (children: JSX.Element) => void;
|
||||
}
|
||||
|
||||
export const TestHelper: TestHelper = {
|
||||
actionsToRun: [],
|
||||
appendCallback: (callback) => {
|
||||
TestHelper.actionsToRun.push(callback);
|
||||
},
|
||||
defaultActions: () => {
|
||||
return [
|
||||
() => {
|
||||
resetContext();
|
||||
},
|
||||
() => {
|
||||
mountHttpLogic(TestHelper.defaultMockValues.httpLogicValues);
|
||||
mountKibanaLogic(TestHelper.defaultMockValues.kibanaLogicValues);
|
||||
},
|
||||
];
|
||||
},
|
||||
defaultMockValues: DEFAULT_VALUES,
|
||||
mountLogic: (logicFile, props?) => {
|
||||
TestHelper.actionsToRun.push(() => logicFile.call(logicFile, props || undefined));
|
||||
},
|
||||
prepare: (options?) => {
|
||||
TestHelper.defaultMockValues = { ...DEFAULT_VALUES, ...(options?.mockValues || {}) };
|
||||
if (!options || !options.noDefaultActions) {
|
||||
TestHelper.actionsToRun = TestHelper.defaultActions();
|
||||
}
|
||||
},
|
||||
render: (children) => {
|
||||
TestHelper.actionsToRun.forEach((action) => {
|
||||
action();
|
||||
});
|
||||
testingLibraryRender(
|
||||
<I18nProvider>
|
||||
<Provider>{children}</Provider>
|
||||
</I18nProvider>
|
||||
);
|
||||
},
|
||||
};
|
|
@ -60,6 +60,9 @@
|
|||
"@kbn/share-plugin",
|
||||
"@kbn/search-api-panels",
|
||||
"@kbn/search-connectors",
|
||||
"@kbn/logs-shared-plugin"
|
||||
"@kbn/logs-shared-plugin",
|
||||
"@kbn/core-http-browser-mocks",
|
||||
"@kbn/core-application-browser",
|
||||
"@kbn/core-capabilities-common"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue