Skip to content

Commit

Permalink
[Enterprise Search] Mocks/tests tech debt - avoid hungry mocking (#10…
Browse files Browse the repository at this point in the history
…1107)

* Move enzyme & misc test helpers out of __mocks__/ and into new test_helpers/

They're not technically mocks since nothing is being mocked, so we should move them into a test_helpers folder for specificity & organization

* Move React Router mocks into its own separate folder/import

This was part of the initial feedback, that it was unclear why importing something for Kea in __mocks__/index.ts was mocking react router along for the ride. Separating this out makes things clearer and imports more explicit

+ add some handy new mock useX jest.fn()s helpers, so we're not doing `useParams() as jest.Mock` errywhere

* Move Kea & logic mocks/helpers into kea_logic subfolder
- for organization

NOTE: It can't be a plain kea/ folder because then Jest automatically mocks the `kea` module itself kea 🤦

* Fix type failures

- Caused by switch from any to unknown (changed back to any + added a .test_helper suffix exclusion for any)

* Fix Enterprise Search tests/imports

- I checked all application folders but this one, whoops

* PR feedback: comment copy

* Update tests/files added since PR open with new import locations

* Fix misc react router typing

- null not being type-able as a boolean
- forgot to remove various useParam imports after adding mockUseParams
+ misc unused kea import, probably added while debugging kea mocks
  • Loading branch information
