mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[8.8] [Security Solution][Endpoint][Response Actions] Fix table navigation when trays are expanded (#157777) (#158306)
# Backport This will backport the following commits from `main` to `8.8`: - [[Security Solution][Endpoint][Response Actions] Fix table navigation when trays are expanded (#157777)](https://github.com/elastic/kibana/pull/157777) <!--- Backport version: 8.9.7 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Ashokaditya","email":"1849116+ashokaditya@users.noreply.github.com"},"sourceCommit":{"committedDate":"2023-05-23T18:49:02Z","message":"[Security Solution][Endpoint][Response Actions] Fix table navigation when trays are expanded (#157777)\n\n## Summary\r\n\r\nFixes an issue where when an action detail is shown via an expanded tray\r\nitem and the total number of items goes beyond the first page on the\r\nresponse actions history page/flyout, switching between pages while the\r\ntray is open breaks the page.\r\n\r\n- [x] fix paging with trays expanded on a flyout \r\n- [x] fix paging with trays expanded on a page\r\n- [x] ensure when the page is loaded with `?withOutputs=` with action\r\nids from different sets of pages, table paging doesn't break when paged,\r\nand trays show open for the action ids in `?withOutputs=` URL param\r\n- tests:\r\n - [x] page navigation flyout/page view\r\n - [x] page reload with URL params (cypress)\r\n \r\n**flyout**\r\n\r\n\r\n\r\n*page with URL load*\r\n- three actions are open on two different pages\r\n- we re-load page 2 with two open trays and then navigate to page 1 to\r\nsee the third one open\r\n- also re-load page 1; we see the tray open, then navigate to page 2 to\r\nsee the other two trays open.\r\n\r\n\r\n\r\n### Checklist\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios","sha":"9e01dc815f87ccb70f961852f6d7c014e3e43c0b","branchLabelMapping":{"^v8.9.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:Defend Workflows","OLM Sprint","v8.9.0","v8.8.1"],"number":157777,"url":"https://github.com/elastic/kibana/pull/157777","mergeCommit":{"message":"[Security Solution][Endpoint][Response Actions] Fix table navigation when trays are expanded (#157777)\n\n## Summary\r\n\r\nFixes an issue where when an action detail is shown via an expanded tray\r\nitem and the total number of items goes beyond the first page on the\r\nresponse actions history page/flyout, switching between pages while the\r\ntray is open breaks the page.\r\n\r\n- [x] fix paging with trays expanded on a flyout \r\n- [x] fix paging with trays expanded on a page\r\n- [x] ensure when the page is loaded with `?withOutputs=` with action\r\nids from different sets of pages, table paging doesn't break when paged,\r\nand trays show open for the action ids in `?withOutputs=` URL param\r\n- tests:\r\n - [x] page navigation flyout/page view\r\n - [x] page reload with URL params (cypress)\r\n \r\n**flyout**\r\n\r\n\r\n\r\n*page with URL load*\r\n- three actions are open on two different pages\r\n- we re-load page 2 with two open trays and then navigate to page 1 to\r\nsee the third one open\r\n- also re-load page 1; we see the tray open, then navigate to page 2 to\r\nsee the other two trays open.\r\n\r\n\r\n\r\n### Checklist\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios","sha":"9e01dc815f87ccb70f961852f6d7c014e3e43c0b"}},"sourceBranch":"main","suggestedTargetBranches":["8.8"],"targetPullRequestStates":[{"branch":"main","label":"v8.9.0","labelRegex":"^v8.9.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/157777","number":157777,"mergeCommit":{"message":"[Security Solution][Endpoint][Response Actions] Fix table navigation when trays are expanded (#157777)\n\n## Summary\r\n\r\nFixes an issue where when an action detail is shown via an expanded tray\r\nitem and the total number of items goes beyond the first page on the\r\nresponse actions history page/flyout, switching between pages while the\r\ntray is open breaks the page.\r\n\r\n- [x] fix paging with trays expanded on a flyout \r\n- [x] fix paging with trays expanded on a page\r\n- [x] ensure when the page is loaded with `?withOutputs=` with action\r\nids from different sets of pages, table paging doesn't break when paged,\r\nand trays show open for the action ids in `?withOutputs=` URL param\r\n- tests:\r\n - [x] page navigation flyout/page view\r\n - [x] page reload with URL params (cypress)\r\n \r\n**flyout**\r\n\r\n\r\n\r\n*page with URL load*\r\n- three actions are open on two different pages\r\n- we re-load page 2 with two open trays and then navigate to page 1 to\r\nsee the third one open\r\n- also re-load page 1; we see the tray open, then navigate to page 2 to\r\nsee the other two trays open.\r\n\r\n\r\n\r\n### Checklist\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios","sha":"9e01dc815f87ccb70f961852f6d7c014e3e43c0b"}},{"branch":"8.8","label":"v8.8.1","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Ashokaditya <1849116+ashokaditya@users.noreply.github.com>
This commit is contained in:
parent
8c83d40f58
commit
b7375b08d8
11 changed files with 248 additions and 42 deletions
|
@ -32,6 +32,9 @@ export interface IndexedEndpointAndFleetActionsForHostResponse {
|
|||
endpointActionResponsesIndex: string;
|
||||
}
|
||||
|
||||
export interface IndexEndpointAndFleetActionsForHostOptions {
|
||||
numResponseActions?: number;
|
||||
}
|
||||
/**
|
||||
* Indexes a random number of Endpoint (via Fleet) Actions for a given host
|
||||
* (NOTE: ensure that fleet is setup first before calling this loading function)
|
||||
|
@ -43,11 +46,13 @@ export interface IndexedEndpointAndFleetActionsForHostResponse {
|
|||
export const indexEndpointAndFleetActionsForHost = async (
|
||||
esClient: Client,
|
||||
endpointHost: HostMetadata,
|
||||
fleetActionGenerator: FleetActionGenerator = defaultFleetActionGenerator
|
||||
fleetActionGenerator: FleetActionGenerator = defaultFleetActionGenerator,
|
||||
options: IndexEndpointAndFleetActionsForHostOptions = {}
|
||||
): Promise<IndexedEndpointAndFleetActionsForHostResponse> => {
|
||||
const ES_INDEX_OPTIONS = { headers: { 'X-elastic-product-origin': 'fleet' } };
|
||||
const agentId = endpointHost.elastic.agent.id;
|
||||
const total = fleetActionGenerator.randomN(5) + 1; // generate at least one
|
||||
const actionsCount = options.numResponseActions ?? 1;
|
||||
const total = fleetActionGenerator.randomN(5) + actionsCount;
|
||||
const response: IndexedEndpointAndFleetActionsForHostResponse = {
|
||||
actions: [],
|
||||
actionResponses: [],
|
||||
|
|
|
@ -26,6 +26,7 @@ import type {
|
|||
import {
|
||||
deleteIndexedEndpointAndFleetActions,
|
||||
indexEndpointAndFleetActionsForHost,
|
||||
type IndexEndpointAndFleetActionsForHostOptions,
|
||||
} from './index_endpoint_fleet_actions';
|
||||
|
||||
import type {
|
||||
|
@ -88,6 +89,7 @@ export async function indexEndpointHostDocs({
|
|||
enrollFleet,
|
||||
generator,
|
||||
withResponseActions = true,
|
||||
numResponseActions,
|
||||
}: {
|
||||
numDocs: number;
|
||||
client: Client;
|
||||
|
@ -99,6 +101,7 @@ export async function indexEndpointHostDocs({
|
|||
enrollFleet: boolean;
|
||||
generator: EndpointDocGenerator;
|
||||
withResponseActions?: boolean;
|
||||
numResponseActions?: IndexEndpointAndFleetActionsForHostOptions['numResponseActions'];
|
||||
}): Promise<IndexedHostsResponse> {
|
||||
const timeBetweenDocs = 6 * 3600 * 1000; // 6 hours between metadata documents
|
||||
const timestamp = new Date().getTime();
|
||||
|
@ -198,7 +201,10 @@ export async function indexEndpointHostDocs({
|
|||
const actionsResponse = await indexEndpointAndFleetActionsForHost(
|
||||
client,
|
||||
hostMetadata,
|
||||
undefined
|
||||
undefined,
|
||||
{
|
||||
numResponseActions,
|
||||
}
|
||||
);
|
||||
mergeAndAppendArrays(response, actionsResponse);
|
||||
}
|
||||
|
|
|
@ -64,7 +64,8 @@ export async function indexHostsAndAlerts(
|
|||
fleet: boolean,
|
||||
options: TreeOptions = {},
|
||||
DocGenerator: typeof EndpointDocGenerator = EndpointDocGenerator,
|
||||
withResponseActions = true
|
||||
withResponseActions = true,
|
||||
numResponseActions?: number
|
||||
): Promise<IndexedHostsAndAlertsResponse> {
|
||||
const random = seedrandom(seed);
|
||||
const epmEndpointPackage = await getEndpointPackageInfo(kbnClient);
|
||||
|
@ -117,6 +118,7 @@ export async function indexHostsAndAlerts(
|
|||
enrollFleet: fleet,
|
||||
generator,
|
||||
withResponseActions,
|
||||
numResponseActions,
|
||||
});
|
||||
|
||||
mergeAndAppendArrays(response, indexedHosts);
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
EuiToolTip,
|
||||
type HorizontalAlignment,
|
||||
type CriteriaWithPagination,
|
||||
EuiSkeletonText,
|
||||
} from '@elastic/eui';
|
||||
import { euiStyled } from '@kbn/kibana-react-plugin/common';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
@ -55,12 +56,12 @@ interface ExpandedRowMapType {
|
|||
|
||||
const getResponseActionListTableColumns = ({
|
||||
getTestId,
|
||||
itemIdToExpandedRowMap,
|
||||
expandedRowMap,
|
||||
showHostNames,
|
||||
onClickCallback,
|
||||
}: {
|
||||
getTestId: (suffix?: string | undefined) => string | undefined;
|
||||
itemIdToExpandedRowMap: ExpandedRowMapType;
|
||||
expandedRowMap: ExpandedRowMapType;
|
||||
showHostNames: boolean;
|
||||
onClickCallback: (actionListDataItem: ActionListApiResponse['data'][number]) => () => void;
|
||||
}) => {
|
||||
|
@ -245,10 +246,8 @@ const getResponseActionListTableColumns = ({
|
|||
<EuiButtonIcon
|
||||
data-test-subj={getTestId('expand-button')}
|
||||
onClick={onClickCallback(actionListDataItem)}
|
||||
aria-label={
|
||||
itemIdToExpandedRowMap[actionId] ? ARIA_LABELS.collapse : ARIA_LABELS.expand
|
||||
}
|
||||
iconType={itemIdToExpandedRowMap[actionId] ? 'arrowUp' : 'arrowDown'}
|
||||
aria-label={expandedRowMap[actionId] ? ARIA_LABELS.collapse : ARIA_LABELS.expand}
|
||||
iconType={expandedRowMap[actionId] ? 'arrowUp' : 'arrowDown'}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
@ -290,13 +289,13 @@ export const ActionsLogTable = memo<ActionsLogTableProps>(
|
|||
showHostNames,
|
||||
totalItemCount,
|
||||
}) => {
|
||||
const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState<ExpandedRowMapType>({});
|
||||
|
||||
const getTestId = useTestIdGenerator(dataTestSubj);
|
||||
const { pagination: paginationFromUrlParams } = useUrlPagination();
|
||||
const { withOutputs: withOutputsFromUrl } = useActionHistoryUrlParams();
|
||||
|
||||
const getActionIdsWithDetails = useCallback((): string[] => {
|
||||
const [expandedRowMap, setExpandedRowMap] = useState<ExpandedRowMapType>({});
|
||||
|
||||
const actionIdsWithOpenTrays = useMemo((): string[] => {
|
||||
// get the list of action ids from URL params on the history page
|
||||
if (!isFlyout) {
|
||||
return withOutputsFromUrl ?? [];
|
||||
|
@ -309,40 +308,48 @@ export const ActionsLogTable = memo<ActionsLogTableProps>(
|
|||
: [];
|
||||
}, [isFlyout, queryParams.withOutputs, withOutputsFromUrl]);
|
||||
|
||||
const redoOpenTrays = useCallback(() => {
|
||||
if (actionIdsWithOpenTrays.length && items.length) {
|
||||
const openDetails = actionIdsWithOpenTrays.reduce<ExpandedRowMapType>(
|
||||
(idToRowMap, actionId) => {
|
||||
const actionItem = items.find((item) => item.id === actionId);
|
||||
if (!actionItem) {
|
||||
idToRowMap[actionId] = <EuiSkeletonText size="relative" lines={8} />;
|
||||
} else {
|
||||
idToRowMap[actionId] = (
|
||||
<ActionsLogExpandedTray action={actionItem} data-test-subj={dataTestSubj} />
|
||||
);
|
||||
}
|
||||
return idToRowMap;
|
||||
},
|
||||
{}
|
||||
);
|
||||
setExpandedRowMap(openDetails);
|
||||
}
|
||||
}, [actionIdsWithOpenTrays, dataTestSubj, items]);
|
||||
|
||||
// open trays that were open using URL params/ query params
|
||||
useEffect(() => {
|
||||
const actionIdsWithDetails = getActionIdsWithDetails();
|
||||
const openDetails = actionIdsWithDetails.reduce<ExpandedRowMapType>(
|
||||
(idToRowMap, actionId) => {
|
||||
idToRowMap[actionId] = (
|
||||
<ActionsLogExpandedTray
|
||||
action={items.filter((item) => item.id === actionId)[0]}
|
||||
data-test-subj={dataTestSubj}
|
||||
/>
|
||||
);
|
||||
return idToRowMap;
|
||||
},
|
||||
{}
|
||||
);
|
||||
setItemIdToExpandedRowMap(openDetails);
|
||||
}, [dataTestSubj, getActionIdsWithDetails, items, queryParams.withOutputs, withOutputsFromUrl]);
|
||||
redoOpenTrays();
|
||||
}, [redoOpenTrays]);
|
||||
|
||||
const toggleDetails = useCallback(
|
||||
(action: ActionListApiResponse['data'][number]) => {
|
||||
const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap };
|
||||
if (itemIdToExpandedRowMapValues[action.id]) {
|
||||
const expandedRowMapCopy = { ...expandedRowMap };
|
||||
if (expandedRowMapCopy[action.id]) {
|
||||
// close tray
|
||||
delete itemIdToExpandedRowMapValues[action.id];
|
||||
delete expandedRowMapCopy[action.id];
|
||||
} else {
|
||||
// assign the expanded tray content to the map
|
||||
// with action details
|
||||
itemIdToExpandedRowMapValues[action.id] = (
|
||||
expandedRowMapCopy[action.id] = (
|
||||
<ActionsLogExpandedTray action={action} data-test-subj={dataTestSubj} />
|
||||
);
|
||||
}
|
||||
onShowActionDetails(Object.keys(itemIdToExpandedRowMapValues));
|
||||
setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues);
|
||||
onShowActionDetails(Object.keys(expandedRowMapCopy));
|
||||
setExpandedRowMap(expandedRowMapCopy);
|
||||
},
|
||||
[itemIdToExpandedRowMap, onShowActionDetails, dataTestSubj]
|
||||
[expandedRowMap, onShowActionDetails, dataTestSubj]
|
||||
);
|
||||
|
||||
// memoized callback for toggleDetails
|
||||
|
@ -409,11 +416,11 @@ export const ActionsLogTable = memo<ActionsLogTableProps>(
|
|||
() =>
|
||||
getResponseActionListTableColumns({
|
||||
getTestId,
|
||||
itemIdToExpandedRowMap,
|
||||
expandedRowMap,
|
||||
onClickCallback,
|
||||
showHostNames,
|
||||
}),
|
||||
[itemIdToExpandedRowMap, getTestId, onClickCallback, showHostNames]
|
||||
[expandedRowMap, getTestId, onClickCallback, showHostNames]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -425,7 +432,7 @@ export const ActionsLogTable = memo<ActionsLogTableProps>(
|
|||
items={items}
|
||||
columns={columns}
|
||||
itemId="id"
|
||||
itemIdToExpandedRowMap={itemIdToExpandedRowMap}
|
||||
itemIdToExpandedRowMap={expandedRowMap}
|
||||
isExpandable
|
||||
pagination={tablePagination}
|
||||
onChange={onChange}
|
||||
|
|
|
@ -462,6 +462,65 @@ describe('Response actions history', () => {
|
|||
expect(noTrays).toEqual([]);
|
||||
});
|
||||
|
||||
it('should show already expanded trays on page navigation', async () => {
|
||||
// start with two pages worth of response actions
|
||||
// 10 on page 1, 3 on page 2
|
||||
useGetEndpointActionListMock.mockReturnValue({
|
||||
...getBaseMockedActionList(),
|
||||
data: await getActionListMock({ actionCount: 13 }),
|
||||
});
|
||||
render();
|
||||
const { getByTestId, getAllByTestId } = renderResult;
|
||||
|
||||
// on page 1
|
||||
expect(getByTestId(`${testPrefix}-endpointListTableTotal`)).toHaveTextContent(
|
||||
'Showing 1-10 of 13 response actions'
|
||||
);
|
||||
const expandButtonsOnPage1 = getAllByTestId(`${testPrefix}-expand-button`);
|
||||
// expand 2nd, 4th, 6th rows
|
||||
expandButtonsOnPage1.forEach((button, i) => {
|
||||
if ([1, 3, 5].includes(i)) {
|
||||
userEvent.click(button);
|
||||
}
|
||||
});
|
||||
// verify 3 rows are expanded
|
||||
const traysOnPage1 = getAllByTestId(`${testPrefix}-details-tray`);
|
||||
expect(traysOnPage1).toBeTruthy();
|
||||
expect(traysOnPage1.length).toEqual(3);
|
||||
|
||||
// go to 2nd page
|
||||
const page2 = getByTestId('pagination-button-1');
|
||||
userEvent.click(page2);
|
||||
|
||||
// verify on page 2
|
||||
expect(getByTestId(`${testPrefix}-endpointListTableTotal`)).toHaveTextContent(
|
||||
'Showing 11-13 of 13 response actions'
|
||||
);
|
||||
|
||||
// go back to 1st page
|
||||
userEvent.click(getByTestId('pagination-button-0'));
|
||||
// verify on page 1
|
||||
expect(getByTestId(`${testPrefix}-endpointListTableTotal`)).toHaveTextContent(
|
||||
'Showing 1-10 of 13 response actions'
|
||||
);
|
||||
|
||||
const traysOnPage1back = getAllByTestId(`${testPrefix}-details-tray`);
|
||||
const expandButtonsOnPage1back = getAllByTestId(`${testPrefix}-expand-button`);
|
||||
const expandedButtons = expandButtonsOnPage1back.reduce<number[]>((acc, button, i) => {
|
||||
// find expanded rows
|
||||
if (button.getAttribute('aria-label') === 'Collapse') {
|
||||
acc.push(i);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
// verify 3 rows are expanded
|
||||
expect(traysOnPage1back).toBeTruthy();
|
||||
expect(traysOnPage1back.length).toEqual(3);
|
||||
// verify 3 rows that are expanded are the ones from before
|
||||
expect(expandedButtons).toEqual([1, 3, 5]);
|
||||
});
|
||||
|
||||
it('should contain relevant details in each expanded row', async () => {
|
||||
render();
|
||||
const { getAllByTestId } = renderResult;
|
||||
|
|
|
@ -226,7 +226,7 @@ export const ResponseActionsLog = memo<
|
|||
setUrlWithOutputs(actionIds.join());
|
||||
}
|
||||
},
|
||||
[isFlyout, setUrlWithOutputs]
|
||||
[isFlyout, setUrlWithOutputs, setQueryParams]
|
||||
);
|
||||
|
||||
if (error?.body?.statusCode === 404 && error?.body?.message === 'index_not_found_exception') {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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 type { ReturnTypeFromChainable } from '../../types';
|
||||
import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts';
|
||||
import { login } from '../../tasks/login';
|
||||
|
||||
describe('Response actions history page', () => {
|
||||
let endpointData: ReturnTypeFromChainable<typeof indexEndpointHosts>;
|
||||
// let actionData: ReturnTypeFromChainable<typeof indexActionResponses>;
|
||||
|
||||
before(() => {
|
||||
indexEndpointHosts({ numResponseActions: 11 }).then((indexEndpoints) => {
|
||||
endpointData = indexEndpoints;
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
login();
|
||||
});
|
||||
|
||||
after(() => {
|
||||
if (endpointData) {
|
||||
endpointData.cleanup();
|
||||
// @ts-expect-error ignore setting to undefined
|
||||
endpointData = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
it('retains expanded action details on page reload', () => {
|
||||
cy.visit(`/app/security/administration/response_actions_history`);
|
||||
cy.getByTestSubj('response-actions-list-expand-button').eq(3).click(); // 4th row on 1st page
|
||||
cy.getByTestSubj('response-actions-list-details-tray').should('exist');
|
||||
cy.url().should('include', 'withOutputs');
|
||||
|
||||
// navigate to page 2
|
||||
cy.getByTestSubj('pagination-button-1').click();
|
||||
cy.getByTestSubj('response-actions-list-details-tray').should('not.exist');
|
||||
|
||||
// reload with URL params on page 2 with existing URL
|
||||
cy.reload();
|
||||
cy.getByTestSubj('response-actions-list-details-tray').should('not.exist');
|
||||
|
||||
// navigate to page 1
|
||||
cy.getByTestSubj('pagination-button-0').click();
|
||||
cy.getByTestSubj('response-actions-list-details-tray').should('exist');
|
||||
});
|
||||
});
|
|
@ -110,7 +110,14 @@ export const dataLoaders = (
|
|||
|
||||
indexEndpointHosts: async (options: IndexEndpointHostsCyTaskOptions = {}) => {
|
||||
const { kbnClient, esClient } = await stackServicesPromise;
|
||||
const { count: numHosts, version, os, isolation, withResponseActions } = options;
|
||||
const {
|
||||
count: numHosts,
|
||||
version,
|
||||
os,
|
||||
isolation,
|
||||
withResponseActions,
|
||||
numResponseActions,
|
||||
} = options;
|
||||
|
||||
return cyLoadEndpointDataHandler(esClient, kbnClient, {
|
||||
numHosts,
|
||||
|
@ -118,6 +125,7 @@ export const dataLoaders = (
|
|||
os,
|
||||
isolation,
|
||||
withResponseActions,
|
||||
numResponseActions,
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ export interface CyLoadEndpointDataOptions
|
|||
generatorSeed: string;
|
||||
waitUntilTransformed: boolean;
|
||||
withResponseActions: boolean;
|
||||
numResponseActions?: number;
|
||||
isolation: boolean;
|
||||
bothIsolatedAndNormalEndpoints?: boolean;
|
||||
}
|
||||
|
@ -63,6 +64,7 @@ export const cyLoadEndpointDataHandler = async (
|
|||
os,
|
||||
withResponseActions,
|
||||
isolation,
|
||||
numResponseActions,
|
||||
} = options;
|
||||
|
||||
const DocGenerator = EndpointDocGenerator.custom({
|
||||
|
@ -91,7 +93,8 @@ export const cyLoadEndpointDataHandler = async (
|
|||
enableFleetIntegration,
|
||||
undefined,
|
||||
DocGenerator,
|
||||
withResponseActions
|
||||
withResponseActions,
|
||||
numResponseActions
|
||||
);
|
||||
|
||||
if (waitUntilTransformed) {
|
||||
|
|
|
@ -42,7 +42,7 @@ export type ReturnTypeFromChainable<C extends PossibleChainable> = C extends Cyp
|
|||
: never;
|
||||
|
||||
export type IndexEndpointHostsCyTaskOptions = Partial<
|
||||
{ count: number; withResponseActions: boolean } & Pick<
|
||||
{ count: number; withResponseActions: boolean; numResponseActions?: number } & Pick<
|
||||
CyLoadEndpointDataOptions,
|
||||
'version' | 'os' | 'isolation'
|
||||
>
|
||||
|
|
|
@ -343,6 +343,42 @@ describe('Response actions history page', () => {
|
|||
);
|
||||
expect(history.location.search).toEqual(`?startDate=${startDate}&endDate=${endDate}`);
|
||||
});
|
||||
|
||||
it('should read and expand actions using `withOutputs`', () => {
|
||||
const allActionIds = mockUseGetEndpointActionList.data?.data.map((action) => action.id) ?? [];
|
||||
// select 5 actions to show details
|
||||
const actionIdsWithDetails = allActionIds.filter((_, i) => [0, 2, 3, 4, 5].includes(i));
|
||||
reactTestingLibrary.act(() => {
|
||||
// load page 1 but with expanded actions.
|
||||
history.push(
|
||||
`/administration/response_actions_history?withOutputs=${actionIdsWithDetails.join(
|
||||
','
|
||||
)}&page=1&pageSize=10`
|
||||
);
|
||||
});
|
||||
|
||||
const { getByTestId, getAllByTestId } = render();
|
||||
|
||||
// verify on page 1
|
||||
expect(getByTestId(`${testPrefix}-endpointListTableTotal`)).toHaveTextContent(
|
||||
'Showing 1-10 of 43 response actions'
|
||||
);
|
||||
|
||||
const traysOnPage1 = getAllByTestId(`${testPrefix}-details-tray`);
|
||||
const expandButtonsOnPage1 = getAllByTestId(`${testPrefix}-expand-button`);
|
||||
const expandedButtons = expandButtonsOnPage1.reduce<number[]>((acc, button, i) => {
|
||||
// find expanded rows
|
||||
if (button.getAttribute('aria-label') === 'Collapse') {
|
||||
acc.push(i);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
// verify 5 rows are expanded
|
||||
expect(traysOnPage1.length).toEqual(5);
|
||||
// verify 5 rows that are expanded are the ones from before
|
||||
expect(expandedButtons).toEqual([0, 2, 3, 4, 5]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Set selected/set values to URL params', () => {
|
||||
|
@ -442,5 +478,33 @@ describe('Response actions history page', () => {
|
|||
|
||||
expect(history.location.search).toEqual('?endDate=now&startDate=now-15m');
|
||||
});
|
||||
|
||||
it('should set actionIds using `withOutputs` to URL params ', async () => {
|
||||
const allActionIds = mockUseGetEndpointActionList.data?.data.map((action) => action.id) ?? [];
|
||||
const actionIdsWithDetails = allActionIds
|
||||
.reduce<string[]>((acc, e, i) => {
|
||||
if ([0, 1].includes(i)) {
|
||||
acc.push(e);
|
||||
}
|
||||
return acc;
|
||||
}, [])
|
||||
.join()
|
||||
.split(',')
|
||||
.join('%2C');
|
||||
|
||||
render();
|
||||
const { getAllByTestId } = renderResult;
|
||||
|
||||
const expandButtons = getAllByTestId(`${testPrefix}-expand-button`);
|
||||
// expand some rows
|
||||
expandButtons.forEach((button, i) => {
|
||||
if ([0, 1].includes(i)) {
|
||||
userEvent.click(button);
|
||||
}
|
||||
});
|
||||
|
||||
// verify 2 rows are expanded and are the ones from before
|
||||
expect(history.location.search).toEqual(`?withOutputs=${actionIdsWithDetails}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue