[Security Solution][Endpoint][Response Actions] Add tests for hosts filter (#140951)

refs elastic/kibana/pull/140259
fixes  elastic/security-team/issues/4721
This commit is contained in:
Ashokaditya 2022-09-20 15:29:38 +02:00 committed by GitHub
parent e71cb1d06e
commit c92f2b95c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 322 additions and 36 deletions

View file

@ -106,4 +106,19 @@ describe('#actionsLogFiltersFromUrlParams', () => {
users: undefined,
});
});
it('should use set given hosts values to URL params', () => {
expect(
actionsLogFiltersFromUrlParams({
hosts: 'agent-id-1,agent-id-2',
})
).toEqual({
commands: undefined,
endDate: undefined,
hosts: ['agent-id-1', 'agent-id-2'],
startDate: undefined,
statuses: undefined,
users: undefined,
});
});
});

View file

@ -84,7 +84,7 @@ export const actionsLogFiltersFromUrlParams = (
}, [])
: [];
const urlUsers = urlParams.hosts ? String(urlParams.users).split(',').sort() : [];
const urlUsers = urlParams.users ? String(urlParams.users).split(',').sort() : [];
actionsLogFilters.commands = urlCommands.length ? urlCommands : undefined;
actionsLogFilters.hosts = urlHosts.length ? urlHosts : undefined;

View file