Constance authored Jun 9, 2021
1 parent c225aaa commit 12986fb
Show file tree
Hide file tree
Showing 279 changed files with 603 additions and 448 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -1370,7 +1370,7 @@ module.exports = {
{
// Source files only - allow `any` in test/mock files
files: ['x-pack/plugins/enterprise_search/**/*.{ts,tsx}'],
excludedFiles: ['x-pack/plugins/enterprise_search/**/*.{test,mock}.{ts,tsx}'],
excludedFiles: ['x-pack/plugins/enterprise_search/**/*.{test,mock,test_helper}.{ts,tsx}'],
rules: {
'@typescript-eslint/no-explicit-any': 'error',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export const mockFlashMessageHelpers = {
flashErrorToast: jest.fn(),
};

jest.mock('../shared/flash_messages', () => ({
...(jest.requireActual('../shared/flash_messages') as object),
jest.mock('../../shared/flash_messages', () => ({
...(jest.requireActual('../../shared/flash_messages') as object),
...mockFlashMessageHelpers,
FlashMessagesLogic: {
values: mockFlashMessagesValues,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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.
*/

/**
* Combine all shared mock values/actions into a single obj
*
* NOTE: These variable names MUST start with 'mock*' in order for
* Jest to accept its use within a jest.mock()
*/
import { mockFlashMessagesValues, mockFlashMessagesActions } from './flash_messages_logic.mock';
import { mockHttpValues } from './http_logic.mock';
import { mockKibanaValues } from './kibana_logic.mock';
import { mockLicensingValues } from './licensing_logic.mock';
import { mockTelemetryActions } from './telemetry_logic.mock';

export const mockAllValues = {
...mockKibanaValues,
...mockLicensingValues,
...mockHttpValues,
...mockFlashMessagesValues,
};
export const mockAllActions = {
...mockTelemetryActions,
...mockFlashMessagesActions,
};

/**
* Import this file directly to mock useValues with a set of default values for all shared logic files.
* Example usage:
*
* import '../../../__mocks__/kea_logic'; // Must come before kea's import, adjust relative path as needed
*/
jest.mock('kea', () => ({
...(jest.requireActual('kea') as object),
useValues: jest.fn(() => ({ ...mockAllValues })),
useActions: jest.fn(() => ({ ...mockAllActions })),
}));

/**
* React component helpers
*
* Call this function to override a specific set of Kea values while retaining all other defaults
*
* Example usage:
*
* import { setMockValues } from '../../../__mocks__/kea_logic';
* import { SomeComponent } from './';
*
* it('some test', () => {
* setMockValues({ someValue: 'hello' });
* shallow(<SomeComponent />);
* });
*/
import { useValues, useActions } from 'kea';

export const setMockValues = (values: object) => {
(useValues as jest.Mock).mockImplementation(() => ({ ...mockAllValues, ...values }));
};
export const setMockActions = (actions: object) => {
(useActions as jest.Mock).mockImplementation(() => ({ ...mockAllActions, ...actions }));
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ export const mockHttpValues = {
readOnlyMode: false,
};

jest.mock('../shared/http', () => ({
jest.mock('../../shared/http', () => ({
HttpLogic: { values: mockHttpValues },
}));
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
* 2.0.
*/

export { mockHistory, mockLocation } from './react_router_history.mock';
export { mockKibanaValues } from './kibana_logic.mock';
export { mockLicensingValues } from './licensing_logic.mock';
export { mockHttpValues } from './http_logic.mock';
Expand All @@ -15,18 +14,6 @@ export {
mockFlashMessagesActions,
mockFlashMessageHelpers,
} from './flash_messages_logic.mock';
export {
mockAllValues,
mockAllActions,
setMockValues,
setMockActions,
LogicMounter,
} from './kea.mock';

export { mountAsync } from './mount_async.mock';
export { mountWithIntl } from './mount_with_i18n.mock';
export { shallowWithIntl } from './shallow_with_i18n.mock';
export { rerender } from './enzyme_rerender.mock';
// Note: shallow_useeffect must be imported directly as a file
export { mockAllValues, mockAllActions, setMockValues, setMockActions } from './hooks.mock';

export { expectedAsyncError } from './expected_async_error';
export { LogicMounter } from './logic_mounter.test_helper';
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
* 2.0.
*/

import { mockHistory } from './react_router_history.mock';
import { chartPluginMock } from '../../../../../../../src/plugins/charts/public/mocks';

import { chartPluginMock } from '../../../../../../src/plugins/charts/public/mocks';
import { mockHistory } from '../react_router/state.mock';

export const mockKibanaValues = {
config: { host: 'http://localhost:3002' },
Expand All @@ -24,6 +24,6 @@ export const mockKibanaValues = {
renderHeaderActions: jest.fn(),
};

jest.mock('../shared/kibana', () => ({
jest.mock('../../shared/kibana', () => ({
KibanaLogic: { values: mockKibanaValues },
}));
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
* 2.0.
*/

import { licensingMock } from '../../../../licensing/public/mocks';
import { licensingMock } from '../../../../../licensing/public/mocks';

export const mockLicensingValues = {
license: licensingMock.createLicense(),
hasPlatinumLicense: false,
hasGoldLicense: false,
};

jest.mock('../shared/licensing', () => ({
jest.mock('../../shared/licensing', () => ({
LicensingLogic: { values: mockLicensingValues },
}));
Original file line number Diff line number Diff line change
Expand Up @@ -6,75 +6,14 @@
*/

/**
* Combine all shared mock values/actions into a single obj
*
* NOTE: These variable names MUST start with 'mock*' in order for
* Jest to accept its use within a jest.mock()
*/
import { mockFlashMessagesValues, mockFlashMessagesActions } from './flash_messages_logic.mock';
import { mockHttpValues } from './http_logic.mock';
import { mockKibanaValues } from './kibana_logic.mock';
import { mockLicensingValues } from './licensing_logic.mock';
import { mockTelemetryActions } from './telemetry_logic.mock';

export const mockAllValues = {
...mockKibanaValues,
...mockLicensingValues,
...mockHttpValues,
...mockFlashMessagesValues,
};
export const mockAllActions = {
...mockTelemetryActions,
...mockFlashMessagesActions,
};

/**
* Import this file directly to mock useValues with a set of default values for all shared logic files.
* Example usage:
*
* import '../../../__mocks__/kea'; // Must come before kea's import, adjust relative path as needed
*/
jest.mock('kea', () => ({
...(jest.requireActual('kea') as object),
useValues: jest.fn(() => ({ ...mockAllValues })),
useActions: jest.fn(() => ({ ...mockAllActions })),
}));

/**
* React component helpers
*
* Call this function to override a specific set of Kea values while retaining all other defaults
*
* Example usage:
*
* import { setMockValues } from '../../../__mocks__/kea.mock';
* import { SomeComponent } from './';
*
* it('some test', () => {
* setMockValues({ someValue: 'hello' });
* shallow(<SomeComponent />);
* });
*/
import { useValues, useActions } from 'kea';

export const setMockValues = (values: object) => {
(useValues as jest.Mock).mockImplementation(() => ({ ...mockAllValues, ...values }));
};
export const setMockActions = (actions: object) => {
(useActions as jest.Mock).mockImplementation(() => ({ ...mockAllActions, ...actions }));
};

/**
* Kea logic helpers
*
* Call this function to mount a logic file and optionally override default values.
* Automatically DRYs out a lot of cruft for us, such as resetting context, creating the
* nested defaults path obj (see https://kea.js.org/docs/api/context#resetcontext), and
* returning an unmount function
*
* Example usage:
*
* import { LogicMounter } from '../../../__mocks__/kea.mock';
* import { LogicMounter } from '../../../__mocks__/kea_logic';
* import { SomeLogic } from './';
*
* const { mount, unmount } = new LogicMounter(SomeLogic);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const mockTelemetryActions = {
sendWorkplaceSearchTelemetry: jest.fn(),
};

jest.mock('../shared/telemetry', () => ({
...(jest.requireActual('../shared/telemetry') as object),
jest.mock('../../shared/telemetry', () => ({
...(jest.requireActual('../../shared/telemetry') as object),
TelemetryLogic: { actions: mockTelemetryActions },
}));
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,25 @@
* 2.0.
*/

/**
* NOTE: These variable names MUST start with 'mock*' in order for
* Jest to accept its use within a jest.mock()
*/
export const mockHistory = {
createHref: jest.fn(({ pathname }) => `/app/enterprise_search${pathname}`),
push: jest.fn(),
location: {
pathname: '/current-path',
},
listen: jest.fn(() => jest.fn()),
} as any;
export const mockLocation = {
key: 'someKey',
pathname: '/current-path',
search: '?query=something',
hash: '#hash',
state: {},
};
import { mockHistory, mockLocation } from './state.mock';

export const mockUseHistory = jest.fn(() => mockHistory);
export const mockUseLocation = jest.fn(() => mockLocation);
export const mockUseParams = jest.fn(() => ({}));
export const mockUseRouteMatch = jest.fn(() => true);

jest.mock('react-router-dom', () => {
const originalModule = jest.requireActual('react-router-dom');
return {
...originalModule,
useHistory: jest.fn(() => mockHistory),
useLocation: jest.fn(() => mockLocation),
useParams: jest.fn(() => ({})),
useRouteMatch: jest.fn(() => null),
useHistory: mockUseHistory,
useLocation: mockUseLocation,
useParams: mockUseParams,
useRouteMatch: mockUseRouteMatch,
// Note: RR's generatePath() opinionatedly encodeURI()s paths (although this doesn't actually
// show up/affect the final browser URL). Since we already have a generateEncodedPath helper &
// RR is removing this behavior in history 5.0+, I'm mocking tests to remove the extra encoding
// for now to make reading generateEncodedPath URLs a little less of a pain
generatePath: jest.fn((path, params) => decodeURI(originalModule.generatePath(path, params))),
};
});

/**
* For example usage, @see public/applications/shared/react_router_helpers/eui_link.test.tsx
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* 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.
*/

// State and hooks are stored in separate mock files for when a file needs to
// import a basic history mock without automatically jest.mock()ing all of React Router
export * from './state.mock';
export * from './hooks.mock';

// For example usage, @see public/applications/shared/react_router_helpers/eui_link.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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.
*/

/**
* NOTE: These variable names MUST start with 'mock*' in order for
* Jest to accept its use within a jest.mock()
*/

export const mockHistory = {
createHref: jest.fn(({ pathname }) => `/app/enterprise_search${pathname}`),
push: jest.fn(),
location: {
pathname: '/current-path',
},
listen: jest.fn(() => jest.fn()),
} as any;

export const mockLocation = {
key: 'someKey',
pathname: '/current-path',
search: '?query=something',
hash: '#hash',
state: {},
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { DEFAULT_INITIAL_APP_DATA } from '../../../common/__mocks__';
import { LogicMounter } from '../__mocks__';
import { LogicMounter } from '../__mocks__/kea_logic';

import { AppLogic } from './app_logic';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
*/

import '../../../__mocks__/shallow_useeffect.mock';
import '../../../__mocks__/react_router_history.mock';
import { mockKibanaValues, setMockValues, setMockActions, rerender } from '../../../__mocks__';
import { mockKibanaValues, setMockValues, setMockActions } from '../../../__mocks__/kea_logic';
import { mockUseParams } from '../../../__mocks__/react_router';

import React from 'react';
import { useParams } from 'react-router-dom';

import { shallow } from 'enzyme';

import { FlashMessages } from '../../../shared/flash_messages';
import { Loading } from '../../../shared/loading';
import { rerender } from '../../../test_helpers';
import { LogRetentionCallout } from '../log_retention';

import { AnalyticsLayout } from './analytics_layout';
Expand Down Expand Up @@ -63,7 +63,7 @@ describe('AnalyticsLayout', () => {

describe('data loading', () => {
it('loads query data for query details pages', () => {
(useParams as jest.Mock).mockReturnValueOnce({ query: 'test' });
mockUseParams.mockReturnValueOnce({ query: 'test' });
shallow(<AnalyticsLayout isQueryView title="" />);

expect(actions.loadQueryData).toHaveBeenCalledWith('test');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
mockKibanaValues,
mockHttpValues,
mockFlashMessageHelpers,
} from '../../../__mocks__';
} from '../../../__mocks__/kea_logic';

jest.mock('../engine', () => ({
EngineLogic: { values: { engineName: 'test-engine' } },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { mockKibanaValues } from '../../../../__mocks__';
import { mockKibanaValues } from '../../../../__mocks__/kea_logic';

import React from 'react';

Expand Down
Loading

0 comments on commit 12986fb

Please sign in to comment.