[React18] Migrate test suites to account for testing library upgrades security-defend-workflows (#201174)

This PR migrates test suites that use `renderHook` from the library
`@testing-library/react-hooks` to adopt the equivalent and replacement
of `renderHook` from the export that is now available from
`@testing-library/react`. This work is required for the planned
migration to react18.

##  Context

In this PR, usages of `waitForNextUpdate` that previously could have
been destructured from `renderHook` are now been replaced with `waitFor`
exported from `@testing-library/react`, furthermore `waitFor`
that would also have been destructured from the same renderHook result
is now been replaced with `waitFor` from the export of
`@testing-library/react`.

***Why is `waitFor` a sufficient enough replacement for
`waitForNextUpdate`, and better for testing values subject to async
computations?***

WaitFor will retry the provided callback if an error is returned, till
the configured timeout elapses. By default the retry interval is `50ms`
with a timeout value of `1000ms` that
effectively translates to at least 20 retries for assertions placed
within waitFor. See
https://testing-library.com/docs/dom-testing-library/api-async/#waitfor
for more information.
This however means that for person's writing tests, said person has to
be explicit about expectations that describe the internal state of the
hook being tested.
This implies checking for instance when a react query hook is being
rendered, there's an assertion that said hook isn't loading anymore.

In this PR you'd notice that this pattern has been adopted, with most
existing assertions following an invocation of `waitForNextUpdate` being
placed within a `waitFor`
invocation. In some cases the replacement is simply a `waitFor(() => new
Promise((resolve) => resolve(null)))` (many thanks to @kapral18, for
point out exactly why this works),
where this suffices the assertions that follow aren't placed within a
waitFor so this PR doesn't get larger than it needs to be.

It's also worth pointing out this PR might also contain changes to test
and application code to improve said existing test.

### What to do next?
1. Review the changes in this PR.
2. If you think the changes are correct, approve the PR.

## Any questions?
If you have any questions or need help with this PR, please leave
comments in this PR.

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Eyo O. Eyo 2024-11-27 13:11:20 +01:00 committed by GitHub
parent 6e5f5ed3bd
commit 2ec351d998
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 232 additions and 184 deletions

View file

@ -6,7 +6,7 @@
*/ */
import { useKibana } from '../../common/lib/kibana'; import { useKibana } from '../../common/lib/kibana';
import { useIsOsqueryAvailableSimple } from './use_is_osquery_available_simple'; import { useIsOsqueryAvailableSimple } from './use_is_osquery_available_simple';
import { renderHook } from '@testing-library/react-hooks'; import { renderHook, waitFor } from '@testing-library/react';
import { createStartServicesMock } from '@kbn/triggers-actions-ui-plugin/public/common/lib/kibana/kibana_react.mock'; import { createStartServicesMock } from '@kbn/triggers-actions-ui-plugin/public/common/lib/kibana/kibana_react.mock';
import { OSQUERY_INTEGRATION_NAME } from '../../../common'; import { OSQUERY_INTEGRATION_NAME } from '../../../common';
import { httpServiceMock } from '@kbn/core/public/mocks'; import { httpServiceMock } from '@kbn/core/public/mocks';
@ -41,15 +41,13 @@ describe('UseIsOsqueryAvailableSimple', () => {
}); });
}); });
it('should expect response from API and return enabled flag', async () => { it('should expect response from API and return enabled flag', async () => {
const { result, waitForValueToChange } = renderHook(() => const { result } = renderHook(() =>
useIsOsqueryAvailableSimple({ useIsOsqueryAvailableSimple({
agentId: '3242332', agentId: '3242332',
}) })
); );
expect(result.current).toBe(false); expect(result.current).toBe(false);
await waitForValueToChange(() => result.current); await waitFor(() => expect(result.current).toBe(true));
expect(result.current).toBe(true);
}); });
}); });

View file