@ -17,6 +17,8 @@ import { ResponseActionsLog } from './response_actions_log';
import type { ActionListApiResponse } from '../../../../common/endpoint/types';
import { MANAGEMENT_PATH } from '../../../../common/constants';
import { getActionListMock } from './mocks';
import { useGetEndpointsList } from '../../hooks/endpoint/use_get_endpoints_list';
import uuid from 'uuid';
let mockUseGetEndpointActionList: {
isFetched?: boolean;
@ -107,6 +109,10 @@ jest.mock('@kbn/kibana-react-plugin/public', () => {
};
});
jest.mock('../../hooks/endpoint/use_get_endpoints_list');
const mockUseGetEndpointsList = useGetEndpointsList as jest.Mock;
describe('Response Actions Log', () => {
const testPrefix = 'response-actions-list';
@ -138,6 +144,19 @@ describe('Response Actions Log', () => {
...baseMockedActionList,
data: await getActionListMock({ actionCount: 13 }),
};
mockUseGetEndpointsList.mockReturnValue({
data: Array.from({ length: 50 }).map(() => {
const id = uuid.v4();
return {
id,
name: `Host-${id.slice(0, 8)}`,
};
}),
page: 0,
pageSize: 50,
total: 50,
});
});
afterEach(() => {
@ -172,8 +191,10 @@ describe('Response Actions Log', () => {
it('should show table when there is data', async () => {
render();
expect(renderResult.getByTestId(`${testPrefix}-table-view`)).toBeTruthy();
expect(renderResult.getByTestId(`${testPrefix}-endpointListTableTotal`)).toHaveTextContent(
const { getByTestId } = renderResult;
expect(getByTestId(`${testPrefix}-table-view`)).toBeTruthy();
expect(getByTestId(`${testPrefix}-endpointListTableTotal`)).toHaveTextContent(
'Showing 1-10 of 13 response actions'
);
});
@ -271,15 +292,16 @@ describe('Response Actions Log', () => {
it('should paginate table when there is data', async () => {
render();
const { getByTestId } = renderResult;
expect(renderResult.getByTestId(`${testPrefix}-table-view`)).toBeTruthy();
expect(renderResult.getByTestId(`${testPrefix}-endpointListTableTotal`)).toHaveTextContent(
expect(getByTestId(`${testPrefix}-table-view`)).toBeTruthy();
expect(getByTestId(`${testPrefix}-endpointListTableTotal`)).toHaveTextContent(
'Showing 1-10 of 13 response actions'
);
const page2 = renderResult.getByTestId('pagination-button-1');
const page2 = getByTestId('pagination-button-1');
userEvent.click(page2);
expect(renderResult.getByTestId(`${testPrefix}-endpointListTableTotal`)).toHaveTextContent(
expect(getByTestId(`${testPrefix}-endpointListTableTotal`)).toHaveTextContent(
'Showing 11-13 of 13 response actions'
);
});
@ -291,9 +313,10 @@ describe('Response Actions Log', () => {
};
render();
const { getByTestId } = renderResult;
expect(renderResult.getByTestId(`${testPrefix}-table-view`)).toBeTruthy();
expect(renderResult.getByTestId(`${testPrefix}-endpointListTableTotal`)).toHaveTextContent(
expect(getByTestId(`${testPrefix}-table-view`)).toBeTruthy();
expect(getByTestId(`${testPrefix}-endpointListTableTotal`)).toHaveTextContent(
'Showing 1-10 of 33 response actions'
);
@ -334,27 +357,29 @@ describe('Response Actions Log', () => {
it('should expand each row to show details', async () => {
render();
const { getAllByTestId, queryAllByTestId } = renderResult;
const expandButtons = renderResult.getAllByTestId(`${testPrefix}-expand-button`);
const expandButtons = getAllByTestId(`${testPrefix}-expand-button`);
expandButtons.map((button) => userEvent.click(button));
const trays = renderResult.getAllByTestId(`${testPrefix}-details-tray`);
const trays = getAllByTestId(`${testPrefix}-details-tray`);
expect(trays).toBeTruthy();
expect(trays.length).toEqual(13);
expandButtons.map((button) => userEvent.click(button));
const noTrays = renderResult.queryAllByTestId(`${testPrefix}-details-tray`);
const noTrays = queryAllByTestId(`${testPrefix}-details-tray`);
expect(noTrays).toEqual([]);
});
it('should refresh data when autoRefresh is toggled on', async () => {
render();
const { getByTestId } = renderResult;
const quickMenuButton = renderResult.getByTestId('superDatePickerToggleQuickMenuButton');
const quickMenuButton = getByTestId('superDatePickerToggleQuickMenuButton');
userEvent.click(quickMenuButton);
await waitForEuiPopoverOpen();
const toggle = renderResult.getByTestId('superDatePickerToggleRefreshButton');
const intervalInput = renderResult.getByTestId('superDatePickerRefreshIntervalInput');
const toggle = getByTestId('superDatePickerToggleRefreshButton');
const intervalInput = getByTestId('superDatePickerRefreshIntervalInput');
userEvent.click(toggle);
reactTestingLibrary.fireEvent.change(intervalInput, { target: { value: 1 } });
@ -374,8 +399,10 @@ describe('Response Actions Log', () => {
it('should set date picker with relative dates', async () => {
render();
const quickMenuButton = renderResult.getByTestId('superDatePickerToggleQuickMenuButton');
const startDatePopoverButton = renderResult.getByTestId(`superDatePickerShowDatesButton`);
const { getByTestId } = renderResult;
const quickMenuButton = getByTestId('superDatePickerToggleQuickMenuButton');
const startDatePopoverButton = getByTestId(`superDatePickerShowDatesButton`);
// shows 24 hours at first
expect(startDatePopoverButton).toHaveTextContent('Last 24 hours');
@ -383,16 +410,18 @@ describe('Response Actions Log', () => {
// pick another relative date
userEvent.click(quickMenuButton);
await waitForEuiPopoverOpen();
userEvent.click(renderResult.getByTestId('superDatePickerCommonlyUsed_Last_15 minutes'));
userEvent.click(getByTestId('superDatePickerCommonlyUsed_Last_15 minutes'));
expect(startDatePopoverButton).toHaveTextContent('Last 15 minutes');
});
});
describe('Action status ', () => {
const expandRows = () => {
const expandButtons = renderResult.getAllByTestId(`${testPrefix}-expand-button`);
const { getAllByTestId } = renderResult;
const expandButtons = getAllByTestId(`${testPrefix}-expand-button`);
expandButtons.map((button) => userEvent.click(button));
const outputs = renderResult.getAllByTestId(`${testPrefix}-details-tray-output`);
const outputs = getAllByTestId(`${testPrefix}-details-tray-output`);
return outputs;
};
@ -468,12 +497,12 @@ describe('Response Actions Log', () => {
});
describe('Actions filter', () => {
const filterPrefix = '-actions-filter';
const filterPrefix = 'actions-filter';
it('should have a search bar', () => {
render();
userEvent.click(renderResult.getByTestId(`${testPrefix}${filterPrefix}-popoverButton`));
const searchBar = renderResult.getByTestId(`${testPrefix}${filterPrefix}-search`);
userEvent.click(renderResult.getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`));
const searchBar = renderResult.getByTestId(`${testPrefix}-${filterPrefix}-search`);
expect(searchBar).toBeTruthy();
expect(searchBar.querySelector('input')?.getAttribute('placeholder')).toEqual(
'Search actions'
@ -482,8 +511,10 @@ describe('Response Actions Log', () => {
it('should show a list of actions when opened', () => {
render();
userEvent.click(renderResult.getByTestId(`${testPrefix}${filterPrefix}-popoverButton`));
const filterList = renderResult.getByTestId(`${testPrefix}${filterPrefix}-popoverList`);
const { getByTestId } = renderResult;
userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`));
const filterList = getByTestId(`${testPrefix}-${filterPrefix}-popoverList`);
expect(filterList).toBeTruthy();
expect(filterList.querySelectorAll('ul>li').length).toEqual(5);
expect(
@ -493,21 +524,23 @@ describe('Response Actions Log', () => {
it('should have `clear all` button `disabled` when no selected values', () => {
render();
userEvent.click(renderResult.getByTestId(`${testPrefix}${filterPrefix}-popoverButton`));
const clearAllButton = renderResult.getByTestId(
`${testPrefix}${filterPrefix}-clearAllButton`
);
const { getByTestId } = renderResult;
userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`));
const clearAllButton = getByTestId(`${testPrefix}-${filterPrefix}-clearAllButton`);
expect(clearAllButton.hasAttribute('disabled')).toBeTruthy();
});
});
describe('Statuses filter', () => {
const filterPrefix = '-statuses-filter';
const filterPrefix = 'statuses-filter';
it('should show a list of statuses when opened', () => {
render();
userEvent.click(renderResult.getByTestId(`${testPrefix}${filterPrefix}-popoverButton`));
const filterList = renderResult.getByTestId(`${testPrefix}${filterPrefix}-popoverList`);
const { getByTestId } = renderResult;
userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`));
const filterList = getByTestId(`${testPrefix}-${filterPrefix}-popoverList`);
expect(filterList).toBeTruthy();
expect(filterList.querySelectorAll('ul>li').length).toEqual(3);
expect(
@ -517,11 +550,172 @@ describe('Response Actions Log', () => {
it('should have `clear all` button `disabled` when no selected values', () => {
render();
userEvent.click(renderResult.getByTestId(`${testPrefix}${filterPrefix}-popoverButton`));
userEvent.click(renderResult.getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`));
const clearAllButton = renderResult.getByTestId(
`${testPrefix}${filterPrefix}-clearAllButton`
`${testPrefix}-${filterPrefix}-clearAllButton`
);
expect(clearAllButton.hasAttribute('disabled')).toBeTruthy();
});
});
describe('Hosts Filter', () => {
const filterPrefix = 'hosts-filter';
it('should show hosts filter for non-flyout or page', () => {
render({ showHostNames: true });
expect(renderResult.getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`)).toBeTruthy();
});
it('should have a search bar ', () => {
render({ showHostNames: true });
const { getByTestId } = renderResult;
userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`));
const searchBar = getByTestId(`${testPrefix}-${filterPrefix}-search`);
expect(searchBar).toBeTruthy();
expect(searchBar.querySelector('input')?.getAttribute('placeholder')).toEqual('Search hosts');
});
it('should show a list of host names when opened', () => {
render({ showHostNames: true });
const { getByTestId } = renderResult;
const popoverButton = getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`);
userEvent.click(popoverButton);
const filterList = getByTestId(`${testPrefix}-${filterPrefix}-popoverList`);
expect(filterList).toBeTruthy();
expect(filterList.querySelectorAll('ul>li').length).toEqual(9);
expect(
getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`).querySelector(
'.euiNotificationBadge'
)?.textContent
).toEqual('50');
});
it('should not pin selected host names to the top when opened and selections are being made', () => {
render({ showHostNames: true });
const { getByTestId, getAllByTestId } = renderResult;
const popoverButton = getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`);
userEvent.click(popoverButton);
const allFilterOptions = getAllByTestId(`${filterPrefix}-option`);
// click 3 options skip alternates
allFilterOptions.forEach((option, i) => {
if ([1, 3, 5].includes(i)) {
option.style.pointerEvents = 'all';
userEvent.click(option);
}
});
const filterList = renderResult.getByTestId(`${testPrefix}-${filterPrefix}-popoverList`);
const selectedFilterOptions = Array.from(filterList.querySelectorAll('ul>li')).reduce<
number[]
>((acc, curr, i) => {
if (curr.getAttribute('aria-checked') === 'true') {
acc.push(i);
}
return acc;
}, []);
expect(selectedFilterOptions).toEqual([1, 3, 5]);
});
it('should pin selected host names to the top when opened after selections were made', () => {
render({ showHostNames: true });
const { getByTestId, getAllByTestId } = renderResult;
const popoverButton = getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`);
userEvent.click(popoverButton);
const allFilterOptions = getAllByTestId(`${filterPrefix}-option`);
// click 3 options skip alternates
allFilterOptions.forEach((option, i) => {
if ([1, 3, 5].includes(i)) {
option.style.pointerEvents = 'all';
userEvent.click(option);
}
});
// close
userEvent.click(popoverButton);
// re-open
userEvent.click(popoverButton);
const filterList = renderResult.getByTestId(`${testPrefix}-${filterPrefix}-popoverList`);
const selectedFilterOptions = Array.from(filterList.querySelectorAll('ul>li')).reduce<
number[]
>((acc, curr, i) => {
if (curr.getAttribute('aria-checked') === 'true') {
acc.push(i);
}
return acc;
}, []);
expect(selectedFilterOptions).toEqual([0, 1, 2]);
});
it('should not pin newly selected items with already pinned items', () => {
render({ showHostNames: true });
const { getByTestId, getAllByTestId } = renderResult;
const popoverButton = getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`);
userEvent.click(popoverButton);
const allFilterOptions = getAllByTestId(`${filterPrefix}-option`);
// click 3 options skip alternates
allFilterOptions.forEach((option, i) => {
if ([1, 3, 5].includes(i)) {
option.style.pointerEvents = 'all';
userEvent.click(option);
}
});
// close
userEvent.click(popoverButton);
// re-open
userEvent.click(popoverButton);
const newSetAllFilterOptions = getAllByTestId(`${filterPrefix}-option`);
// click new options
newSetAllFilterOptions.forEach((option, i) => {
if ([4, 6, 8].includes(i)) {
option.style.pointerEvents = 'all';
userEvent.click(option);
}
});
const filterList = renderResult.getByTestId(`${testPrefix}-${filterPrefix}-popoverList`);
const selectedFilterOptions = Array.from(filterList.querySelectorAll('ul>li')).reduce<
number[]
>((acc, curr, i) => {
if (curr.getAttribute('aria-checked') === 'true') {
acc.push(i);
}
return acc;
}, []);
expect(selectedFilterOptions).toEqual([0, 1, 2, 4, 6, 8]);
});
it('should update the selected options count correctly', () => {
render({ showHostNames: true });
const { getByTestId, getAllByTestId } = renderResult;
const popoverButton = getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`);
userEvent.click(popoverButton);
const allFilterOptions = getAllByTestId(`${filterPrefix}-option`);
// click 3 options skip alternates
allFilterOptions.forEach((option, i) => {
if ([0, 2, 4, 6].includes(i)) {
option.style.pointerEvents = 'all';
userEvent.click(option);
}
});
expect(popoverButton.textContent).toEqual('Hosts4');
});
});
});

View file

@ -17,6 +17,7 @@ import { ResponseActionsListPage } from './response_actions_list_page';
import type { ActionListApiResponse } from '../../../../../common/endpoint/types';
import { MANAGEMENT_PATH } from '../../../../../common/constants';
import { getActionListMock } from '../../../components/endpoint_response_actions_list/mocks';
import { useGetEndpointsList } from '../../../hooks/endpoint/use_get_endpoints_list';
let mockUseGetEndpointActionList: {
isFetched?: boolean;
@ -107,6 +108,9 @@ jest.mock('@kbn/kibana-react-plugin/public', () => {
};
});
jest.mock('../../../hooks/endpoint/use_get_endpoints_list');
const mockUseGetEndpointsList = useGetEndpointsList as jest.Mock;
describe('Action history page', () => {
const testPrefix = 'response-actions-list';
@ -135,6 +139,18 @@ describe('Action history page', () => {
...baseMockedActionList,
data: await getActionListMock({ actionCount: 43 }),
};
mockUseGetEndpointsList.mockReturnValue({
data: Array.from({ length: 10 }).map((_, i) => {
return {
id: `agent-id-${i}`,
name: `Host-name-${i}`,
};
}),
page: 0,
pageSize: 50,
total: 10,
});
});
afterEach(() => {
@ -180,6 +196,50 @@ describe('Action history page', () => {
expect(history.location.search).toEqual('?commands=release,processes');
});
it('should read and set hosts filter values from URL params', () => {
mockUseGetEndpointsList.mockReturnValue({
data: Array.from({ length: 10 }).map((_, i) => {
return {
id: `agent-id-${i}`,
name: `Host-name-${i}`,
selected: [0, 1, 3, 5].includes(i) ? true : false,
};
}),
page: 0,
pageSize: 50,
total: 10,
});
const filterPrefix = 'hosts-filter';
reactTestingLibrary.act(() => {
history.push(
'/administration/action_history?hosts=agent-id-1,agent-id-2,agent-id-4,agent-id-5'
);
});
render();
const { getAllByTestId, getByTestId } = renderResult;
userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`));
const allFilterOptions = getAllByTestId(`${filterPrefix}-option`);
const selectedFilterOptions = allFilterOptions.reduce<string[]>((acc, option) => {
if (option.getAttribute('aria-checked') === 'true') {
acc.push(option.textContent?.split(' - ')[0] as string);
}
return acc;
}, []);
expect(selectedFilterOptions.length).toEqual(4);
expect(selectedFilterOptions).toEqual([
'Host-name-0',
'Host-name-1',
'Host-name-3',
'Host-name-5',
]);
expect(history.location.search).toEqual('?hosts=agent-id-1,agent-id-2,agent-id-4,agent-id-5');
});
it('should read and set status filter values from URL params', () => {
const filterPrefix = 'statuses-filter';
reactTestingLibrary.act(() => {
@ -203,7 +263,7 @@ describe('Action history page', () => {
expect(history.location.search).toEqual('?statuses=pending,failed');
});
// TODO: add tests for hosts and users when those filters are added
// TODO: add tests for users when that filter is added
it('should read and set relative date ranges filter values from URL params', () => {
reactTestingLibrary.act(() => {
@ -279,6 +339,23 @@ describe('Action history page', () => {
);
});
it('should set selected hosts filter options to URL params ', () => {
const filterPrefix = 'hosts-filter';
render();
const { getAllByTestId, getByTestId } = renderResult;
userEvent.click(getByTestId(`${testPrefix}-${filterPrefix}-popoverButton`));
const allFilterOptions = getAllByTestId(`${filterPrefix}-option`);
allFilterOptions.forEach((option, i) => {
if ([0, 1, 2].includes(i)) {
option.style.pointerEvents = 'all';
userEvent.click(option);
}
});
expect(history.location.search).toEqual('?hosts=agent-id-0%2Cagent-id-1%2Cagent-id-2');
});
it('should set selected status filter options to URL params ', () => {
const filterPrefix = 'statuses-filter';
render();
@ -294,7 +371,7 @@ describe('Action history page', () => {
expect(history.location.search).toEqual('?statuses=failed%2Cpending%2Csuccessful');
});
// TODO: add tests for hosts and users when those filters are added
// TODO: add tests for users when that filter is added
it('should set selected relative date range filter options to URL params ', async () => {
const { getByTestId } = render();