From a458aae83016c1e11f70d82d7c8c8136bc5a324b Mon Sep 17 00:00:00 2001 From: Dennis Oelkers Date: Tue, 7 Jan 2025 11:31:34 +0100 Subject: [PATCH] Adding unique key, adding test case. --- .../events/bulk-replay/BulkEventReplay.tsx | 3 +- .../events/bulk-replay/EventListItem.tsx | 4 +- .../__tests__/BulkEventReplay.test.tsx | 139 +++++++++++++ .../bulk-replay/__tests__/events.fixtures.ts | 182 ++++++++++++++++++ 4 files changed, 325 insertions(+), 3 deletions(-) create mode 100644 graylog2-web-interface/src/components/events/bulk-replay/__tests__/BulkEventReplay.test.tsx create mode 100644 graylog2-web-interface/src/components/events/bulk-replay/__tests__/events.fixtures.ts diff --git a/graylog2-web-interface/src/components/events/bulk-replay/BulkEventReplay.tsx b/graylog2-web-interface/src/components/events/bulk-replay/BulkEventReplay.tsx index 4c539da1925d..75f8c9515bc9 100644 --- a/graylog2-web-interface/src/components/events/bulk-replay/BulkEventReplay.tsx +++ b/graylog2-web-interface/src/components/events/bulk-replay/BulkEventReplay.tsx @@ -151,7 +151,8 @@ const BulkEventReplay = ({ initialEventIds, events: _events, onClose }: Props) = Investigation of {completed}/{total} events completed. {eventIds.map(({ id: eventId, status }) => ( - {event?.message ?? Unknown} - - + + ); diff --git a/graylog2-web-interface/src/components/events/bulk-replay/__tests__/BulkEventReplay.test.tsx b/graylog2-web-interface/src/components/events/bulk-replay/__tests__/BulkEventReplay.test.tsx new file mode 100644 index 000000000000..0572f029477a --- /dev/null +++ b/graylog2-web-interface/src/components/events/bulk-replay/__tests__/BulkEventReplay.test.tsx @@ -0,0 +1,139 @@ +import * as React from 'react'; +import { render, screen, waitFor } from 'wrappedTestingLibrary'; +import userEvent from '@testing-library/user-event'; +import { PluginManifest } from 'graylog-web-plugin/plugin'; + +import type { Event } from 'components/events/events/types'; +import { usePlugin } from 'views/test/testPlugins'; +import MenuItem from 'components/bootstrap/menuitem/MenuItem'; + +import events from './events.fixtures'; + +import BulkEventReplay from '../BulkEventReplay'; + +const initialEventIds = [ + '01JH007XPDF710TPJZT8K2CN3W', + '01JH006340WP7HQ5E7P71Q9HHX', + '01JH0029TS9PX5ED87TZ1RVRT2', +]; + +jest.mock('components/events/bulk-replay/ReplaySearch', () => ({ event }: { event: Event }) => Replaying search for event {event.id}); + +const markEventAsInvestigated = async (eventId: string) => { + const markAsInvestigatedButton = await screen.findByRole('button', { name: new RegExp(`mark event "${eventId}" as investigated`, 'i') }); + + return userEvent.click(markAsInvestigatedButton); +}; + +const removeEvent = async (eventId: string) => { + const removeEventButton = await screen.findByRole('button', { name: new RegExp(`remove event "${eventId}" from list`, 'i') }); + + return userEvent.click(removeEventButton); +}; + +const expectReplayingEvent = (eventId: string) => screen.findByText(new RegExp(`replaying search for event ${eventId}`, 'i')); + +const eventByIndex = (index: number) => events[initialEventIds[index]].event; +const eventMessage = (index: number) => eventByIndex(index).message; + +const SUT = (props: Partial>) => ( + {}} {...props} /> +); + +const bulkAction = jest.fn(); +const testPlugin = new PluginManifest({}, { + 'views.components.eventActions': [{ + key: 'test-bulk-action', + component: ({ events: _events }) => ( + bulkAction(_events)}>Test Action + ), + useCondition: () => true, + isBulk: true, + }], +}); + +describe('BulkEventReplay', () => { + usePlugin(testPlugin); + + it('calls `onClose` when close button is clicked', async () => { + const onClose = jest.fn(); + render(); + const closeButton = await screen.findByRole('button', { name: 'Close' }); + userEvent.click(closeButton); + + await waitFor(() => { + expect(onClose).toHaveBeenCalled(); + }); + }); + + it('renders list of selected events', async () => { + render(); + await screen.findByText(eventMessage(0)); + await screen.findByText(eventMessage(1)); + await screen.findByText(eventMessage(2)); + + await expectReplayingEvent(initialEventIds[0]); + }); + + it('clicking delete button removes event from list', async () => { + render(); + await removeEvent(initialEventIds[0]); + + await screen.findByText(eventMessage(1)); + await expectReplayingEvent(initialEventIds[1]); + + expect(screen.queryByText(eventMessage(0))).not.toBeInTheDocument(); + }); + + it('marking events as investigated jumps to next one', async () => { + render(); + await markEventAsInvestigated(initialEventIds[0]); + + await expectReplayingEvent(initialEventIds[1]); + + await markEventAsInvestigated(initialEventIds[1]); + await expectReplayingEvent(initialEventIds[2]); + + await markEventAsInvestigated(initialEventIds[2]); + await screen.findByText('You are done investigating all events. You can now select a bulk action to apply to all remaining events, or close the page to return to the events list.'); + }); + + it('allows jumping to specific events', async () => { + render(); + await expectReplayingEvent(initialEventIds[0]); + + userEvent.click(await screen.findByText(eventMessage(1))); + + await expectReplayingEvent(initialEventIds[1]); + + userEvent.click(await screen.findByText(eventMessage(0))); + + await expectReplayingEvent(initialEventIds[0]); + }); + + it('skips removed event when jumping to next one', async () => { + render(); + await removeEvent(initialEventIds[1]); + await markEventAsInvestigated(initialEventIds[0]); + await expectReplayingEvent(initialEventIds[2]); + }); + + it('skips event marked as investigated when jumping to next one', async () => { + render(); + await markEventAsInvestigated(initialEventIds[1]); + await markEventAsInvestigated(initialEventIds[0]); + await expectReplayingEvent(initialEventIds[2]); + }); + + it('bulk actions get current list of events', async () => { + render(); + await removeEvent(initialEventIds[1]); + + userEvent.click(await screen.findByRole('button', { name: /bulk actions/i })); + userEvent.click(await screen.findByRole('menuitem', { name: /test action/i })); + + await waitFor(() => { + expect(bulkAction).toHaveBeenCalledWith([eventByIndex(0), eventByIndex(2)]); + }); + }); +}); diff --git a/graylog2-web-interface/src/components/events/bulk-replay/__tests__/events.fixtures.ts b/graylog2-web-interface/src/components/events/bulk-replay/__tests__/events.fixtures.ts new file mode 100644 index 000000000000..6a2cea5389d7 --- /dev/null +++ b/graylog2-web-interface/src/components/events/bulk-replay/__tests__/events.fixtures.ts @@ -0,0 +1,182 @@ +export default { + '01JH006340WP7HQ5E7P71Q9HHX': { + event: { + id: '01JH006340WP7HQ5E7P71Q9HHX', + event_definition_type: 'aggregation-v1', + event_definition_id: '66d719128a7ffa68df52fd7f', + origin_context: null, + timestamp: '2025-01-07T09:05:23.262Z', + timestamp_processing: '2025-01-07T09:05:29.216Z', + timerange_start: '2025-01-07T09:04:23.262Z', + timerange_end: '2025-01-07T09:05:23.262Z', + streams: [ + '000000000000000000000002', + ], + source_streams: [ + '66e962c1b3a0040a1570faf9', + '000000000000000000000001', + ], + message: 'Error Rate: count()=4758.0', + source: 'church', + key_tuple: [ + 'foo', + 'hey there!', + ], + key: 'foo|hey there!', + priority: 3, + scores: { + raw_risk: 6, + normalized_risk: 6, + }, + associated_assets: [], + alert: false, + fields: { + mightyalert: 'foo', + secondkey: 'hey there!', + }, + group_by_fields: {}, + replay_info: { + timerange_start: '2025-01-07T09:04:23.262Z', + timerange_end: '2025-01-07T09:05:23.262Z', + query: '', + streams: [ + '66e962c1b3a0040a1570faf9', + '000000000000000000000001', + ], + filters: [ + { + type: 'inlineQueryString', + title: 'Index Actions', + id: 'd1057daf-f0e0-4159-ae00-e7e340629bb3', + description: '', + queryString: 'action:index', + negation: false, + disabled: false, + }, + ], + }, + }, + index_name: 'gl-events_13', + index_type: 'message', + }, + '01JH0029TS9PX5ED87TZ1RVRT2': { + event: { + id: '01JH0029TS9PX5ED87TZ1RVRT2', + event_definition_type: 'aggregation-v1', + event_definition_id: '66d719128a7ffa68df52fd7f', + origin_context: null, + timestamp: '2025-01-07T09:03:23.262Z', + timestamp_processing: '2025-01-07T09:03:25.017Z', + timerange_start: '2025-01-07T09:02:23.262Z', + timerange_end: '2025-01-07T09:03:23.262Z', + streams: [ + '000000000000000000000002', + ], + source_streams: [ + '66e962c1b3a0040a1570faf9', + '000000000000000000000001', + ], + message: 'Error Rate: count()=4760.0', + source: 'church', + key_tuple: [ + 'foo', + 'hey there!', + ], + key: 'foo|hey there!', + priority: 3, + scores: { + raw_risk: 6, + normalized_risk: 6, + }, + associated_assets: [], + alert: false, + fields: { + mightyalert: 'foo', + secondkey: 'hey there!', + }, + group_by_fields: {}, + replay_info: { + timerange_start: '2025-01-07T09:02:23.262Z', + timerange_end: '2025-01-07T09:03:23.262Z', + query: '', + streams: [ + '66e962c1b3a0040a1570faf9', + '000000000000000000000001', + ], + filters: [ + { + type: 'inlineQueryString', + title: 'Index Actions', + id: 'd1057daf-f0e0-4159-ae00-e7e340629bb3', + description: '', + queryString: 'action:index', + negation: false, + disabled: false, + }, + ], + }, + }, + index_name: 'gl-events_13', + index_type: 'message', + }, + '01JH007XPDF710TPJZT8K2CN3W': { + event: { + id: '01JH007XPDF710TPJZT8K2CN3W', + event_definition_type: 'aggregation-v1', + event_definition_id: '66d719128a7ffa68df52fd7f', + origin_context: null, + timestamp: '2025-01-07T09:06:23.262Z', + timestamp_processing: '2025-01-07T09:06:29.197Z', + timerange_start: '2025-01-07T09:05:23.262Z', + timerange_end: '2025-01-07T09:06:23.262Z', + streams: [ + '000000000000000000000002', + ], + source_streams: [ + '66e962c1b3a0040a1570faf9', + '000000000000000000000001', + ], + message: 'Error Rate: count()=4718.0', + source: 'church', + key_tuple: [ + 'foo', + 'hey there!', + ], + key: 'foo|hey there!', + priority: 3, + scores: { + raw_risk: 6, + normalized_risk: 6, + }, + associated_assets: [], + alert: false, + fields: { + mightyalert: 'foo', + secondkey: 'hey there!', + }, + group_by_fields: {}, + replay_info: { + timerange_start: '2025-01-07T09:05:23.262Z', + timerange_end: '2025-01-07T09:06:23.262Z', + query: '', + streams: [ + '66e962c1b3a0040a1570faf9', + '000000000000000000000001', + ], + filters: [ + { + type: 'inlineQueryString', + title: 'Index Actions', + id: 'd1057daf-f0e0-4159-ae00-e7e340629bb3', + description: '', + queryString: 'action:index', + negation: false, + disabled: false, + }, + ], + }, + }, + index_name: 'gl-events_13', + index_type: 'message', + }, +};