@ -4,7 +4,8 @@
* 2.0; you may not use this file except in compliance with the Elastic License * 2.0; you may not use this file except in compliance with the Elastic License
* 2.0. * 2.0.
*/ */
import type React from 'react';
import { act } from '@testing-library/react';
import type { UseHostIsolationActionProps } from './use_host_isolation_action'; import type { UseHostIsolationActionProps } from './use_host_isolation_action';
import { useHostIsolationAction } from './use_host_isolation_action'; import { useHostIsolationAction } from './use_host_isolation_action';
import type { AppContextTestRender, UserPrivilegesMockSetter } from '../../../../mock/endpoint'; import type { AppContextTestRender, UserPrivilegesMockSetter } from '../../../../mock/endpoint';
@ -15,7 +16,6 @@ import type { AlertTableContextMenuItem } from '../../../../../detections/compon
import type { ResponseActionsApiCommandNames } from '../../../../../../common/endpoint/service/response_actions/constants'; import type { ResponseActionsApiCommandNames } from '../../../../../../common/endpoint/service/response_actions/constants';
import { agentStatusMocks } from '../../../../../../common/endpoint/service/response_actions/mocks/agent_status.mocks'; import { agentStatusMocks } from '../../../../../../common/endpoint/service/response_actions/mocks/agent_status.mocks';
import { ISOLATE_HOST, UNISOLATE_HOST } from './translations'; import { ISOLATE_HOST, UNISOLATE_HOST } from './translations';
import type React from 'react';
import { import {
HOST_ENDPOINT_UNENROLLED_TOOLTIP, HOST_ENDPOINT_UNENROLLED_TOOLTIP,
LOADING_ENDPOINT_DATA_TOOLTIP, LOADING_ENDPOINT_DATA_TOOLTIP,
@ -87,20 +87,22 @@ describe('useHostIsolationAction', () => {
}); });
} }
const { result, waitForValueToChange } = render(); const { result } = render();
await waitForValueToChange(() => result.current);
expect(result.current).toEqual([ await appContextMock.waitFor(() =>
buildExpectedMenuItemResult({ expect(result.current).toEqual([
...(command === 'unisolate' ? { name: UNISOLATE_HOST } : {}), buildExpectedMenuItemResult({
}), ...(command === 'unisolate' ? { name: UNISOLATE_HOST } : {}),
]); }),
])
);
} }
); );
it('should call `closePopover` callback when menu item `onClick` is called', async () => { it('should call `closePopover` callback when menu item `onClick` is called', async () => {
const { result, waitForValueToChange } = render(); const { result } = render();
await waitForValueToChange(() => result.current); await appContextMock.waitFor(() => expect(result.current[0].onClick).toBeDefined());
result.current[0].onClick!({} as unknown as React.MouseEvent); result.current[0].onClick!({} as unknown as React.MouseEvent);
expect(hookProps.closePopover).toHaveBeenCalled(); expect(hookProps.closePopover).toHaveBeenCalled();
@ -135,12 +137,14 @@ describe('useHostIsolationAction', () => {
it('should return disabled menu item while loading agent status', async () => { it('should return disabled menu item while loading agent status', async () => {
const { result } = render(); const { result } = render();
expect(result.current).toEqual([ await appContextMock.waitFor(() =>
buildExpectedMenuItemResult({ expect(result.current).toEqual([
disabled: true, buildExpectedMenuItemResult({
toolTipContent: LOADING_ENDPOINT_DATA_TOOLTIP, disabled: true,
}), toolTipContent: LOADING_ENDPOINT_DATA_TOOLTIP,
]); }),
])
);
}); });
it.each(['endpoint', 'non-endpoint'])( it.each(['endpoint', 'non-endpoint'])(
@ -156,37 +160,51 @@ describe('useHostIsolationAction', () => {
if (type === 'non-endpoint') { if (type === 'non-endpoint') {
hookProps.detailsData = endpointAlertDataMock.generateSentinelOneAlertDetailsItemData(); hookProps.detailsData = endpointAlertDataMock.generateSentinelOneAlertDetailsItemData();
} }
const { result, waitForValueToChange } = render(); const { result } = render();
await waitForValueToChange(() => result.current); await appContextMock.waitFor(() =>
expect(result.current).toEqual([
expect(result.current).toEqual([ buildExpectedMenuItemResult({
buildExpectedMenuItemResult({ disabled: true,
disabled: true, toolTipContent:
toolTipContent: type === 'endpoint'
type === 'endpoint' ? HOST_ENDPOINT_UNENROLLED_TOOLTIP : NOT_FROM_ENDPOINT_HOST_TOOLTIP, ? HOST_ENDPOINT_UNENROLLED_TOOLTIP
}), : NOT_FROM_ENDPOINT_HOST_TOOLTIP,
]); }),
])
);
} }
); );
it('should call isolate API when agent is currently NOT isolated', async () => { it('should call isolate API when agent is currently NOT isolated', async () => {
const { result, waitForValueToChange } = render(); const { result } = render();
await waitForValueToChange(() => result.current); await appContextMock.waitFor(() => expect(result.current[0].onClick).toBeDefined());
result.current[0].onClick!({} as unknown as React.MouseEvent); result.current[0].onClick!({} as unknown as React.MouseEvent);
expect(hookProps.onAddIsolationStatusClick).toHaveBeenCalledWith('isolateHost'); expect(hookProps.onAddIsolationStatusClick).toHaveBeenCalledWith('isolateHost');
}); });
it('should call un-isolate API when agent is currently isolated', async () => { it('should call un-isolate API when agent is currently isolated', async () => {
apiMock.responseProvider.getAgentStatus.mockReturnValue( apiMock.responseProvider.getAgentStatus.mockImplementation(({ query }) => {
agentStatusMocks.generateAgentStatusApiResponse({ const agentId = (query!.agentIds as string[])[0];
data: { 'abfe4a35-d5b4-42a0-a539-bd054c791769': { isolated: true } },
})
);
const { result, waitForValueToChange } = render();
await waitForValueToChange(() => result.current);
result.current[0].onClick!({} as unknown as React.MouseEvent);
expect(hookProps.onAddIsolationStatusClick).toHaveBeenCalledWith('unisolateHost'); return agentStatusMocks.generateAgentStatusApiResponse({
data: { [agentId]: { isolated: true } },
});
});
const { result } = render();
await appContextMock.waitFor(() => {
expect(apiMock.responseProvider.getAgentStatus).toHaveBeenCalled();
expect(result.current[0].onClick).toBeDefined();
});
act(() => {
result.current[0].onClick!({} as unknown as React.MouseEvent);
});
await appContextMock.waitFor(() =>
expect(hookProps.onAddIsolationStatusClick).toHaveBeenCalledWith('unisolateHost')
);
}); });
}); });

View file

@ -25,7 +25,8 @@ import type { AppContextTestRender } from '../../../../mock/endpoint';
import { createAppRootMockRenderer, endpointAlertDataMock } from '../../../../mock/endpoint'; import { createAppRootMockRenderer, endpointAlertDataMock } from '../../../../mock/endpoint';
import { HOST_METADATA_LIST_ROUTE } from '../../../../../../common/endpoint/constants'; import { HOST_METADATA_LIST_ROUTE } from '../../../../../../common/endpoint/constants';
import { endpointMetadataHttpMocks } from '../../../../../management/pages/endpoint_hosts/mocks'; import { endpointMetadataHttpMocks } from '../../../../../management/pages/endpoint_hosts/mocks';
import type { RenderHookResult } from '@testing-library/react-hooks/src/types'; import type { RenderHookResult } from '@testing-library/react';
import { waitFor, act } from '@testing-library/react';
import { createHttpFetchError } from '@kbn/core-http-browser-mocks'; import { createHttpFetchError } from '@kbn/core-http-browser-mocks';
import { HostStatus } from '../../../../../../common/endpoint/types'; import { HostStatus } from '../../../../../../common/endpoint/types';
import { import {
@ -61,17 +62,14 @@ describe('use responder action data hooks', () => {
describe('useWithResponderActionDataFromAlert() hook', () => { describe('useWithResponderActionDataFromAlert() hook', () => {
let renderHook: () => RenderHookResult< let renderHook: () => RenderHookResult<
UseWithResponderActionDataFromAlertProps, ResponderActionData,
ResponderActionData UseWithResponderActionDataFromAlertProps
>; >;
let alertDetailItemData: TimelineEventsDetailsItem[]; let alertDetailItemData: TimelineEventsDetailsItem[];
beforeEach(() => { beforeEach(() => {
renderHook = () => { renderHook = () => {
return appContextMock.renderHook< return appContextMock.renderHook(() =>
UseWithResponderActionDataFromAlertProps,
ResponderActionData
>(() =>
useWithResponderActionDataFromAlert({ useWithResponderActionDataFromAlert({
eventData: alertDetailItemData, eventData: alertDetailItemData,
onClick: onClickMock, onClick: onClickMock,
@ -95,7 +93,9 @@ describe('use responder action data hooks', () => {
it('should call `onClick()` function prop when is pass to the hook', () => { it('should call `onClick()` function prop when is pass to the hook', () => {
alertDetailItemData = endpointAlertDataMock.generateSentinelOneAlertDetailsItemData(); alertDetailItemData = endpointAlertDataMock.generateSentinelOneAlertDetailsItemData();
const { result } = renderHook(); const { result } = renderHook();
result.current.handleResponseActionsClick(); act(() => {
result.current.handleResponseActionsClick();
});
expect(onClickMock).toHaveBeenCalled(); expect(onClickMock).toHaveBeenCalled();
}); });
@ -103,7 +103,9 @@ describe('use responder action data hooks', () => {
it('should NOT call `onClick` if the action is disabled', () => { it('should NOT call `onClick` if the action is disabled', () => {
alertDetailItemData = endpointAlertDataMock.generateAlertDetailsItemDataForAgentType('foo'); alertDetailItemData = endpointAlertDataMock.generateAlertDetailsItemDataForAgentType('foo');
const { result } = renderHook(); const { result } = renderHook();
result.current.handleResponseActionsClick(); act(() => {
result.current.handleResponseActionsClick();
});
expect(onClickMock).not.toHaveBeenCalled(); expect(onClickMock).not.toHaveBeenCalled();
}); });
@ -169,8 +171,8 @@ describe('use responder action data hooks', () => {
}); });
it('should show action enabled if host metadata was retrieved and host is enrolled', async () => { it('should show action enabled if host metadata was retrieved and host is enrolled', async () => {
const { result, waitForValueToChange } = renderHook(); const { result } = renderHook();
await waitForValueToChange(() => result.current.isDisabled); await waitFor(() => expect(result.current.isDisabled).toBe(false));
expect(result.current).toEqual(getExpectedResponderActionData()); expect(result.current).toEqual(getExpectedResponderActionData());
}); });
@ -181,8 +183,10 @@ describe('use responder action data hooks', () => {
statusCode: 404, statusCode: 404,
}); });
}); });
const { result, waitForValueToChange } = renderHook();
await waitForValueToChange(() => result.current.tooltip); const { result } = renderHook();
await waitFor(() => expect(result.current.tooltip).not.toEqual('Loading'));
expect(result.current).toEqual( expect(result.current).toEqual(
getExpectedResponderActionData({ getExpectedResponderActionData({
@ -199,8 +203,8 @@ describe('use responder action data hooks', () => {
}; };
metadataApiMocks.responseProvider.metadataDetails.mockReturnValue(hostMetadata); metadataApiMocks.responseProvider.metadataDetails.mockReturnValue(hostMetadata);
const { result, waitForValueToChange } = renderHook(); const { result } = renderHook();
await waitForValueToChange(() => result.current.tooltip); await waitFor(() => expect(result.current.tooltip).not.toEqual('Loading'));
expect(result.current).toEqual( expect(result.current).toEqual(
getExpectedResponderActionData({ getExpectedResponderActionData({
@ -216,8 +220,8 @@ describe('use responder action data hooks', () => {
statusCode: 500, statusCode: 500,
}); });
}); });
const { result, waitForValueToChange } = renderHook(); const { result } = renderHook();
await waitForValueToChange(() => result.current.tooltip); await waitFor(() => expect(result.current.tooltip).not.toEqual('Loading'));
expect(result.current).toEqual( expect(result.current).toEqual(
getExpectedResponderActionData({ getExpectedResponderActionData({
@ -231,7 +235,7 @@ describe('use responder action data hooks', () => {
describe('useResponderActionData() hook', () => { describe('useResponderActionData() hook', () => {
let hookProps: UseResponderActionDataProps; let hookProps: UseResponderActionDataProps;
let renderHook: () => RenderHookResult<UseResponderActionDataProps, ResponderActionData>; let renderHook: () => RenderHookResult<ResponderActionData, UseResponderActionDataProps>;
beforeEach(() => { beforeEach(() => {
endpointMetadataHttpMocks(appContextMock.coreStart.http); endpointMetadataHttpMocks(appContextMock.coreStart.http);
@ -241,15 +245,13 @@ describe('use responder action data hooks', () => {
onClick: onClickMock, onClick: onClickMock,
}; };
renderHook = () => { renderHook = () => {
return appContextMock.renderHook<UseResponderActionDataProps, ResponderActionData>(() => return appContextMock.renderHook(() => useResponderActionData(hookProps));
useResponderActionData(hookProps)
);
}; };
}); });
it('should show action enabled when agentType is Endpoint and host is enabled', async () => { it('should show action enabled when agentType is Endpoint and host is enabled', async () => {
const { result, waitForValueToChange } = renderHook(); const { result } = renderHook();
await waitForValueToChange(() => result.current.isDisabled); await waitFor(() => expect(result.current.isDisabled).toBe(false));
expect(result.current).toEqual(getExpectedResponderActionData()); expect(result.current).toEqual(getExpectedResponderActionData());
}); });
@ -266,9 +268,13 @@ describe('use responder action data hooks', () => {
}); });
it('should call `onClick` prop when action is enabled', async () => { it('should call `onClick` prop when action is enabled', async () => {
const { result, waitForValueToChange } = renderHook(); const { result } = renderHook();
await waitForValueToChange(() => result.current.isDisabled);
result.current.handleResponseActionsClick(); await waitFor(() => expect(result.current.isDisabled).toBe(false));
act(() => {
result.current.handleResponseActionsClick();
});
expect(onClickMock).toHaveBeenCalled(); expect(onClickMock).toHaveBeenCalled();
}); });
@ -276,7 +282,10 @@ describe('use responder action data hooks', () => {
it('should not call `onCLick` prop when action is disabled', () => { it('should not call `onCLick` prop when action is disabled', () => {
hookProps.agentType = 'sentinel_one'; hookProps.agentType = 'sentinel_one';
const { result } = renderHook(); const { result } = renderHook();
result.current.handleResponseActionsClick();
act(() => {
result.current.handleResponseActionsClick();
});
expect(onClickMock).not.toHaveBeenCalled(); expect(onClickMock).not.toHaveBeenCalled();
}); });

View file

@ -9,18 +9,21 @@ import type { ReactPortal } from 'react';
import React from 'react'; import React from 'react';
import type { MemoryHistory } from 'history'; import type { MemoryHistory } from 'history';
import { createMemoryHistory } from 'history'; import { createMemoryHistory } from 'history';
import type { RenderOptions, RenderResult } from '@testing-library/react'; import type {
import { render as reactRender } from '@testing-library/react'; RenderOptions,
RenderResult,
RenderHookResult,
RenderHookOptions,
} from '@testing-library/react';
import {
render as reactRender,
waitFor,
renderHook as reactRenderHook,
} from '@testing-library/react';
import type { Action, Reducer, Store } from 'redux'; import type { Action, Reducer, Store } from 'redux';
import { QueryClient } from '@tanstack/react-query'; import { QueryClient } from '@tanstack/react-query';
import { coreMock } from '@kbn/core/public/mocks'; import { coreMock } from '@kbn/core/public/mocks';
import { PLUGIN_ID } from '@kbn/fleet-plugin/common'; import { PLUGIN_ID } from '@kbn/fleet-plugin/common';
import type { RenderHookOptions, RenderHookResult } from '@testing-library/react-hooks';
import { renderHook as reactRenderHook } from '@testing-library/react-hooks';
import type {
ReactHooksRenderer,
WrapperComponent,
} from '@testing-library/react-hooks/src/types/react';
import type { UseBaseQueryResult } from '@tanstack/react-query'; import type { UseBaseQueryResult } from '@tanstack/react-query';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import type { DeepReadonly } from 'utility-types'; import type { DeepReadonly } from 'utility-types';
@ -101,17 +104,16 @@ export type WaitForReactHookState =
> >
| false; | false;
type HookRendererFunction<TProps, TResult> = (props: TProps) => TResult; type HookRendererFunction<TResult, TProps> = (props: TProps) => TResult;
/** /**
* A utility renderer for hooks that return React Query results * A utility renderer for hooks that return React Query results
*/ */
export type ReactQueryHookRenderer< export type ReactQueryHookRenderer<
// eslint-disable-next-line @typescript-eslint/no-explicit-any TProps = unknown,
TProps = any,
TResult extends UseBaseQueryResult = UseBaseQueryResult TResult extends UseBaseQueryResult = UseBaseQueryResult
> = ( > = (
hookFn: HookRendererFunction<TProps, TResult>, hookFn: HookRendererFunction<TResult, TProps>,
/** /**
* If defined (default is `isSuccess`), the renderer will wait for the given react * If defined (default is `isSuccess`), the renderer will wait for the given react
* query response state value to be true * query response state value to be true
@ -150,7 +152,15 @@ export interface AppContextTestRender {
/** /**
* Renders a hook within a mocked security solution app context * Renders a hook within a mocked security solution app context
*/ */
renderHook: ReactHooksRenderer['renderHook']; renderHook: <TResult, TProps>(
hookFn: HookRendererFunction<TResult, TProps>,
options?: RenderHookOptions<TProps>
) => RenderHookResult<TResult, TProps>;
/**
* Waits the return value of the callback provided to is truthy
*/
waitFor: typeof waitFor;
/** /**
* A helper utility for rendering specifically hooks that wrap ReactQuery * A helper utility for rendering specifically hooks that wrap ReactQuery
@ -305,12 +315,12 @@ export const createAppRootMockRenderer = (): AppContextTestRender => {
}); });
}; };
const renderHook: ReactHooksRenderer['renderHook'] = <TProps, TResult>( const renderHook = <TResult, TProps>(
hookFn: HookRendererFunction<TProps, TResult>, hookFn: HookRendererFunction<TResult, TProps>,
options: RenderHookOptions<TProps> = {} options: RenderHookOptions<TProps> = {}
): RenderHookResult<TProps, TResult> => { ) => {
return reactRenderHook<TProps, TResult>(hookFn, { return reactRenderHook<TResult, TProps>(hookFn, {
wrapper: AppWrapper as WrapperComponent<TProps>, wrapper: AppWrapper as React.FC<React.PropsWithChildren>,
...options, ...options,
}); });
}; };
@ -319,16 +329,17 @@ export const createAppRootMockRenderer = (): AppContextTestRender => {
TProps, TProps,
TResult extends UseBaseQueryResult = UseBaseQueryResult TResult extends UseBaseQueryResult = UseBaseQueryResult
>( >(
hookFn: HookRendererFunction<TProps, TResult>, hookFn: HookRendererFunction<TResult, TProps>,
/**
* If defined (default is `isSuccess`), the renderer will wait for the given react query to be truthy
*/
waitForHook: WaitForReactHookState = 'isSuccess', waitForHook: WaitForReactHookState = 'isSuccess',
options: RenderHookOptions<TProps> = {} options: RenderHookOptions<TProps> = {}
) => { ) => {
const { result: hookResult, waitFor } = renderHook<TProps, TResult>(hookFn, options); const { result: hookResult } = renderHook<TResult, TProps>(hookFn, options);
if (waitForHook) { if (waitForHook) {
await waitFor(() => { await waitFor(() => expect(hookResult.current[waitForHook]).toBe(true));
return hookResult.current[waitForHook];
});
} }
return hookResult.current; return hookResult.current;
@ -400,6 +411,7 @@ export const createAppRootMockRenderer = (): AppContextTestRender => {
setExperimentalFlag, setExperimentalFlag,
getUserPrivilegesMockSetter, getUserPrivilegesMockSetter,
queryClient, queryClient,
waitFor,
}; };
}; };

View file

@ -5,7 +5,11 @@
* 2.0. * 2.0.
*/ */
import { renderMutation, renderQuery } from '../../../management/hooks/test_utils'; import {
renderMutation,
renderQuery,
renderWrappedHook,
} from '../../../management/hooks/test_utils';
import type { Entity } from './use_asset_criticality'; import type { Entity } from './use_asset_criticality';
import { useAssetCriticalityPrivileges, useAssetCriticalityData } from './use_asset_criticality'; import { useAssetCriticalityPrivileges, useAssetCriticalityData } from './use_asset_criticality';
@ -69,10 +73,7 @@ describe('useAssetCriticality', () => {
mockCreateAssetCriticality.mockResolvedValue({}); mockCreateAssetCriticality.mockResolvedValue({});
const entity: Entity = { name: 'test_entity_name', type: 'host' }; const entity: Entity = { name: 'test_entity_name', type: 'host' };
const { mutation } = await renderQuery( const { mutation } = await renderWrappedHook(() => useAssetCriticalityData({ entity }));
() => useAssetCriticalityData({ entity }),
'isSuccess'
);
await renderMutation(async () => await renderMutation(async () =>
mutation.mutate({ mutation.mutate({
@ -91,10 +92,7 @@ describe('useAssetCriticality', () => {
mockCreateAssetCriticality.mockResolvedValue({}); mockCreateAssetCriticality.mockResolvedValue({});
const entity: Entity = { name: 'test_entity_name', type: 'host' }; const entity: Entity = { name: 'test_entity_name', type: 'host' };
const { mutation } = await renderQuery( const { mutation } = await renderWrappedHook(() => useAssetCriticalityData({ entity }));
() => useAssetCriticalityData({ entity }),
'isSuccess'
);
await renderMutation(async () => await renderMutation(async () =>
mutation.mutate({ mutation.mutate({

View file

@ -47,9 +47,9 @@ describe('When the flyout is opened in the ArtifactListPage component', () => {
render = async (props = {}) => { render = async (props = {}) => {
renderResult = renderSetup.renderArtifactListPage(props); renderResult = renderSetup.renderArtifactListPage(props);
await waitFor(async () => { await waitFor(async () =>
expect(renderResult.getByTestId('testPage-flyout')); expect(renderResult.getByTestId('testPage-flyout')).toBeInTheDocument()
}); );
return renderResult; return renderResult;
}; };

View file

@ -113,13 +113,9 @@ export const ConsoleManager = memo<ConsoleManagerProps>(({ storage = {}, childre
validateIdOrThrow(id); validateIdOrThrow(id);
setConsoleStorage((prevState) => { setConsoleStorage((prevState) => {
return { const newState = { ...prevState };
...prevState, newState[id].isOpen = false;
[id]: { return newState;
...prevState[id],
isOpen: false,
},
};
}); });
}, },
[validateIdOrThrow] // << IMPORTANT: this callback should have only immutable dependencies [validateIdOrThrow] // << IMPORTANT: this callback should have only immutable dependencies

View file

@ -5,8 +5,6 @@
* 2.0. * 2.0.
*/ */
import type { RenderHookResult } from '@testing-library/react-hooks';
import { renderHook as _renderHook, act } from '@testing-library/react-hooks';
import { useConsoleManager } from '../console_manager'; import { useConsoleManager } from '../console_manager';
import React from 'react'; import React from 'react';
import type { import type {
@ -22,12 +20,13 @@ import {
getNewConsoleRegistrationMock, getNewConsoleRegistrationMock,
} from '../mocks'; } from '../mocks';
import userEvent, { type UserEvent } from '@testing-library/user-event'; import userEvent, { type UserEvent } from '@testing-library/user-event';
import { waitFor } from '@testing-library/react'; import type { RenderHookResult } from '@testing-library/react';
import { waitFor, act, renderHook as _renderHook } from '@testing-library/react';
import { enterConsoleCommand } from '../../../mocks'; import { enterConsoleCommand } from '../../../mocks';
describe('When using ConsoleManager', () => { describe('When using ConsoleManager', () => {
describe('and using the ConsoleManagerInterface via the hook', () => { describe('and using the ConsoleManagerInterface via the hook', () => {
type RenderResultInterface = RenderHookResult<never, ConsoleManagerClient>; type RenderResultInterface = RenderHookResult<ConsoleManagerClient, never>;
let renderHook: () => RenderResultInterface; let renderHook: () => RenderResultInterface;
let renderResult: RenderResultInterface; let renderResult: RenderResultInterface;
@ -103,20 +102,27 @@ describe('When using ConsoleManager', () => {
); );
}); });
it('should hide a console by `id`', () => { it('should hide a console by `id`', async () => {
renderHook(); renderHook();
const { id: consoleId } = registerNewConsole(); const { id: consoleId } = registerNewConsole();
let consoleClient: ReturnType<ConsoleManagerClient['getOne']>;
act(() => {
consoleClient = renderResult.result.current.getOne(consoleId);
});
act(() => { act(() => {
renderResult.result.current.show(consoleId); renderResult.result.current.show(consoleId);
}); });
expect(renderResult.result.current.getOne(consoleId)!.isVisible()).toBe(true); await waitFor(() => expect(consoleClient!.isVisible()).toBe(true));
act(() => { act(() => {
renderResult.result.current.hide(consoleId); renderResult.result.current.hide(consoleId);
}); });
expect(renderResult.result.current.getOne(consoleId)!.isVisible()).toBe(false); await waitFor(() => expect(consoleClient!.isVisible()).toBe(false));
}); });
it('should throw if attempting to hide a console with invalid `id`', () => { it('should throw if attempting to hide a console with invalid `id`', () => {
@ -163,7 +169,9 @@ describe('When using ConsoleManager', () => {
beforeEach(() => { beforeEach(() => {
renderHook(); renderHook();
({ id: consoleId } = registerNewConsole()); ({ id: consoleId } = registerNewConsole());
registeredConsole = renderResult.result.current.getOne(consoleId)!; act(() => {
registeredConsole = renderResult.result.current.getOne(consoleId)!;
});
}); });
it('should have the expected interface', () => { it('should have the expected interface', () => {
@ -178,27 +186,31 @@ describe('When using ConsoleManager', () => {
}); });
it('should display the console when `.show()` is called', async () => { it('should display the console when `.show()` is called', async () => {
registeredConsole.show(); act(() => {
await renderResult.waitForNextUpdate(); registeredConsole.show();
});
expect(registeredConsole.isVisible()).toBe(true); await waitFor(() => expect(registeredConsole.isVisible()).toBe(true));
}); });
it('should hide the console when `.hide()` is called', async () => { it('should hide the console when `.hide()` is called', async () => {
registeredConsole.show(); act(() => {
await renderResult.waitForNextUpdate(); registeredConsole.show();
expect(registeredConsole.isVisible()).toBe(true); });
registeredConsole.hide(); await waitFor(() => expect(registeredConsole.isVisible()).toBe(true));
await renderResult.waitForNextUpdate();
expect(registeredConsole.isVisible()).toBe(false); act(() => {
registeredConsole.hide();
});
await waitFor(() => expect(registeredConsole.isVisible()).toBe(false));
}); });
it('should un-register the console when `.terminate() is called', async () => { it('should un-register the console when `.terminate() is called', async () => {
registeredConsole.terminate(); act(() => {
await renderResult.waitForNextUpdate(); registeredConsole.terminate();
});
expect(renderResult.result.current.getOne(consoleId)).toBeUndefined(); await waitFor(() => expect(renderResult.result.current.getOne(consoleId)).toBeUndefined());
}); });
}); });
}); });

View file

@ -10,14 +10,15 @@ import { createAppRootMockRenderer } from '../../../common/mock/endpoint';
import { useGetAgentStatus } from './use_get_agent_status'; import { useGetAgentStatus } from './use_get_agent_status';
import { agentStatusGetHttpMock } from '../../mocks'; import { agentStatusGetHttpMock } from '../../mocks';
import { AGENT_STATUS_ROUTE } from '../../../../common/endpoint/constants'; import { AGENT_STATUS_ROUTE } from '../../../../common/endpoint/constants';
import type { RenderHookResult } from '@testing-library/react-hooks/src/types'; import type { RenderHookResult } from '@testing-library/react';
import { waitFor } from '@testing-library/react';
describe('useGetAgentStatus hook', () => { describe('useGetAgentStatus hook', () => {
let httpMock: AppContextTestRender['coreStart']['http']; let httpMock: AppContextTestRender['coreStart']['http'];
let agentIdsProp: Parameters<typeof useGetAgentStatus>[0]; let agentIdsProp: Parameters<typeof useGetAgentStatus>[0];
let optionsProp: Parameters<typeof useGetAgentStatus>[2]; let optionsProp: Parameters<typeof useGetAgentStatus>[2];
let apiMock: ReturnType<typeof agentStatusGetHttpMock>; let apiMock: ReturnType<typeof agentStatusGetHttpMock>;
let renderHook: () => RenderHookResult<unknown, ReturnType<typeof useGetAgentStatus>>; let renderHook: () => RenderHookResult<ReturnType<typeof useGetAgentStatus>, unknown>;
beforeEach(() => { beforeEach(() => {
const appTestContext = createAppRootMockRenderer(); const appTestContext = createAppRootMockRenderer();
@ -25,7 +26,7 @@ describe('useGetAgentStatus hook', () => {
httpMock = appTestContext.coreStart.http; httpMock = appTestContext.coreStart.http;
apiMock = agentStatusGetHttpMock(httpMock); apiMock = agentStatusGetHttpMock(httpMock);
renderHook = () => { renderHook = () => {
return appTestContext.renderHook<unknown, ReturnType<typeof useGetAgentStatus>>(() => return appTestContext.renderHook(() =>
useGetAgentStatus(agentIdsProp, 'endpoint', optionsProp) useGetAgentStatus(agentIdsProp, 'endpoint', optionsProp)
); );
}; };
@ -63,28 +64,28 @@ describe('useGetAgentStatus hook', () => {
}); });
it('should return expected data', async () => { it('should return expected data', async () => {
const { result, waitForValueToChange } = renderHook(); const { result } = renderHook();
await waitForValueToChange(() => result.current); await waitFor(() =>
expect(result.current.data).toEqual({
expect(result.current.data).toEqual({ '1-2-3': {
'1-2-3': { agentId: '1-2-3',
agentId: '1-2-3', agentType: 'endpoint',
agentType: 'endpoint', found: true,
found: true, isolated: false,
isolated: false, lastSeen: expect.any(String),
lastSeen: expect.any(String), pendingActions: {},
pendingActions: {}, status: 'healthy',
status: 'healthy', },
}, })
}); );
}); });
it('should NOT call agent status api if list of agent ids is empty', async () => { it('should NOT call agent status api if list of agent ids is empty', async () => {
agentIdsProp = ['', ' ']; agentIdsProp = ['', ' '];
const { result, waitForValueToChange } = renderHook(); const { result } = renderHook();
await waitForValueToChange(() => result.current); await waitFor(() => {
expect(result.current.data).toEqual({});
expect(result.current.data).toEqual({}); expect(apiMock.responseProvider.getAgentStatus).not.toHaveBeenCalled();
expect(apiMock.responseProvider.getAgentStatus).not.toHaveBeenCalled(); });
}); });
}); });

View file

@ -15,7 +15,7 @@ import {
renderMutation, renderMutation,
} from '../test_utils'; } from '../test_utils';
import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock';
import { act } from '@testing-library/react-hooks'; import { act } from '@testing-library/react';
const apiVersion = '2023-10-31'; const apiVersion = '2023-10-31';

View file

@ -15,7 +15,7 @@ import {
renderMutation, renderMutation,
} from '../test_utils'; } from '../test_utils';
import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock';
import { act } from '@testing-library/react-hooks'; import { act } from '@testing-library/react';
const apiVersion = '2023-10-31'; const apiVersion = '2023-10-31';

View file

@ -15,7 +15,7 @@ import {
renderMutation, renderMutation,
} from '../test_utils'; } from '../test_utils';
import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock';
import { act } from '@testing-library/react-hooks'; import { act } from '@testing-library/react';
describe('Create artifact hook', () => { describe('Create artifact hook', () => {
let result: ReturnType<typeof useCreateArtifact>; let result: ReturnType<typeof useCreateArtifact>;

View file

@ -15,7 +15,7 @@ import {
renderMutation, renderMutation,
} from '../test_utils'; } from '../test_utils';
import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock';
import { act } from '@testing-library/react-hooks'; import { act } from '@testing-library/react';
describe('Delete artifact hook', () => { describe('Delete artifact hook', () => {
let result: ReturnType<typeof useDeleteArtifact>; let result: ReturnType<typeof useDeleteArtifact>;

View file

@ -6,7 +6,7 @@
*/ */
import { useGetUpdatedTags } from '.'; import { useGetUpdatedTags } from '.';
import { renderHook } from '@testing-library/react-hooks'; import { renderHook } from '@testing-library/react';
import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
import type { TagFilter } from '../../../../common/endpoint/service/artifacts/utils'; import type { TagFilter } from '../../../../common/endpoint/service/artifacts/utils';

View file

@ -5,7 +5,7 @@
* 2.0. * 2.0.
*/ */
import { renderHook } from '@testing-library/react-hooks'; import { renderHook, waitFor } from '@testing-library/react';
import { useHostIsolationExceptionsAccess } from './use_host_isolation_exceptions_access'; import { useHostIsolationExceptionsAccess } from './use_host_isolation_exceptions_access';
import { checkArtifactHasData } from '../../services/exceptions_list/check_artifact_has_data'; import { checkArtifactHasData } from '../../services/exceptions_list/check_artifact_has_data';
@ -29,7 +29,7 @@ describe('useHostIsolationExceptionsAccess', () => {
}; };
test('should set access to true if canAccessHostIsolationExceptions is true', async () => { test('should set access to true if canAccessHostIsolationExceptions is true', async () => {
const { result, waitFor } = setupHook(true, false); const { result } = setupHook(true, false);
await waitFor(() => expect(result.current.hasAccessToHostIsolationExceptions).toBe(true)); await waitFor(() => expect(result.current.hasAccessToHostIsolationExceptions).toBe(true));
}); });
@ -37,7 +37,7 @@ describe('useHostIsolationExceptionsAccess', () => {
test('should check for artifact data if canReadHostIsolationExceptions is true and canAccessHostIsolationExceptions is false', async () => { test('should check for artifact data if canReadHostIsolationExceptions is true and canAccessHostIsolationExceptions is false', async () => {
mockArtifactHasData(); mockArtifactHasData();
const { result, waitFor } = setupHook(false, true); const { result } = setupHook(false, true);
await waitFor(() => { await waitFor(() => {
expect(checkArtifactHasData).toHaveBeenCalledWith(mockApiClient()); expect(checkArtifactHasData).toHaveBeenCalledWith(mockApiClient());
@ -48,7 +48,7 @@ describe('useHostIsolationExceptionsAccess', () => {
test('should set access to false if canReadHostIsolationExceptions is true but no artifact data exists', async () => { test('should set access to false if canReadHostIsolationExceptions is true but no artifact data exists', async () => {
mockArtifactHasData(false); mockArtifactHasData(false);
const { result, waitFor } = setupHook(false, true); const { result } = setupHook(false, true);
await waitFor(() => { await waitFor(() => {
expect(checkArtifactHasData).toHaveBeenCalledWith(mockApiClient()); expect(checkArtifactHasData).toHaveBeenCalledWith(mockApiClient());
@ -57,14 +57,14 @@ describe('useHostIsolationExceptionsAccess', () => {
}); });
test('should set access to false if neither canAccessHostIsolationExceptions nor canReadHostIsolationExceptions is true', async () => { test('should set access to false if neither canAccessHostIsolationExceptions nor canReadHostIsolationExceptions is true', async () => {
const { result, waitFor } = setupHook(false, false); const { result } = setupHook(false, false);
await waitFor(() => { await waitFor(() => {
expect(result.current.hasAccessToHostIsolationExceptions).toBe(false); expect(result.current.hasAccessToHostIsolationExceptions).toBe(false);
}); });
}); });
test('should not call checkArtifactHasData if canAccessHostIsolationExceptions is true', async () => { test('should not call checkArtifactHasData if canAccessHostIsolationExceptions is true', async () => {
const { result, waitFor } = setupHook(true, true); const { result } = setupHook(true, true);
await waitFor(() => { await waitFor(() => {
expect(checkArtifactHasData).not.toHaveBeenCalled(); expect(checkArtifactHasData).not.toHaveBeenCalled();
@ -73,7 +73,7 @@ describe('useHostIsolationExceptionsAccess', () => {
}); });
test('should set loading state correctly while checking access', async () => { test('should set loading state correctly while checking access', async () => {
const { result, waitFor } = setupHook(false, true); const { result } = setupHook(false, true);
expect(result.current.isHostIsolationExceptionsAccessLoading).toBe(true); expect(result.current.isHostIsolationExceptionsAccessLoading).toBe(true);

View file

@ -15,7 +15,7 @@ import {
renderMutation, renderMutation,
} from '../test_utils'; } from '../test_utils';
import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock';
import { act } from '@testing-library/react-hooks'; import { act } from '@testing-library/react';
describe('Update artifact hook', () => { describe('Update artifact hook', () => {
let result: ReturnType<typeof useUpdateArtifact>; let result: ReturnType<typeof useUpdateArtifact>;

View file

@ -27,7 +27,7 @@ jest.mock('@tanstack/react-query', () => {
// FLAKY: https://github.com/elastic/kibana/issues/192435 // FLAKY: https://github.com/elastic/kibana/issues/192435
describe.skip('useGetEndpointDetails hook', () => { describe.skip('useGetEndpointDetails hook', () => {
let renderReactQueryHook: ReactQueryHookRenderer< let renderReactQueryHook: ReactQueryHookRenderer<
Parameters<typeof useGetEndpointDetails>, Parameters<typeof useGetEndpointDetails>[number],
ReturnType<typeof useGetEndpointDetails> ReturnType<typeof useGetEndpointDetails>
>; >;
let http: AppContextTestRender['coreStart']['http']; let http: AppContextTestRender['coreStart']['http'];

View file

@ -7,7 +7,7 @@
import type { AppContextTestRender, ReactQueryHookRenderer } from '../../../common/mock/endpoint'; import type { AppContextTestRender, ReactQueryHookRenderer } from '../../../common/mock/endpoint';
import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; import { createAppRootMockRenderer } from '../../../common/mock/endpoint';
import { useGetEndpointsList } from './use_get_endpoints_list'; import { useGetEndpointsList, PAGING_PARAMS } from './use_get_endpoints_list';
import { HOST_METADATA_LIST_ROUTE } from '../../../../common/endpoint/constants'; import { HOST_METADATA_LIST_ROUTE } from '../../../../common/endpoint/constants';
import { useQuery as _useQuery } from '@tanstack/react-query'; import { useQuery as _useQuery } from '@tanstack/react-query';
import { endpointMetadataHttpMocks } from '../../pages/endpoint_hosts/mocks'; import { endpointMetadataHttpMocks } from '../../pages/endpoint_hosts/mocks';
@ -117,13 +117,15 @@ describe('useGetEndpointsList hook', () => {
it('should also list inactive agents', async () => { it('should also list inactive agents', async () => {
const getApiResponse = apiMocks.responseProvider.metadataList.getMockImplementation(); const getApiResponse = apiMocks.responseProvider.metadataList.getMockImplementation();
const inActiveIndex = [0, 1, 3];
// set a few of the agents as inactive/unenrolled // set a few of the agents as inactive/unenrolled
apiMocks.responseProvider.metadataList.mockImplementation(() => { apiMocks.responseProvider.metadataList.mockImplementation(() => {
if (getApiResponse) { if (getApiResponse) {
return { return {
...getApiResponse(), ...getApiResponse(),
data: getApiResponse().data.map((item, i) => { data: getApiResponse().data.map((item, i) => {
const isInactiveIndex = [0, 1, 3].includes(i); const isInactiveIndex = inActiveIndex.includes(i);
return { return {
...item, ...item,
host_status: isInactiveIndex ? HostStatus.INACTIVE : item.host_status, host_status: isInactiveIndex ? HostStatus.INACTIVE : item.host_status,
@ -154,7 +156,7 @@ describe('useGetEndpointsList hook', () => {
const res = await renderReactQueryHook(() => useGetEndpointsList({ searchString: 'inactive' })); const res = await renderReactQueryHook(() => useGetEndpointsList({ searchString: 'inactive' }));
expect( expect(
res.data?.map((host) => host.name.split('-')[2]).filter((name) => name === 'inactive').length res.data?.map((host) => host.name.split('-')[2]).filter((name) => name === 'inactive').length
).toEqual(3); ).toEqual(inActiveIndex.length);
}); });
it('should only list 50 agents when more than 50 in the metadata list API', async () => { it('should only list 50 agents when more than 50 in the metadata list API', async () => {
@ -192,7 +194,7 @@ describe('useGetEndpointsList hook', () => {
// verify useGetEndpointsList hook returns all 50 agents in the list // verify useGetEndpointsList hook returns all 50 agents in the list
const res = await renderReactQueryHook(() => useGetEndpointsList({ searchString: '' })); const res = await renderReactQueryHook(() => useGetEndpointsList({ searchString: '' }));
expect(res.data?.length).toEqual(50); expect(res.data?.length).toEqual(PAGING_PARAMS.default);
}); });
it('should only list 10 more agents when 50 or more agents are already selected', async () => { it('should only list 10 more agents when 50 or more agents are already selected', async () => {
@ -232,7 +234,7 @@ describe('useGetEndpointsList hook', () => {
const agentIdsToSelect = apiMocks.responseProvider const agentIdsToSelect = apiMocks.responseProvider
.metadataList() .metadataList()
.data.map((d) => d.metadata.agent.id) .data.map((d) => d.metadata.agent.id)
.slice(0, 50); .slice(0, PAGING_PARAMS.default);
// call useGetEndpointsList with all 50 agents selected // call useGetEndpointsList with all 50 agents selected
const res = await renderReactQueryHook(() => const res = await renderReactQueryHook(() =>

View file

@ -18,7 +18,7 @@ type GetEndpointsListResponse = Array<{
selected: boolean; selected: boolean;
}>; }>;
const PAGING_PARAMS = Object.freeze({ export const PAGING_PARAMS = Object.freeze({
default: 50, default: 50,
all: 10000, all: 10000,
}); });

View file

@ -13,7 +13,7 @@ import type {
UseUpdateEndpointPolicyOptions, UseUpdateEndpointPolicyOptions,
UseUpdateEndpointPolicyResult, UseUpdateEndpointPolicyResult,
} from './use_update_endpoint_policy'; } from './use_update_endpoint_policy';
import type { RenderHookResult } from '@testing-library/react-hooks/src/types'; import type { RenderHookResult } from '@testing-library/react';
import { useUpdateEndpointPolicy } from './use_update_endpoint_policy'; import { useUpdateEndpointPolicy } from './use_update_endpoint_policy';
import type { PolicyData } from '../../../../common/endpoint/types'; import type { PolicyData } from '../../../../common/endpoint/types';
import { FleetPackagePolicyGenerator } from '../../../../common/endpoint/data_generators/fleet_package_policy_generator'; import { FleetPackagePolicyGenerator } from '../../../../common/endpoint/data_generators/fleet_package_policy_generator';
@ -37,8 +37,8 @@ describe('When using the `useFetchEndpointPolicyAgentSummary()` hook', () => {
let apiMocks: ReturnType<typeof allFleetHttpMocks>; let apiMocks: ReturnType<typeof allFleetHttpMocks>;
let policy: PolicyData; let policy: PolicyData;
let renderHook: () => RenderHookResult< let renderHook: () => RenderHookResult<
UseUpdateEndpointPolicyOptions, UseUpdateEndpointPolicyResult,
UseUpdateEndpointPolicyResult UseUpdateEndpointPolicyOptions
>; >;
beforeEach(() => { beforeEach(() => {

View file

@ -7,7 +7,7 @@
import { useMutation as _useMutation } from '@tanstack/react-query'; import { useMutation as _useMutation } from '@tanstack/react-query';
import type { AppContextTestRender } from '../../../common/mock/endpoint'; import type { AppContextTestRender } from '../../../common/mock/endpoint';
import type { RenderHookResult } from '@testing-library/react-hooks/src/types'; import type { RenderHookResult } from '@testing-library/react';
import { createAppRootMockRenderer } from '../../../common/mock/endpoint'; import { createAppRootMockRenderer } from '../../../common/mock/endpoint';
import { responseActionsHttpMocks } from '../../mocks/response_actions_http_mocks'; import { responseActionsHttpMocks } from '../../mocks/response_actions_http_mocks';
import { import {
@ -38,7 +38,7 @@ describe('When using the `useSendScanRequest()` hook', () => {
let customOptions: ScanRequestCustomOptions; let customOptions: ScanRequestCustomOptions;
let http: AppContextTestRender['coreStart']['http']; let http: AppContextTestRender['coreStart']['http'];
let apiMocks: ReturnType<typeof responseActionsHttpMocks>; let apiMocks: ReturnType<typeof responseActionsHttpMocks>;
let renderHook: () => RenderHookResult<ScanRequestCustomOptions, UseSendScanRequestResult>; let renderHook: () => RenderHookResult<UseSendScanRequestResult, ScanRequestCustomOptions>;
beforeEach(() => { beforeEach(() => {
const testContext = createAppRootMockRenderer(); const testContext = createAppRootMockRenderer();

View file

@ -5,7 +5,7 @@
* 2.0. * 2.0.
*/ */
import React from 'react'; import React from 'react';
import { renderHook } from '@testing-library/react-hooks'; import { waitFor, renderHook } from '@testing-library/react';
import type { HttpSetup } from '@kbn/core/public'; import type { HttpSetup } from '@kbn/core/public';
import type { CreateExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types'; import type { CreateExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types';
import { coreMock } from '@kbn/core/public/mocks'; import { coreMock } from '@kbn/core/public/mocks';
@ -39,10 +39,10 @@ export const renderQuery = async (
const wrapper = ({ children }: { children: React.ReactNode }): JSX.Element => ( const wrapper = ({ children }: { children: React.ReactNode }): JSX.Element => (
<ReactQueryClientProvider>{children}</ReactQueryClientProvider> <ReactQueryClientProvider>{children}</ReactQueryClientProvider>
); );
const { result: resultHook, waitFor } = renderHook(() => hook(), { const { result: resultHook } = renderHook(() => hook(), {
wrapper, wrapper,
}); });
await waitFor(() => resultHook.current[waitForHook]); await waitFor(() => expect(resultHook.current[waitForHook]).toBeTruthy());
return resultHook.current; return resultHook.current;
}; };
@ -58,3 +58,5 @@ export const renderMutation = async (
}); });
return resultHook.current; return resultHook.current;
}; };
export const renderWrappedHook = renderMutation;