diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts
index a16b88f649618..ffb9e9748d9c1 100644
--- a/x-pack/plugins/security_solution/common/experimental_features.ts
+++ b/x-pack/plugins/security_solution/common/experimental_features.ts
@@ -183,10 +183,6 @@ export const allowedExperimentalValues = Object.freeze({
*
*/
timelineEsqlTabDisabled: false,
- /*
- * Disables experimental Discover components, UnifiedFieldList and UnifiedDataTable in Timeline.
- */
- unifiedComponentsInTimelineDisabled: false,
/*
* Disables date pickers and sourcerer in analyzer if needed.
diff --git a/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.test.tsx b/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.test.tsx
index 5b48b4286fe63..22320b2a9a3db 100644
--- a/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.test.tsx
@@ -14,7 +14,6 @@ import { TimelineTabs } from '../../../../common/types';
import { HeaderActions } from './header_actions';
import { timelineActions } from '../../../timelines/store';
import { getColumnHeader } from '../../../timelines/components/timeline/body/column_headers/helpers';
-import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features';
jest.mock('../../hooks/use_experimental_features', () => ({
useIsExperimentalFeatureEnabled: jest.fn(),
@@ -141,51 +140,23 @@ describe('HeaderActions', () => {
});
});
- describe('conditional components based on unifiedComponentsInTimelineDisabled', () => {
- describe('when unifiedComponentsInTimelineDisabled is false', () => {
- beforeEach(() => {
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
- });
- it('should not show the event renderer settings', () => {
- const result = render(
-
-
-
- );
- expect(result.queryByTestId('show-row-renderers-gear')).toBeNull();
- });
-
- it('should not show the sorting settings', () => {
- const result = render(
-
-
-
- );
- expect(result.queryByTestId('timeline-sorting-fields')).toBeNull();
- });
+ describe('Controls', () => {
+ it('should not show the event renderer settings', () => {
+ const result = render(
+
+
+
+ );
+ expect(result.queryByTestId('show-row-renderers-gear')).toBeNull();
});
- describe('when unifiedComponentsInTimelineDisabled is true', () => {
- beforeEach(() => {
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
- });
- it('should show the event renderer settings', () => {
- const result = render(
-
-
-
- );
- result.getByTestId('show-row-renderers-gear');
- });
-
- it('should show the sorting settings', () => {
- const result = render(
-
-
-
- );
- result.getByTestId('timeline-sorting-fields');
- });
+ it('should not show the sorting settings', () => {
+ const result = render(
+
+
+
+ );
+ expect(result.queryByTestId('timeline-sorting-fields')).toBeNull();
});
});
});
diff --git a/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.tsx b/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.tsx
index 7dabf3014da95..1341cb72104d8 100644
--- a/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.tsx
@@ -6,13 +6,12 @@
*/
import React, { useMemo, useCallback, memo } from 'react';
-import type { EuiDataGridSorting, EuiDataGridSchemaDetector } from '@elastic/eui';
-import { EuiButtonIcon, EuiToolTip, useDataGridColumnSorting, EuiCheckbox } from '@elastic/eui';
+import { EuiButtonIcon, EuiToolTip, EuiCheckbox } from '@elastic/eui';
import { useDispatch } from 'react-redux';
import styled from 'styled-components';
-import type { HeaderActionProps, SortDirection } from '../../../../common/types';
-import { TimelineTabs, TimelineId } from '../../../../common/types';
+import type { HeaderActionProps } from '../../../../common/types';
+import { TimelineId } from '../../../../common/types';
import { isFullScreen } from '../../../timelines/components/timeline/body/column_headers';
import { isActiveTimeline } from '../../../helpers';
import { getColumnHeader } from '../../../timelines/components/timeline/body/column_headers/helpers';
@@ -21,28 +20,12 @@ import { useGlobalFullScreen, useTimelineFullScreen } from '../../containers/use
import { useKibana } from '../../lib/kibana';
import { DEFAULT_ACTION_BUTTON_WIDTH } from '.';
import { EventsTh, EventsThContent } from '../../../timelines/components/timeline/styles';
-import { StatefulRowRenderersBrowser } from '../../../timelines/components/row_renderers_browser';
import { EXIT_FULL_SCREEN } from '../exit_full_screen/translations';
import { EventsSelect } from '../../../timelines/components/timeline/body/column_headers/events_select';
import * as i18n from './translations';
-import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features';
import { useDeepEqualSelector } from '../../hooks/use_selector';
import { selectTimelineById } from '../../../timelines/store/selectors';
-const SortingColumnsContainer = styled.div`
- button {
- color: ${({ theme }) => theme.eui.euiColorPrimary};
- }
-
- .euiPopover .euiButtonEmpty {
- padding: 0;
-
- .euiButtonEmpty__text {
- display: none;
- }
- }
-`;
-
const FieldBrowserContainer = styled.div`
.euiToolTipAnchor {
.euiButtonContent {
@@ -66,23 +49,15 @@ const ActionsContainer = styled.div`
display: flex;
`;
-// Defined statically to reduce rerenders
-const emptySchema = {};
-const emptySchemaDetectors: EuiDataGridSchemaDetector[] = [];
-
const HeaderActionsComponent: React.FC = memo(
({
- width,
browserFields,
columnHeaders,
- isEventViewer = false,
isSelectAllChecked,
onSelectAll,
showEventsSelect,
showSelectAllCheckbox,
showFullScreenToggle = true,
- sort,
- tabType,
timelineId,
fieldBrowserOptions,
}) => {
@@ -91,10 +66,6 @@ const HeaderActionsComponent: React.FC = memo(
const { timelineFullScreen, setTimelineFullScreen } = useTimelineFullScreen();
const dispatch = useDispatch();
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
-
const { defaultColumns } = useDeepEqualSelector((state) =>
selectTimelineById(state, timelineId)
);
@@ -129,57 +100,6 @@ const HeaderActionsComponent: React.FC = memo(
[onSelectAll]
);
- const onSortColumns = useCallback(
- (cols: EuiDataGridSorting['columns']) =>
- dispatch(
- timelineActions.updateSort({
- id: timelineId,
- sort: cols.map(({ id, direction }) => {
- const columnHeader = columnHeaders.find((ch) => ch.id === id);
- const columnType = columnHeader?.type ?? '';
- const esTypes = columnHeader?.esTypes ?? [];
-
- return {
- columnId: id,
- columnType,
- esTypes,
- sortDirection: direction as SortDirection,
- };
- }),
- })
- ),
- [columnHeaders, dispatch, timelineId]
- );
-
- const sortedColumns = useMemo(
- () => ({
- onSort: onSortColumns,
- columns:
- sort?.map<{ id: string; direction: 'asc' | 'desc' }>(({ columnId, sortDirection }) => ({
- id: columnId,
- direction: sortDirection as 'asc' | 'desc',
- })) ?? [],
- }),
- [onSortColumns, sort]
- );
- const displayValues = useMemo(
- () =>
- columnHeaders?.reduce((acc, ch) => ({ ...acc, [ch.id]: ch.displayAsText ?? ch.id }), {}) ??
- {},
- [columnHeaders]
- );
-
- const myColumns = useMemo(
- () =>
- columnHeaders?.map(({ aggregatable, displayAsText, id, type }) => ({
- id,
- isSortable: aggregatable,
- displayAsText,
- schema: type,
- })) ?? [],
- [columnHeaders]
- );
-
const onResetColumns = useCallback(() => {
dispatch(timelineActions.updateColumns({ id: timelineId, columns: defaultColumns }));
}, [defaultColumns, dispatch, timelineId]);
@@ -206,14 +126,6 @@ const HeaderActionsComponent: React.FC = memo(
[columnHeaders, dispatch, timelineId, defaultColumns]
);
- const ColumnSorting = useDataGridColumnSorting({
- columns: myColumns,
- sorting: sortedColumns,
- schema: emptySchema,
- schemaDetectors: emptySchemaDetectors,
- displayValues,
- });
-
return (
{showSelectAllCheckbox && (
@@ -242,11 +154,6 @@ const HeaderActionsComponent: React.FC = memo(
)}
- {unifiedComponentsInTimelineDisabled && (
-
-
-
- )}
{showFullScreenToggle && (
@@ -275,15 +182,6 @@ const HeaderActionsComponent: React.FC = memo(
)}
- {tabType !== TimelineTabs.eql && unifiedComponentsInTimelineDisabled && (
-
-
-
- {ColumnSorting}
-
-
-
- )}
{showEventsSelect && (
diff --git a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts
index d885787c14b12..5e032a9a97d07 100644
--- a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts
+++ b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts
@@ -19,10 +19,6 @@ import { URL_PARAM_KEY } from '../use_url_state';
import { useIsExperimentalFeatureEnabled } from '../use_experimental_features';
export const useInitTimelineFromUrlParam = () => {
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
-
const isEsqlTabDisabled = useIsExperimentalFeatureEnabled('timelineEsqlTabDisabled');
const queryTimelineById = useQueryTimelineById();
@@ -43,11 +39,10 @@ export const useInitTimelineFromUrlParam = () => {
timelineId: initialState.id,
openTimeline: initialState.isOpen,
savedSearchId: initialState.savedSearchId,
- unifiedComponentsInTimelineDisabled,
});
}
},
- [isEsqlTabDisabled, queryTimelineById, unifiedComponentsInTimelineDisabled]
+ [isEsqlTabDisabled, queryTimelineById]
);
useEffect(() => {
diff --git a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_query_timeline_by_id_on_url_change.ts b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_query_timeline_by_id_on_url_change.ts
index d4df6d67504d6..60d4ff10b8de1 100644
--- a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_query_timeline_by_id_on_url_change.ts
+++ b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_query_timeline_by_id_on_url_change.ts
@@ -21,7 +21,6 @@ import {
getQueryStringFromLocation,
} from '../../utils/global_query_string/helpers';
import { URL_PARAM_KEY } from '../use_url_state';
-import { useIsExperimentalFeatureEnabled } from '../use_experimental_features';
/**
* After the initial load of the security solution, timeline is not updated when the timeline URL search value is changed
@@ -42,10 +41,6 @@ export const useQueryTimelineByIdOnUrlChange = () => {
const oldSearch = usePrevious(search);
const timelineIdFromReduxStore = flyoutTimeline?.savedObjectId ?? '';
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
-
const [previousTimeline, currentTimeline] = useMemo(() => {
const oldUrlStateString = getQueryStringKeyValue({
urlKey: URL_PARAM_KEY.timeline,
@@ -74,18 +69,9 @@ export const useQueryTimelineByIdOnUrlChange = () => {
graphEventId,
timelineId: newId,
openTimeline: true,
- unifiedComponentsInTimelineDisabled,
});
}
- }, [
- timelineIdFromReduxStore,
- oldId,
- newId,
- activeTab,
- graphEventId,
- queryTimelineById,
- unifiedComponentsInTimelineDisabled,
- ]);
+ }, [timelineIdFromReduxStore, oldId, newId, activeTab, graphEventId, queryTimelineById]);
};
export const getQueryStringKeyValue = ({ search, urlKey }: { search: string; urlKey: string }) =>
diff --git a/x-pack/plugins/security_solution/public/common/utils/timeline/use_timeline_click.tsx b/x-pack/plugins/security_solution/public/common/utils/timeline/use_timeline_click.tsx
index db42ca9b774bd..f5e866f240e16 100644
--- a/x-pack/plugins/security_solution/public/common/utils/timeline/use_timeline_click.tsx
+++ b/x-pack/plugins/security_solution/public/common/utils/timeline/use_timeline_click.tsx
@@ -8,25 +8,19 @@
import { useCallback } from 'react';
import { useQueryTimelineById } from '../../../timelines/components/open_timeline/helpers';
import type { TimelineErrorCallback } from '../../../timelines/components/open_timeline/types';
-import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features';
export const useTimelineClick = () => {
const queryTimelineById = useQueryTimelineById();
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
-
const handleTimelineClick = useCallback(
(timelineId: string, onError: TimelineErrorCallback, graphEventId?: string) => {
queryTimelineById({
graphEventId,
timelineId,
onError,
- unifiedComponentsInTimelineDisabled,
});
},
- [queryTimelineById, unifiedComponentsInTimelineDisabled]
+ [queryTimelineById]
);
return handleTimelineClick;
diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx
index a68d773468e75..a67eb08496dd0 100644
--- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx
@@ -19,7 +19,6 @@ import { useApi } from '@kbn/securitysolution-list-hooks';
import type { Filter } from '@kbn/es-query';
import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs';
import { isEmpty } from 'lodash';
-import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import { createHistoryEntry } from '../../../../common/utils/global_query_string/helpers';
import { useKibana } from '../../../../common/lib/kibana';
import { TimelineId } from '../../../../../common/types/timeline';
@@ -35,7 +34,6 @@ import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
import { useStartTransaction } from '../../../../common/lib/apm/use_start_transaction';
import { ALERTS_ACTIONS } from '../../../../common/lib/apm/user_actions';
import { defaultUdtHeaders } from '../../../../timelines/components/timeline/unified_components/default_headers';
-import { defaultHeaders } from '../../../../timelines/components/timeline/body/column_headers/default_headers';
interface UseInvestigateInTimelineActionProps {
ecsRowData?: Ecs | Ecs[] | null;
@@ -146,21 +144,13 @@ export const useInvestigateInTimeline = ({
timelineType: TimelineTypeEnum.default,
});
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
-
const updateTimeline = useUpdateTimeline();
const createTimeline = useCallback(
async ({ from: fromTimeline, timeline, to: toTimeline, ruleNote }: CreateTimelineProps) => {
const newColumns = timeline.columns;
const newColumnsOverride =
- !newColumns || isEmpty(newColumns)
- ? !unifiedComponentsInTimelineDisabled
- ? defaultUdtHeaders
- : defaultHeaders
- : newColumns;
+ !newColumns || isEmpty(newColumns) ? defaultUdtHeaders : newColumns;
await clearActiveTimeline();
updateTimelineIsLoading({ id: TimelineId.active, isLoading: false });
@@ -175,7 +165,6 @@ export const useInvestigateInTimeline = ({
indexNames: timeline.indexNames ?? [],
show: true,
excludedRowRendererIds:
- !unifiedComponentsInTimelineDisabled &&
timeline.timelineType !== TimelineTypeEnum.template
? timeline.excludedRowRendererIds
: [],
@@ -184,12 +173,7 @@ export const useInvestigateInTimeline = ({
ruleNote,
});
},
- [
- updateTimeline,
- updateTimelineIsLoading,
- clearActiveTimeline,
- unifiedComponentsInTimelineDisabled,
- ]
+ [updateTimeline, updateTimelineIsLoading, clearActiveTimeline]
);
const investigateInTimelineAlertClick = useCallback(async () => {
diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.tsx
index a8eec62f2d58a..27c2699cdf195 100644
--- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.tsx
+++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.tsx
@@ -10,7 +10,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useDispatch } from 'react-redux';
import { i18n } from '@kbn/i18n';
import type { EqlOptionsSelected } from '@kbn/timelines-plugin/common';
-import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import { convertKueryToElasticSearchQuery } from '../../../../common/lib/kuery';
import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
import { useSourcererDataView } from '../../../../sourcerer/containers';
@@ -48,10 +47,6 @@ export const useRuleFromTimeline = (setRuleQuery: SetRuleQuery): RuleFromTimelin
SourcererScopeName.timeline
);
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
-
const isEql = useRef(false);
// selectedTimeline = timeline to set rule from
@@ -200,11 +195,10 @@ export const useRuleFromTimeline = (setRuleQuery: SetRuleQuery): RuleFromTimelin
queryTimelineById({
timelineId,
onOpenTimeline,
- unifiedComponentsInTimelineDisabled,
});
}
},
- [onOpenTimeline, queryTimelineById, selectedTimeline, unifiedComponentsInTimelineDisabled]
+ [onOpenTimeline, queryTimelineById, selectedTimeline]
);
const [urlStateInitialized, setUrlStateInitialized] = useState(false);
diff --git a/x-pack/plugins/security_solution/public/notes/components/open_timeline_button.test.tsx b/x-pack/plugins/security_solution/public/notes/components/open_timeline_button.test.tsx
index 85ecfce68e5d9..c6a2629494758 100644
--- a/x-pack/plugins/security_solution/public/notes/components/open_timeline_button.test.tsx
+++ b/x-pack/plugins/security_solution/public/notes/components/open_timeline_button.test.tsx
@@ -10,7 +10,6 @@ import React from 'react';
import { OpenTimelineButtonIcon } from './open_timeline_button';
import type { Note } from '../../../common/api/timeline';
import { OPEN_TIMELINE_BUTTON_TEST_ID } from './test_ids';
-import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features';
import { useQueryTimelineById } from '../../timelines/components/open_timeline/helpers';
jest.mock('../../common/hooks/use_experimental_features');
@@ -40,11 +39,6 @@ describe('OpenTimelineButtonIcon', () => {
const openTimeline = jest.fn();
(useQueryTimelineById as jest.Mock).mockReturnValue(openTimeline);
- const unifiedComponentsInTimelineDisabled = false;
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(
- unifiedComponentsInTimelineDisabled
- );
-
const { getByTestId } = render();
const button = getByTestId(`${OPEN_TIMELINE_BUTTON_TEST_ID}-${index}`);
@@ -55,7 +49,6 @@ describe('OpenTimelineButtonIcon', () => {
onOpenTimeline: undefined,
timelineId: note.timelineId,
timelineType: undefined,
- unifiedComponentsInTimelineDisabled,
});
});
});
diff --git a/x-pack/plugins/security_solution/public/notes/components/open_timeline_button.tsx b/x-pack/plugins/security_solution/public/notes/components/open_timeline_button.tsx
index b44ffd55a767a..91f3a722ebeeb 100644
--- a/x-pack/plugins/security_solution/public/notes/components/open_timeline_button.tsx
+++ b/x-pack/plugins/security_solution/public/notes/components/open_timeline_button.tsx
@@ -8,7 +8,6 @@
import React, { memo, useCallback } from 'react';
import { EuiButtonIcon } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features';
import { useQueryTimelineById } from '../../timelines/components/open_timeline/helpers';
import { OPEN_TIMELINE_BUTTON_TEST_ID } from './test_ids';
import type { Note } from '../../../common/api/timeline';
@@ -32,10 +31,6 @@ export interface OpenTimelineButtonIconProps {
* Renders a button to open the timeline associated with a note
*/
export const OpenTimelineButtonIcon = memo(({ note, index }: OpenTimelineButtonIconProps) => {
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
-
const queryTimelineById = useQueryTimelineById();
const openTimeline = useCallback(
({ timelineId }: { timelineId: string }) =>
@@ -44,9 +39,8 @@ export const OpenTimelineButtonIcon = memo(({ note, index }: OpenTimelineButtonI
onOpenTimeline: undefined,
timelineId,
timelineType: undefined,
- unifiedComponentsInTimelineDisabled,
}),
- [queryTimelineById, unifiedComponentsInTimelineDisabled]
+ [queryTimelineById]
);
return (
diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx
index a167791a7b06c..97310f8a90f23 100644
--- a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx
@@ -8,7 +8,6 @@
import { EuiHorizontalRule, EuiText } from '@elastic/eui';
import React, { useCallback, useMemo, useEffect } from 'react';
-import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
import { SortFieldTimelineEnum, TimelineTypeEnum } from '../../../../common/api/timeline';
import { useGetAllTimeline } from '../../../timelines/containers/all';
import { useQueryTimelineById } from '../../../timelines/components/open_timeline/helpers';
@@ -33,10 +32,6 @@ interface Props {
const PAGE_SIZE = 3;
const StatefulRecentTimelinesComponent: React.FC = ({ filterBy }) => {
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
-
const { formatUrl } = useFormatUrl(SecurityPageName.timelines);
const { navigateToApp } = useKibana().services.application;
@@ -47,10 +42,9 @@ const StatefulRecentTimelinesComponent: React.FC = ({ filterBy }) => {
queryTimelineById({
duplicate,
timelineId,
- unifiedComponentsInTimelineDisabled,
});
},
- [queryTimelineById, unifiedComponentsInTimelineDisabled]
+ [queryTimelineById]
);
const goToTimelines = useCallback(
diff --git a/x-pack/plugins/security_solution/public/timelines/components/modal/actions/new_timeline_button.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/modal/actions/new_timeline_button.test.tsx
index e1fdbe8817041..c26b34dffcaf4 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/modal/actions/new_timeline_button.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/modal/actions/new_timeline_button.test.tsx
@@ -10,9 +10,7 @@ import React from 'react';
import { NewTimelineButton } from './new_timeline_button';
import { TimelineId } from '../../../../../common/types';
import { timelineActions } from '../../../store';
-import { defaultHeaders } from '../../timeline/body/column_headers/default_headers';
import { TestProviders } from '../../../../common/mock';
-import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import { RowRendererValues } from '../../../../../common/api/timeline';
import { defaultUdtHeaders } from '../../timeline/unified_components/default_headers';
@@ -76,26 +74,5 @@ describe('NewTimelineButton', () => {
excludedRowRendererIds: RowRendererValues,
});
});
-
- // disable unified components in timeline
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
-
- getByTestId('timeline-modal-new-timeline-dropdown-button').click();
- getByTestId('timeline-modal-new-timeline').click();
-
- spy.mockClear();
-
- await waitFor(() => {
- expect(spy).toHaveBeenCalledWith({
- columns: defaultHeaders,
- dataViewId,
- id: TimelineId.test,
- indexNames: selectedPatterns,
- show: true,
- timelineType: 'default',
- updated: undefined,
- excludedRowRendererIds: [],
- });
- });
});
});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap
index e881d9d5d1ce1..1267a66e0f6d7 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap
@@ -119,12 +119,13 @@ tr:hover .c3:focus::before {
margin-right: 5px;
}
-.c7 {
- margin-right: 3px;
+.c15 {
+ position: relative;
+ top: 1px;
}
-.c8 {
- margin: 0 5px;
+.c14 {
+ margin-right: 5px;
}
.c17 {
@@ -155,13 +156,12 @@ tr:hover .c3:focus::before {
margin: 0 5px;
}
-.c15 {
- position: relative;
- top: 1px;
+.c7 {
+ margin-right: 3px;
}
-.c14 {
- margin-right: 5px;
+.c8 {
+ margin: 0 5px;
}
.c12 {
diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts
index a3428ae6f2e1d..5da977c30a410 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts
+++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts
@@ -36,12 +36,8 @@ import {
} from './__mocks__';
import { resolveTimeline } from '../../containers/api';
import { defaultUdtHeaders } from '../timeline/unified_components/default_headers';
-import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
-import type { ExperimentalFeatures } from '../../../../common';
-import { allowedExperimentalValues } from '../../../../common';
jest.mock('../../../common/hooks/use_experimental_features');
-const useIsExperimentalFeatureEnabledMock = useIsExperimentalFeatureEnabled as jest.Mock;
jest.mock('react-redux', () => {
const actual = jest.requireActual('react-redux');
@@ -146,14 +142,6 @@ describe('helpers', () => {
beforeEach(() => {
mockResults = cloneDeep(mockTimelineResults);
-
- (useIsExperimentalFeatureEnabledMock as jest.Mock).mockImplementation(
- (featureFlag: keyof ExperimentalFeatures) => {
- return featureFlag === 'unifiedComponentsInTimelineDisabled'
- ? false
- : allowedExperimentalValues[featureFlag];
- }
- );
});
describe('#getPinnedEventCount', () => {
@@ -500,8 +488,7 @@ describe('helpers', () => {
const newTimeline = defaultTimelineToTimelineModel(
timeline,
false,
- TimelineTypeEnum.template,
- false
+ TimelineTypeEnum.template
);
expect(newTimeline).toEqual({
...defaultTimeline,
@@ -523,12 +510,7 @@ describe('helpers', () => {
timelineType: TimelineTypeEnum.default,
};
- const newTimeline = defaultTimelineToTimelineModel(
- timeline,
- false,
- TimelineTypeEnum.default,
- false
- );
+ const newTimeline = defaultTimelineToTimelineModel(timeline, false, TimelineTypeEnum.default);
expect(newTimeline).toEqual({
...defaultTimeline,
dateRange: { end: '2020-07-08T08:20:18.966Z', start: '2020-07-07T08:20:18.966Z' },
@@ -538,7 +520,7 @@ describe('helpers', () => {
});
});
- test('should produce correct model if unifiedComponentsInTimelineDisabled is false', () => {
+ test('should produce correct model', () => {
const timeline = {
savedObjectId: 'savedObject-1',
title: 'Awesome Timeline',
@@ -547,12 +529,7 @@ describe('helpers', () => {
timelineType: TimelineTypeEnum.default,
};
- const newTimeline = defaultTimelineToTimelineModel(
- timeline,
- false,
- TimelineTypeEnum.default,
- false
- );
+ const newTimeline = defaultTimelineToTimelineModel(timeline, false, TimelineTypeEnum.default);
expect(newTimeline).toEqual({
...defaultTimeline,
dateRange: { end: '2020-07-08T08:20:18.966Z', start: '2020-07-07T08:20:18.966Z' },
@@ -564,7 +541,7 @@ describe('helpers', () => {
});
});
- test('should produce correct model if unifiedComponentsInTimelineDisabled == false and custom set of columns is passed', () => {
+ test('should produce correct model if custom set of columns is passed', () => {
const customColumns = defaultUdtHeaders.slice(0, 2);
const timeline = {
savedObjectId: 'savedObject-1',
@@ -575,12 +552,7 @@ describe('helpers', () => {
columns: customColumns as ColumnHeaderResult[],
};
- const newTimeline = defaultTimelineToTimelineModel(
- timeline,
- false,
- TimelineTypeEnum.default,
- false
- );
+ const newTimeline = defaultTimelineToTimelineModel(timeline, false, TimelineTypeEnum.default);
expect(newTimeline).toEqual({
...defaultTimeline,
dateRange: { end: '2020-07-08T08:20:18.966Z', start: '2020-07-07T08:20:18.966Z' },
@@ -592,7 +564,7 @@ describe('helpers', () => {
});
});
- test('should produce correct model if unifiedComponentsInTimelineDisabled == false and custom set of excludedRowRendererIds is passed', () => {
+ test('should produce correct model if custom set of excludedRowRendererIds is passed', () => {
const excludedRowRendererIds: RowRendererId[] = ['zeek'];
const timeline = {
savedObjectId: 'savedObject-1',
@@ -603,12 +575,7 @@ describe('helpers', () => {
excludedRowRendererIds,
};
- const newTimeline = defaultTimelineToTimelineModel(
- timeline,
- false,
- TimelineTypeEnum.default,
- false
- );
+ const newTimeline = defaultTimelineToTimelineModel(timeline, false, TimelineTypeEnum.default);
expect(newTimeline).toEqual({
...defaultTimeline,
dateRange: { end: '2020-07-08T08:20:18.966Z', start: '2020-07-07T08:20:18.966Z' },
@@ -649,7 +616,7 @@ describe('helpers', () => {
});
});
- describe('open a timeline', () => {
+ describe('open a timeline 1', () => {
const selectedTimeline = {
...mockSelectedTimeline,
};
@@ -715,7 +682,8 @@ describe('helpers', () => {
describe('update a timeline', () => {
const selectedTimeline = { ...mockSelectedTimeline };
-
+ const untitledTimeline = { ...mockSelectedTimeline, title: '' };
+ const onOpenTimeline = jest.fn();
const args: QueryTimelineById = {
duplicate: false,
graphEventId: '',
@@ -724,146 +692,76 @@ describe('helpers', () => {
openTimeline: true,
};
- beforeAll(async () => {
+ beforeEach(async () => {
(resolveTimeline as jest.Mock).mockResolvedValue(selectedTimeline);
- renderHook(async () => {
- const queryTimelineById = useQueryTimelineById();
- await queryTimelineById(args);
- });
});
- afterAll(() => {
+ afterEach(() => {
jest.clearAllMocks();
});
- test('dispatch updateIsLoading to true', () => {
- expect(dispatchUpdateIsLoading).toBeCalledWith({
- id: TimelineId.active,
- isLoading: true,
- });
- });
-
- test('get timeline by Id', () => {
- expect(resolveTimeline).toHaveBeenCalled();
- });
-
- test('should not override daterange if TimelineStatus is active', () => {
- const { timeline } = formatTimelineResponseToModel(
- omitTypenameInTimeline(getOr({}, 'data.timeline', selectedTimeline)),
- args.duplicate,
- args.timelineType
- );
-
- expect(mockUpdateTimeline).toBeCalledWith({
- timeline: {
- ...timeline,
- graphEventId: '',
- show: true,
- dateRange: {
- start: '2020-07-07T08:20:18.966Z',
- end: '2020-07-08T08:20:18.966Z',
- },
- },
- preventSettingQuery: true,
- duplicate: false,
- from: '2020-07-07T08:20:18.966Z',
- to: '2020-07-08T08:20:18.966Z',
- notes: [],
- id: TimelineId.active,
- resolveTimelineConfig: {
- outcome: 'exactMatch',
- alias_target_id: undefined,
- },
- });
- });
-
- test('dispatch updateIsLoading to false', () => {
- expect(dispatchUpdateIsLoading).toBeCalledWith({
- id: TimelineId.active,
- isLoading: false,
- });
- });
- });
-
- describe('open an immutable template', () => {
- const template = { ...mockSelectedTemplate };
- const onOpenTimeline = jest.fn();
- const args = {
- duplicate: false,
- graphEventId: '',
- timelineId: '',
- timelineType: TimelineTypeEnum.template,
- onOpenTimeline,
- openTimeline: true,
- };
-
- beforeAll(async () => {
- (resolveTimeline as jest.Mock).mockResolvedValue(template);
+ test('should get timeline by Id with correct statuses', async () => {
renderHook(async () => {
const queryTimelineById = useQueryTimelineById();
- queryTimelineById(args);
+ await queryTimelineById(args);
});
- });
- afterAll(() => {
- (resolveTimeline as jest.Mock).mockReset();
- jest.clearAllMocks();
- });
-
- test('dispatch updateIsLoading to true', () => {
expect(dispatchUpdateIsLoading).toBeCalledWith({
id: TimelineId.active,
isLoading: true,
});
- });
- test('get timeline by Id', () => {
- expect(resolveTimeline).toHaveBeenCalled();
- });
-
- test('override daterange if TimelineStatus is immutable', () => {
+ // expect(resolveTimeline).toHaveBeenCalled();
const { timeline } = formatTimelineResponseToModel(
- omitTypenameInTimeline(getOr({}, 'data.timeline', template)),
+ omitTypenameInTimeline(getOr({}, 'data.timeline', selectedTimeline)),
args.duplicate,
args.timelineType
);
- expect(onOpenTimeline).toBeCalledWith({
- ...timeline,
- dateRange: {
- end: '2020-10-28T11:37:31.655Z',
- start: '2020-10-27T11:37:31.655Z',
- },
+
+ await waitFor(() => {
+ expect(mockUpdateTimeline).toHaveBeenCalledWith({
+ timeline: {
+ ...timeline,
+ graphEventId: '',
+ show: true,
+ dateRange: {
+ start: '2020-07-07T08:20:18.966Z',
+ end: '2020-07-08T08:20:18.966Z',
+ },
+ },
+ preventSettingQuery: true,
+ duplicate: false,
+ from: '2020-07-07T08:20:18.966Z',
+ to: '2020-07-08T08:20:18.966Z',
+ notes: [],
+ id: TimelineId.active,
+ resolveTimelineConfig: {
+ outcome: 'exactMatch',
+ alias_target_id: undefined,
+ },
+ });
});
- });
- test('dispatch updateIsLoading to false', () => {
expect(dispatchUpdateIsLoading).toBeCalledWith({
id: TimelineId.active,
isLoading: false,
});
});
- });
- describe('open a timeline when unifiedComponentsInTimelineDisabled is false', () => {
- const untitledTimeline = { ...mockSelectedTimeline, title: '' };
- const onOpenTimeline = jest.fn();
- afterEach(() => {
- jest.clearAllMocks();
- });
- it('should update timeline correctly when timeline is untitled', async () => {
- const args: QueryTimelineById = {
+ test('should update timeline correctly when timeline is untitled', async () => {
+ (resolveTimeline as jest.Mock).mockResolvedValue(selectedTimeline);
+ const newArgs: QueryTimelineById = {
duplicate: false,
graphEventId: '',
timelineId: undefined,
timelineType: TimelineTypeEnum.default,
onOpenTimeline,
openTimeline: true,
- unifiedComponentsInTimelineDisabled: false,
};
(resolveTimeline as jest.Mock).mockResolvedValue(untitledTimeline);
renderHook(async () => {
const queryTimelineById = useQueryTimelineById();
- queryTimelineById(args);
+ queryTimelineById(newArgs);
});
expect(dispatchUpdateIsLoading).toHaveBeenCalledWith({
@@ -886,17 +784,7 @@ describe('helpers', () => {
});
});
- it('should update timeline correctly when timeline is already saved and onOpenTimeline is not provided', async () => {
- const args: QueryTimelineById = {
- duplicate: false,
- graphEventId: '',
- timelineId: TimelineId.active,
- timelineType: TimelineTypeEnum.default,
- onOpenTimeline: undefined,
- openTimeline: true,
- unifiedComponentsInTimelineDisabled: false,
- };
-
+ test('should update timeline correctly when timeline is already saved and onOpenTimeline is not provided', async () => {
(resolveTimeline as jest.Mock).mockResolvedValue(mockSelectedTimeline);
renderHook(async () => {
const queryTimelineById = useQueryTimelineById();
@@ -925,17 +813,7 @@ describe('helpers', () => {
});
});
- it('should update timeline correctly when timeline is already saved and onOpenTimeline IS provided', async () => {
- const args: QueryTimelineById = {
- duplicate: false,
- graphEventId: '',
- timelineId: TimelineId.active,
- timelineType: TimelineTypeEnum.default,
- onOpenTimeline,
- openTimeline: true,
- unifiedComponentsInTimelineDisabled: false,
- };
-
+ test('should update timeline correctly when timeline is already saved and onOpenTimeline IS provided', async () => {
(resolveTimeline as jest.Mock).mockResolvedValue(mockSelectedTimeline);
renderHook(async () => {
const queryTimelineById = useQueryTimelineById();
@@ -956,16 +834,75 @@ describe('helpers', () => {
});
});
});
+
+ describe('open an immutable template', () => {
+ const template = { ...mockSelectedTemplate };
+ const onOpenTimeline = jest.fn();
+ const args = {
+ duplicate: false,
+ graphEventId: '',
+ timelineId: '',
+ timelineType: TimelineTypeEnum.template,
+ onOpenTimeline,
+ openTimeline: true,
+ };
+
+ beforeAll(async () => {
+ (resolveTimeline as jest.Mock).mockResolvedValue(template);
+ renderHook(async () => {
+ const queryTimelineById = useQueryTimelineById();
+ queryTimelineById(args);
+ });
+ });
+
+ afterAll(() => {
+ (resolveTimeline as jest.Mock).mockReset();
+ jest.clearAllMocks();
+ });
+
+ test('dispatch updateIsLoading to true', () => {
+ expect(dispatchUpdateIsLoading).toBeCalledWith({
+ id: TimelineId.active,
+ isLoading: true,
+ });
+ });
+
+ test('get timeline by Id', () => {
+ expect(resolveTimeline).toHaveBeenCalled();
+ });
+
+ test('override daterange if TimelineStatus is immutable', () => {
+ const { timeline } = formatTimelineResponseToModel(
+ omitTypenameInTimeline(getOr({}, 'data.timeline', template)),
+ args.duplicate,
+ args.timelineType
+ );
+ expect(onOpenTimeline).toBeCalledWith({
+ ...timeline,
+ dateRange: {
+ end: '2020-10-28T11:37:31.655Z',
+ start: '2020-10-27T11:37:31.655Z',
+ },
+ });
+ });
+
+ test('dispatch updateIsLoading to false', () => {
+ expect(dispatchUpdateIsLoading).toBeCalledWith({
+ id: TimelineId.active,
+ isLoading: false,
+ });
+ });
+ });
});
describe('omitTypenameInTimeline', () => {
- test('it does not modify the passed in timeline if no __typename exists', () => {
+ test('should not modify the passed in timeline if no __typename exists', () => {
const result = omitTypenameInTimeline(mockGetOneTimelineResult);
expect(result).toEqual(mockGetOneTimelineResult);
});
- test('it returns timeline with __typename removed when it exists', () => {
+ test('should return timeline with __typename removed when it exists', () => {
const mockTimeline = {
...mockGetOneTimelineResult,
__typename: 'something, something',
diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts
index 000a7b226561e..b3f84a82d4ed0 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts
+++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts
@@ -35,10 +35,7 @@ import { useUpdateTimeline } from './use_update_timeline';
import type { TimelineModel } from '../../store/model';
import { timelineDefaults } from '../../store/defaults';
-import {
- defaultColumnHeaderType,
- defaultHeaders,
-} from '../timeline/body/column_headers/default_headers';
+import { defaultColumnHeaderType } from '../timeline/body/column_headers/default_headers';
import type { OpenTimelineResult, TimelineErrorCallback } from './types';
import { IS_OPERATOR } from '../timeline/data_providers/data_provider';
@@ -238,13 +235,10 @@ export const getTimelineStatus = (
export const defaultTimelineToTimelineModel = (
timeline: TimelineResponse,
duplicate: boolean,
- timelineType?: TimelineType,
- unifiedComponentsInTimelineDisabled?: boolean
+ timelineType?: TimelineType
): TimelineModel => {
const isTemplate = timeline.timelineType === TimelineTypeEnum.template;
- const defaultHeadersValue = !unifiedComponentsInTimelineDisabled
- ? defaultUdtHeaders
- : defaultHeaders;
+ const defaultHeadersValue = defaultUdtHeaders;
const timelineEntries = {
...timeline,
@@ -294,18 +288,12 @@ export const defaultTimelineToTimelineModel = (
export const formatTimelineResponseToModel = (
timelineToOpen: TimelineResponse,
duplicate: boolean = false,
- timelineType?: TimelineType,
- unifiedComponentsInTimelineDisabled?: boolean
+ timelineType?: TimelineType
): { notes: Note[] | null | undefined; timeline: TimelineModel } => {
const { notes, ...timelineModel } = timelineToOpen;
return {
notes,
- timeline: defaultTimelineToTimelineModel(
- timelineModel,
- duplicate,
- timelineType,
- unifiedComponentsInTimelineDisabled
- ),
+ timeline: defaultTimelineToTimelineModel(timelineModel, duplicate, timelineType),
};
};
@@ -319,11 +307,6 @@ export interface QueryTimelineById {
onOpenTimeline?: (timeline: TimelineModel) => void;
openTimeline?: boolean;
savedSearchId?: string;
- /*
- * Below feature flag will be removed once
- * unified components have been fully migrated
- * */
- unifiedComponentsInTimelineDisabled?: boolean;
}
export const useQueryTimelineById = () => {
@@ -347,7 +330,6 @@ export const useQueryTimelineById = () => {
onOpenTimeline,
openTimeline = true,
savedSearchId,
- unifiedComponentsInTimelineDisabled = false,
}: QueryTimelineById) => {
updateIsLoading({ id: TimelineId.active, isLoading: true });
if (timelineId == null) {
@@ -359,14 +341,14 @@ export const useQueryTimelineById = () => {
to: DEFAULT_TO_MOMENT.toISOString(),
timeline: {
...timelineDefaults,
- columns: !unifiedComponentsInTimelineDisabled ? defaultUdtHeaders : defaultHeaders,
+ columns: defaultUdtHeaders,
id: TimelineId.active,
activeTab: activeTimelineTab,
show: openTimeline,
initialized: true,
savedSearchId: savedSearchId ?? null,
excludedRowRendererIds:
- !unifiedComponentsInTimelineDisabled && timelineType !== TimelineTypeEnum.template
+ timelineType !== TimelineTypeEnum.template
? timelineDefaults.excludedRowRendererIds
: [],
},
@@ -384,8 +366,7 @@ export const useQueryTimelineById = () => {
const { timeline, notes } = formatTimelineResponseToModel(
timelineToOpen,
duplicate,
- timelineType,
- unifiedComponentsInTimelineDisabled
+ timelineType
);
if (onOpenTimeline != null) {
diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx
index cdb61ecf61f6e..6afd900185af7 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx
@@ -9,7 +9,6 @@ import React, { useEffect, useState, useCallback, useMemo } from 'react';
import { useDispatch } from 'react-redux';
import { encode } from '@kbn/rison';
-import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
import {
RULE_FROM_EQL_URL_PARAM,
RULE_FROM_TIMELINE_URL_PARAM,
@@ -25,8 +24,6 @@ import { createTimeline as dispatchCreateNewTimeline } from '../../store/actions
import { useGetAllTimeline } from '../../containers/all';
-import { defaultHeaders } from '../timeline/body/column_headers/default_headers';
-
import { OpenTimeline } from './open_timeline';
import { OPEN_TIMELINE_CLASS_NAME, useQueryTimelineById } from './helpers';
import { OpenTimelineModalBody } from './open_timeline_modal/open_timeline_modal_body';
@@ -160,9 +157,6 @@ export const StatefulOpenTimelineComponent = React.memo(
);
const { dataViewId, selectedPatterns } = useSourcererDataView(SourcererScopeName.timeline);
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
const {
customTemplateTimelineCount,
@@ -251,13 +245,11 @@ export const StatefulOpenTimelineComponent = React.memo(
dispatch(
dispatchCreateNewTimeline({
id: TimelineId.active,
- columns: !unifiedComponentsInTimelineDisabled ? defaultUdtHeaders : defaultHeaders,
+ columns: defaultUdtHeaders,
dataViewId,
indexNames: selectedPatterns,
show: false,
- excludedRowRendererIds: !unifiedComponentsInTimelineDisabled
- ? timelineDefaults.excludedRowRendererIds
- : [],
+ excludedRowRendererIds: timelineDefaults.excludedRowRendererIds,
})
);
}
@@ -265,15 +257,7 @@ export const StatefulOpenTimelineComponent = React.memo(
await deleteTimelinesByIds(timelineIds, searchIds);
refetch();
},
- [
- startTransaction,
- timelineSavedObjectId,
- refetch,
- dispatch,
- dataViewId,
- selectedPatterns,
- unifiedComponentsInTimelineDisabled,
- ]
+ [startTransaction, timelineSavedObjectId, refetch, dispatch, dataViewId, selectedPatterns]
);
const onDeleteOneTimeline: OnDeleteOneTimeline = useCallback(
@@ -374,7 +358,6 @@ export const StatefulOpenTimelineComponent = React.memo(
onOpenTimeline,
timelineId,
timelineType: timelineTypeToOpen,
- unifiedComponentsInTimelineDisabled,
});
},
// eslint-disable-next-line react-hooks/exhaustive-deps
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx
index f502c1b8884aa..991355b1177bb 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx
@@ -12,11 +12,9 @@ import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
import { isTab } from '@kbn/timelines-plugin/public';
-import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
import { useUserPrivileges } from '../../../common/components/user_privileges';
import { timelineActions, timelineSelectors } from '../../store';
import { timelineDefaults } from '../../store/defaults';
-import { defaultHeaders } from './body/column_headers/default_headers';
import type { CellValueElementProps } from './cell_rendering';
import { SourcererScopeName } from '../../../sourcerer/store/model';
import { TimelineModalHeader } from '../modal/header';
@@ -75,10 +73,6 @@ const StatefulTimelineComponent: React.FC = ({
}) => {
const dispatch = useDispatch();
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
-
const containerElement = useRef(null);
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
const selectedPatternsSourcerer = useSelector((state: State) => {
@@ -129,13 +123,11 @@ const StatefulTimelineComponent: React.FC = ({
dispatch(
timelineActions.createTimeline({
id: timelineId,
- columns: !unifiedComponentsInTimelineDisabled ? defaultUdtHeaders : defaultHeaders,
+ columns: defaultUdtHeaders,
dataViewId: selectedDataViewIdSourcerer,
indexNames: selectedPatternsSourcerer,
show: false,
- excludedRowRendererIds: !unifiedComponentsInTimelineDisabled
- ? timelineDefaults.excludedRowRendererIds
- : [],
+ excludedRowRendererIds: timelineDefaults.excludedRowRendererIds,
})
);
}
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/__snapshots__/index.test.tsx.snap
deleted file mode 100644
index e238e24ebce7a..0000000000000
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/__snapshots__/index.test.tsx.snap
+++ /dev/null
@@ -1,1165 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Timeline rendering renders correctly against snapshot 1`] = `
-
-`;
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.test.tsx
index 034f812373ec2..7c8949c1b6121 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.test.tsx
@@ -5,7 +5,6 @@
* 2.0.
*/
-import { shallow } from 'enzyme';
import React from 'react';
import useResizeObserver from 'use-resize-observer/polyfilled';
import type { Dispatch } from 'redux';
@@ -17,7 +16,6 @@ import { TestProviders } from '../../../../../common/mock/test_providers';
import type { Props as EqlTabContentComponentProps } from '.';
import { EqlTabContentComponent } from '.';
-import { useMountAppended } from '../../../../../common/utils/use_mount_appended';
import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline';
import { useTimelineEvents } from '../../../../containers';
import { useTimelineEventsDetails } from '../../../../containers/details';
@@ -26,6 +24,7 @@ import { mockSourcererScope } from '../../../../../sourcerer/containers/mocks';
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
import type { ExperimentalFeatures } from '../../../../../../common';
import { allowedExperimentalValues } from '../../../../../../common';
+import { render, screen } from '@testing-library/react';
jest.mock('../../../../containers', () => ({
useTimelineEvents: jest.fn(),
@@ -54,18 +53,25 @@ mockUseResizeObserver.mockImplementation(() => ({}));
jest.mock('../../../../../common/lib/kibana');
-describe('Timeline', () => {
+describe('EQL Tab', () => {
let props = {} as EqlTabContentComponentProps;
const startDate = '2018-03-23T18:49:23.132Z';
const endDate = '2018-03-24T03:33:52.253Z';
- const mount = useMountAppended();
+ beforeAll(() => {
+ // https://github.com/atlassian/react-beautiful-dnd/blob/4721a518356f72f1dac45b5fd4ee9d466aa2996b/docs/guides/setup-problem-detection-and-error-recovery.md#disable-logging
+ Object.defineProperty(window, '__@hello-pangea/dnd-disable-dev-warnings', {
+ get() {
+ return true;
+ },
+ });
+ });
beforeEach(() => {
(useTimelineEvents as jest.Mock).mockReturnValue([
false,
{
- events: mockTimelineData,
+ events: mockTimelineData.slice(0, 1),
pageInfo: {
activePage: 0,
totalPages: 10,
@@ -78,9 +84,6 @@ describe('Timeline', () => {
(useIsExperimentalFeatureEnabledMock as jest.Mock).mockImplementation(
(feature: keyof ExperimentalFeatures) => {
- if (feature === 'unifiedComponentsInTimelineDisabled') {
- return true;
- }
return allowedExperimentalValues[feature];
}
);
@@ -105,133 +108,45 @@ describe('Timeline', () => {
});
describe('rendering', () => {
- test('renders correctly against snapshot', () => {
- const wrapper = shallow(
-
-
-
- );
-
- expect(wrapper.find('EqlTabContentComponent')).toMatchSnapshot();
- });
-
- test('it renders the timeline header', () => {
- const wrapper = mount(
-
-
-
- );
-
- expect(wrapper.find('[data-test-subj="timelineHeader"]').exists()).toEqual(true);
- });
-
- test('it renders the timeline table', () => {
- const wrapper = mount(
+ test('should render the timeline table', async () => {
+ render(
);
- expect(wrapper.find(`[data-test-subj="${TimelineTabs.eql}-events-table"]`).exists()).toEqual(
- true
- );
- });
-
- test('it renders the timeline column headers', () => {
- const wrapper = mount(
-
-
-
- );
-
- expect(
- wrapper
- .find(
- `[data-test-subj="${TimelineTabs.eql}-events-table"] [data-test-subj="column-headers"]`
- )
- .exists()
- ).toEqual(true);
- });
-
- test('it does NOT renders the timeline global sorting icon in headers', () => {
- const wrapper = mount(
-
-
-
- );
- expect(
- wrapper
- .find(
- `[data-test-subj="${TimelineTabs.eql}-events-table"] [data-test-subj="column-headers"] [data-test-subj="timeline-sorting-fields"]`
- )
- .exists()
- ).toEqual(false);
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
});
- test('it does render the timeline table when the source is loading with no events', () => {
- (useSourcererDataView as jest.Mock).mockReturnValue({
- browserFields: {},
- loading: true,
- indexPattern: {},
- selectedPatterns: [],
- missingPatterns: [],
- });
- const wrapper = mount(
+ test('it renders the timeline column headers', async () => {
+ render(
);
- expect(wrapper.find(`[data-test-subj="${TimelineTabs.eql}-events-table"]`).exists()).toEqual(
- true
- );
- expect(wrapper.find('[data-test-subj="events"]').exists()).toEqual(false);
- });
-
- test('it does NOT render the timeline table when start is empty', () => {
- const wrapper = mount(
-
-
-
- );
-
- expect(wrapper.find(`[data-test-subj="${TimelineTabs.eql}-events-table"]`).exists()).toEqual(
- true
- );
- expect(wrapper.find('[data-test-subj="events"]').exists()).toEqual(false);
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
});
- test('it does NOT render the timeline table when end is empty', () => {
- const wrapper = mount(
-
-
-
- );
-
- expect(wrapper.find(`[data-test-subj="${TimelineTabs.eql}-events-table"]`).exists()).toEqual(
- true
- );
- expect(wrapper.find('[data-test-subj="events"]').exists()).toEqual(false);
- });
+ test('should render correct placeholder when there are not results', async () => {
+ (useTimelineEvents as jest.Mock).mockReturnValue([
+ false,
+ {
+ events: [],
+ pageInfo: {
+ activePage: 0,
+ totalPages: 10,
+ },
+ },
+ ]);
- it('it does NOT render the timeline footer when query is empty', () => {
- const wrapper = mount(
+ render(
);
- expect(wrapper.find('[data-test-subj="timeline-footer"]').exists()).toEqual(false);
- });
-
- it('it shows the timeline footer when query is non-empty', () => {
- const wrapper = mount(
-
-
-
- );
-
- expect(wrapper.find('[data-test-subj="timeline-footer"]').exists()).toEqual(true);
+ expect(await screen.findByText('No results found')).toBeVisible();
});
});
});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx
index e2881c8c24458..e41d9017d49be 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx
@@ -17,23 +17,17 @@ import type { EuiDataGridControlColumn } from '@elastic/eui';
import { DataLoadingState } from '@kbn/unified-data-table';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import type { RunTimeMappings } from '@kbn/timelines-plugin/common/search_strategy';
+import { InputsModelId } from '../../../../../common/store/inputs/constants';
import { useKibana } from '../../../../../common/lib/kibana';
import {
DocumentDetailsLeftPanelKey,
DocumentDetailsRightPanelKey,
} from '../../../../../flyout/document_details/shared/constants/panel_keys';
-import { InputsModelId } from '../../../../../common/store/inputs/constants';
-import type { ControlColumnProps } from '../../../../../../common/types';
import { useDeepEqualSelector } from '../../../../../common/hooks/use_selector';
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
import { timelineActions, timelineSelectors } from '../../../../store';
import { useTimelineEvents } from '../../../../containers';
-import { StatefulBody } from '../../body';
-import { Footer, footerHeight } from '../../footer';
-import { calculateTotalPages } from '../../helpers';
-import { TimelineRefetch } from '../../refetch_timeline';
import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline';
-import { EventDetailsWidthProvider } from '../../../../../common/components/events_viewer/event_details_width_context';
import type { inputsModel, State } from '../../../../../common/store';
import { inputsSelectors } from '../../../../../common/store';
import { SourcererScopeName } from '../../../../../sourcerer/store/model';
@@ -42,19 +36,8 @@ import { useSourcererDataView } from '../../../../../sourcerer/containers';
import { useEqlEventsCountPortal } from '../../../../../common/hooks/use_timeline_events_count';
import type { TimelineModel } from '../../../../store/model';
import { useTimelineFullScreen } from '../../../../../common/containers/use_full_screen';
-import {
- EventsCountBadge,
- FullWidthFlexGroup,
- ScrollableFlexItem,
- StyledEuiFlyoutBody,
- StyledEuiFlyoutFooter,
-} from '../shared/layout';
-import {
- TIMELINE_EMPTY_EVENTS,
- isTimerangeSame,
- timelineEmptyTrailingControlColumns,
- TIMELINE_NO_SORTING,
-} from '../shared/utils';
+import { EventsCountBadge, FullWidthFlexGroup } from '../shared/layout';
+import { isTimerangeSame, TIMELINE_NO_SORTING } from '../shared/utils';
import type { TimelineTabCommonProps } from '../shared/types';
import { UnifiedTimelineBody } from '../../body/unified_timeline_body';
import { EqlTabHeader } from './header';
@@ -63,6 +46,7 @@ import { useTimelineControlColumn } from '../shared/use_timeline_control_columns
import { LeftPanelNotesTab } from '../../../../../flyout/document_details/left';
import { useNotesInFlyout } from '../../properties/use_notes_in_flyout';
import { NotesFlyout } from '../../properties/notes_flyout';
+import { TimelineRefetch } from '../../refetch_timeline';
export type Props = TimelineTabCommonProps & PropsFromRedux;
@@ -72,10 +56,8 @@ export const EqlTabContentComponent: React.FC = ({
end,
eqlOptions,
timelineId,
- isLive,
itemsPerPage,
itemsPerPageOptions,
- renderCellValue,
rowRenderers,
start,
timerangeKind,
@@ -88,7 +70,6 @@ export const EqlTabContentComponent: React.FC = ({
const { portalNode: eqlEventsCountPortalNode } = useEqlEventsCountPortal();
const { setTimelineFullScreen, timelineFullScreen } = useTimelineFullScreen();
const {
- browserFields,
dataViewId,
loading: loadingSourcerer,
selectedPatterns,
@@ -96,10 +77,6 @@ export const EqlTabContentComponent: React.FC = ({
} = useSourcererDataView(SourcererScopeName.timeline);
const { augmentedColumnHeaders, timelineQueryFieldsFromColumns } = useTimelineColumns(columns);
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
-
const getManageTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
const currentTimeline = useDeepEqualSelector((state) =>
@@ -132,7 +109,7 @@ export const EqlTabContentComponent: React.FC = ({
id: timelineId,
indexNames: selectedPatterns,
language: 'eql',
- limit: !unifiedComponentsInTimelineDisabled ? sampleSize : itemsPerPage,
+ limit: sampleSize,
runtimeMappings: sourcererDataView?.runtimeFieldMap as RunTimeMappings,
skip: !canQueryTimeline(),
startDate: start,
@@ -267,103 +244,42 @@ export const EqlTabContentComponent: React.FC = ({
return (
<>
- {!unifiedComponentsInTimelineDisabled ? (
- <>
-
- {totalCount >= 0 ? (
- {totalCount}
- ) : null}
-
- {NotesFlyoutMemo}
-
-
-
- >
- ) : (
- <>
-
- {totalCount >= 0 ? {totalCount} : null}
-
- {NotesFlyoutMemo}
-
-
- {unifiedHeader}
-
-
-
-
-
-
-
- {!isBlankTimeline && (
-
- )}
-
-
-
-
- >
- )}
+
+
+ {totalCount >= 0 ? (
+ {totalCount}
+ ) : null}
+
+ {NotesFlyoutMemo}
+
+
+
>
);
};
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/__snapshots__/index.test.tsx.snap
deleted file mode 100644
index 19c12dffb1e89..0000000000000
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/__snapshots__/index.test.tsx.snap
+++ /dev/null
@@ -1,1171 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`PinnedTabContent rendering renders correctly against snapshot 1`] = `
-
-`;
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.test.tsx
index e5ca2af9d70a8..0df50a8cf47c3 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.test.tsx
@@ -5,18 +5,17 @@
* 2.0.
*/
-import { shallow } from 'enzyme';
import React from 'react';
import useResizeObserver from 'use-resize-observer/polyfilled';
import type { Dispatch } from 'redux';
+import { render, screen } from '@testing-library/react';
import { DefaultCellRenderer } from '../../cell_rendering/default_cell_renderer';
import { defaultHeaders, mockTimelineData } from '../../../../../common/mock';
import { TestProviders } from '../../../../../common/mock/test_providers';
import { defaultRowRenderers } from '../../body/renderers';
import type { Sort } from '../../body/sort';
-import { useMountAppended } from '../../../../../common/utils/use_mount_appended';
-import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline';
+import { TimelineId } from '../../../../../../common/types/timeline';
import { useTimelineEvents } from '../../../../containers';
import { useTimelineEventsDetails } from '../../../../containers/details';
import { useSourcererDataView } from '../../../../../sourcerer/containers';
@@ -24,10 +23,11 @@ import { mockSourcererScope } from '../../../../../sourcerer/containers/mocks';
import type { Props as PinnedTabContentComponentProps } from '.';
import { PinnedTabContentComponent } from '.';
import { Direction } from '../../../../../../common/search_strategy';
-import { mockCasesContext } from '@kbn/cases-plugin/public/mocks/mock_cases_context';
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
import type { ExperimentalFeatures } from '../../../../../../common';
import { allowedExperimentalValues } from '../../../../../../common';
+import { useKibana } from '../../../../../common/lib/kibana';
+import { createStartServicesMock } from '../../../../../common/lib/kibana/kibana_react.mock';
jest.mock('../../../../containers', () => ({
useTimelineEvents: jest.fn(),
@@ -51,48 +51,21 @@ const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock;
jest.mock('use-resize-observer/polyfilled');
mockUseResizeObserver.mockImplementation(() => ({}));
-const useAddToTimeline = () => ({
- beginDrag: jest.fn(),
- cancelDrag: jest.fn(),
- dragToLocation: jest.fn(),
- endDrag: jest.fn(),
- hasDraggableLock: jest.fn(),
- startDragToTimeline: jest.fn(),
-});
-
jest.mock('../../../../../common/lib/kibana', () => {
const originalModule = jest.requireActual('../../../../../common/lib/kibana');
return {
...originalModule,
- useKibana: jest.fn().mockReturnValue({
- services: {
- application: {
- navigateToApp: jest.fn(),
- getUrlForApp: jest.fn(),
- },
- cases: {
- ui: {
- getCasesContext: () => mockCasesContext,
- },
- },
- uiSettings: {
- get: jest.fn(),
- },
- savedObjects: {
- client: {},
- },
- timelines: {
- getLastUpdated: jest.fn(),
- getFieldBrowser: jest.fn(),
- getUseAddToTimeline: () => useAddToTimeline,
- },
- triggersActionsUi: { getFieldBrowser: jest.fn() },
- },
- }),
+ useKibana: jest.fn(),
useGetUserSavedObjectPermissions: jest.fn(),
};
});
+const kibanaMockResult = {
+ services: createStartServicesMock(),
+};
+
+const useKibanaMock = useKibana as jest.Mock;
+
describe('PinnedTabContent', () => {
let props = {} as PinnedTabContentComponentProps;
const sort: Sort[] = [
@@ -104,16 +77,23 @@ describe('PinnedTabContent', () => {
},
];
- const mount = useMountAppended();
+ beforeAll(() => {
+ // https://github.com/atlassian/react-beautiful-dnd/blob/4721a518356f72f1dac45b5fd4ee9d466aa2996b/docs/guides/setup-problem-detection-and-error-recovery.md#disable-logging
+ Object.defineProperty(window, '__@hello-pangea/dnd-disable-dev-warnings', {
+ get() {
+ return true;
+ },
+ });
+ });
beforeEach(() => {
(useTimelineEvents as jest.Mock).mockReturnValue([
false,
{
- events: mockTimelineData,
+ events: mockTimelineData.slice(0, 1),
pageInfo: {
activePage: 0,
- totalPages: 10,
+ totalPages: 1,
},
},
]);
@@ -123,13 +103,12 @@ describe('PinnedTabContent', () => {
(useIsExperimentalFeatureEnabledMock as jest.Mock).mockImplementation(
(feature: keyof ExperimentalFeatures) => {
- if (feature === 'unifiedComponentsInTimelineDisabled') {
- return true;
- }
return allowedExperimentalValues[feature];
}
);
+ useKibanaMock.mockReturnValue(kibanaMockResult);
+
props = {
dispatch: {} as Dispatch,
columns: defaultHeaders,
@@ -145,36 +124,14 @@ describe('PinnedTabContent', () => {
});
describe('rendering', () => {
- test('renders correctly against snapshot', () => {
- const wrapper = shallow(
-
-
-
- );
-
- expect(wrapper.find('PinnedTabContentComponent')).toMatchSnapshot();
- });
-
- test('it renders the timeline table', () => {
- const wrapper = mount(
-
-
-
- );
-
- expect(
- wrapper.find(`[data-test-subj="${TimelineTabs.pinned}-events-table"]`).exists()
- ).toEqual(true);
- });
-
- it('it shows the timeline footer', () => {
- const wrapper = mount(
+ test('should render timeline table correctly', async () => {
+ render(
);
- expect(wrapper.find('[data-test-subj="timeline-footer"]').exists()).toEqual(true);
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
});
});
});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.tsx
index 43632efb3b0eb..959d6a3b52c3e 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.tsx
@@ -7,45 +7,30 @@
import { isEmpty } from 'lodash/fp';
import React, { useMemo, useCallback, memo } from 'react';
-import styled from 'styled-components';
import type { ConnectedProps } from 'react-redux';
import { connect } from 'react-redux';
import deepEqual from 'fast-deep-equal';
import type { EuiDataGridControlColumn } from '@elastic/eui';
-import { DataLoadingState } from '@kbn/unified-data-table';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import type { RunTimeMappings } from '@kbn/timelines-plugin/common/search_strategy';
import {
DocumentDetailsLeftPanelKey,
DocumentDetailsRightPanelKey,
} from '../../../../../flyout/document_details/shared/constants/panel_keys';
-import type { ControlColumnProps } from '../../../../../../common/types';
import { useKibana } from '../../../../../common/lib/kibana';
import { timelineSelectors } from '../../../../store';
import type { Direction } from '../../../../../../common/search_strategy';
import { useTimelineEvents } from '../../../../containers';
import { defaultHeaders } from '../../body/column_headers/default_headers';
-import { StatefulBody } from '../../body';
-import { Footer, footerHeight } from '../../footer';
import { requiredFieldsForActions } from '../../../../../detections/components/alerts_table/default_config';
-import { EventDetailsWidthProvider } from '../../../../../common/components/events_viewer/event_details_width_context';
import { SourcererScopeName } from '../../../../../sourcerer/store/model';
import { timelineDefaults } from '../../../../store/defaults';
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
import { useSourcererDataView } from '../../../../../sourcerer/containers';
-import { useTimelineFullScreen } from '../../../../../common/containers/use_full_screen';
import type { TimelineModel } from '../../../../store/model';
import type { State } from '../../../../../common/store';
-import { calculateTotalPages } from '../../helpers';
import { TimelineTabs } from '../../../../../../common/types/timeline';
-import { ExitFullScreen } from '../../../../../common/components/exit_full_screen';
import { UnifiedTimelineBody } from '../../body/unified_timeline_body';
-import {
- FullWidthFlexGroup,
- ScrollableFlexItem,
- StyledEuiFlyoutBody,
- StyledEuiFlyoutFooter,
-} from '../shared/layout';
import type { TimelineTabCommonProps } from '../shared/types';
import { useTimelineColumns } from '../shared/use_timeline_columns';
import { useTimelineControlColumn } from '../shared/use_timeline_control_columns';
@@ -53,10 +38,6 @@ import { LeftPanelNotesTab } from '../../../../../flyout/document_details/left';
import { useNotesInFlyout } from '../../properties/use_notes_in_flyout';
import { NotesFlyout } from '../../properties/notes_flyout';
-const ExitFullScreenContainer = styled.div`
- width: 180px;
-`;
-
interface PinnedFilter {
bool: {
should: Array<{ match_phrase: { _id: string } }>;
@@ -66,8 +47,6 @@ interface PinnedFilter {
export type Props = TimelineTabCommonProps & PropsFromRedux;
-const trailingControlColumns: ControlColumnProps[] = []; // stable reference
-
const rowDetailColumn = [
{
id: 'row-details',
@@ -84,22 +63,13 @@ export const PinnedTabContentComponent: React.FC = ({
itemsPerPage,
itemsPerPageOptions,
pinnedEventIds,
- renderCellValue,
rowRenderers,
sort,
eventIdToNoteIds,
}) => {
const { telemetry } = useKibana().services;
- const {
- browserFields,
- dataViewId,
- loading: loadingSourcerer,
- sourcererDataView,
- selectedPatterns,
- } = useSourcererDataView(SourcererScopeName.timeline);
- const { setTimelineFullScreen, timelineFullScreen } = useTimelineFullScreen();
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
+ const { dataViewId, sourcererDataView, selectedPatterns } = useSourcererDataView(
+ SourcererScopeName.timeline
);
const filterQuery = useMemo(() => {
@@ -257,11 +227,6 @@ export const PinnedTabContentComponent: React.FC = ({
onToggleShowNotes,
});
- const isQueryLoading = useMemo(
- () => [DataLoadingState.loading, DataLoadingState.loadingMore].includes(queryLoadingState),
- [queryLoadingState]
- );
-
const NotesFlyoutMemo = useMemo(() => {
return (
= ({
);
}, [associateNote, closeNotesFlyout, isNotesFlyoutVisible, noteEventId, notes, timelineId]);
- if (!unifiedComponentsInTimelineDisabled) {
- return (
- <>
- {NotesFlyoutMemo}
- >}
- columns={augmentedColumnHeaders}
- rowRenderers={rowRenderers}
- timelineId={timelineId}
- itemsPerPage={itemsPerPage}
- itemsPerPageOptions={itemsPerPageOptions}
- sort={sort}
- events={events}
- refetch={refetch}
- dataLoadingState={queryLoadingState}
- totalCount={events.length}
- onChangePage={loadPage}
- activeTab={TimelineTabs.pinned}
- updatedAt={refreshedAt}
- isTextBasedQuery={false}
- pageInfo={pageInfo}
- leadingControlColumns={leadingControlColumns as EuiDataGridControlColumn[]}
- trailingControlColumns={rowDetailColumn}
- />
- >
- );
- }
-
return (
<>
{NotesFlyoutMemo}
-
-
- {timelineFullScreen && setTimelineFullScreen != null && (
-
-
-
- )}
-
-
-
-
-
-
-
-
-
-
+ >}
+ columns={augmentedColumnHeaders}
+ rowRenderers={rowRenderers}
+ timelineId={timelineId}
+ itemsPerPage={itemsPerPage}
+ itemsPerPageOptions={itemsPerPageOptions}
+ sort={sort}
+ events={events}
+ refetch={refetch}
+ dataLoadingState={queryLoadingState}
+ totalCount={totalCount}
+ onChangePage={loadPage}
+ activeTab={TimelineTabs.pinned}
+ updatedAt={refreshedAt}
+ isTextBasedQuery={false}
+ pageInfo={pageInfo}
+ leadingControlColumns={leadingControlColumns as EuiDataGridControlColumn[]}
+ trailingControlColumns={rowDetailColumn}
+ />
>
);
};
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/__snapshots__/index.test.tsx.snap
deleted file mode 100644
index 53c2b7ae4914a..0000000000000
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/__snapshots__/index.test.tsx.snap
+++ /dev/null
@@ -1,1317 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Timeline rendering renders correctly against snapshot 1`] = `
-
-`;
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/header/index.tsx
index b70475787eb65..af2e30018535a 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/header/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/header/index.tsx
@@ -12,10 +12,7 @@ import { InPortal } from 'react-reverse-portal';
import { IS_DRAGGING_CLASS_NAME } from '@kbn/securitysolution-t-grid';
import styled from '@emotion/styled';
import { euiThemeVars } from '@kbn/ui-theme';
-import { useIsExperimentalFeatureEnabled } from '../../../../../../common/hooks/use_experimental_features';
import { useTimelineEventsCountPortal } from '../../../../../../common/hooks/use_timeline_events_count';
-import { useTimelineFullScreen } from '../../../../../../common/containers/use_full_screen';
-import { ExitFullScreen } from '../../../../../../common/components/exit_full_screen';
import {
type TimelineStatus,
TimelineStatusEnum,
@@ -70,11 +67,7 @@ const QueryTabHeaderComponent: React.FC = ({
showEventsCountBadge,
totalCount,
}) => {
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
const { portalNode: timelineEventsCountPortalNode } = useTimelineEventsCountPortal();
- const { setTimelineFullScreen, timelineFullScreen } = useTimelineFullScreen();
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
const getIsDataProviderVisible = useMemo(
@@ -101,18 +94,6 @@ const QueryTabHeaderComponent: React.FC = ({
) : null}
- {unifiedComponentsInTimelineDisabled &&
- timelineFullScreen &&
- setTimelineFullScreen != null && (
-
-
-
-
-
- )}
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.test.tsx
index 8f6540f54eaf1..493fdb4bc603e 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.test.tsx
@@ -5,43 +5,53 @@
* 2.0.
*/
-import { shallow } from 'enzyme';
-import React from 'react';
-import useResizeObserver from 'use-resize-observer/polyfilled';
-import type { Dispatch } from 'redux';
-
-import { DefaultCellRenderer } from '../../cell_rendering/default_cell_renderer';
-import { defaultHeaders, mockTimelineData } from '../../../../../common/mock';
-import { TestProviders } from '../../../../../common/mock/test_providers';
-
-import type { Props as QueryTabContentComponentProps } from '.';
-import { QueryTabContentComponent } from '.';
+import type { ComponentProps, FunctionComponent } from 'react';
+import React, { useEffect } from 'react';
+import QueryTabContent from '.';
import { defaultRowRenderers } from '../../body/renderers';
-import type { Sort } from '../../body/sort';
-import { mockDataProviders } from '../../data_providers/mock/mock_data_providers';
-import { useMountAppended } from '../../../../../common/utils/use_mount_appended';
-import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline';
-import { TimelineStatusEnum } from '../../../../../../common/api/timeline';
+import { TimelineId } from '../../../../../../common/types/timeline';
import { useTimelineEvents } from '../../../../containers';
import { useTimelineEventsDetails } from '../../../../containers/details';
import { useSourcererDataView } from '../../../../../sourcerer/containers';
import { mockSourcererScope } from '../../../../../sourcerer/containers/mocks';
-import { Direction } from '../../../../../../common/search_strategy';
-import * as helpers from '../../../../../common/lib/kuery';
-import { waitFor } from '@testing-library/react';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
+import {
+ createMockStore,
+ createSecuritySolutionStorageMock,
+ mockGlobalState,
+ mockTimelineData,
+ TestProviders,
+} from '../../../../../common/mock';
+import { DefaultCellRenderer } from '../../cell_rendering/default_cell_renderer';
+import { render, screen, waitFor, fireEvent, within, cleanup } from '@testing-library/react';
+import { createStartServicesMock } from '../../../../../common/lib/kibana/kibana_react.mock';
+import type { StartServices } from '../../../../../types';
+import { useKibana } from '../../../../../common/lib/kibana';
+import { useDispatch } from 'react-redux';
import type { ExperimentalFeatures } from '../../../../../../common';
import { allowedExperimentalValues } from '../../../../../../common';
+import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
+import { defaultUdtHeaders } from '../../unified_components/default_headers';
+import { defaultColumnHeaderType } from '../../body/column_headers/default_headers';
+import { useUserPrivileges } from '../../../../../common/components/user_privileges';
+import { getEndpointPrivilegesInitialStateMock } from '../../../../../common/components/user_privileges/endpoint/mocks';
+import * as timelineActions from '../../../../store/actions';
+import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
+import { createExpandableFlyoutApiMock } from '../../../../../common/mock/expandable_flyout';
+import { OPEN_FLYOUT_BUTTON_TEST_ID } from '../../../../../notes/components/test_ids';
+import { userEvent } from '@testing-library/user-event';
+
+jest.mock('../../../../../common/components/user_privileges');
jest.mock('../../../../containers', () => ({
useTimelineEvents: jest.fn(),
}));
-jest.mock('../../../../containers/details', () => ({
- useTimelineEventsDetails: jest.fn(),
-}));
+
+jest.mock('../../../../containers/details');
+
jest.mock('../../../fields_browser', () => ({
useFieldBrowserOptions: jest.fn(),
}));
+
jest.mock('../../body/events', () => ({
Events: () => <>>,
}));
@@ -53,209 +63,1139 @@ jest.mock('../../../../../sourcerer/containers/use_signal_helpers', () => ({
jest.mock('../../../../../common/lib/kuery');
-const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock;
-jest.mock('use-resize-observer/polyfilled');
-mockUseResizeObserver.mockImplementation(() => ({}));
+jest.mock('../../../../../common/hooks/use_experimental_features');
+
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useLocation: jest.fn(() => ({
+ pathname: '',
+ search: '',
+ })),
+}));
+
+// These tests can take more than standard timeout of 5s
+// that is why we are increasing it.
+const SPECIAL_TEST_TIMEOUT = 50000;
+
+const useIsExperimentalFeatureEnabledMock = jest.fn((feature: keyof ExperimentalFeatures) => {
+ return allowedExperimentalValues[feature];
+});
jest.mock('../../../../../common/lib/kibana');
-jest.mock('../../../../containers/use_timeline_data_filters', () => ({
- useTimelineDataFilters: jest.fn().mockReturnValue({ from: 'now-15m', to: 'now' }),
-}));
+// unified-field-list is reporting multiple analytics events
+jest.mock(`@elastic/ebt/client`);
-jest.mock('../../../../../common/hooks/use_experimental_features');
+const mockOpenFlyout = jest.fn();
+const mockCloseFlyout = jest.fn();
+jest.mock('@kbn/expandable-flyout');
+
+const TestComponent = (props: Partial>) => {
+ const testComponentDefaultProps: ComponentProps = {
+ timelineId: TimelineId.test,
+ renderCellValue: DefaultCellRenderer,
+ rowRenderers: defaultRowRenderers,
+ };
+
+ const dispatch = useDispatch();
+
+ useEffect(() => {
+ // Unified field list can be a culprit for long load times, so we wait for the timeline to be interacted with to load
+ dispatch(timelineActions.showTimeline({ id: TimelineId.test, show: true }));
+
+ // populating timeline so that it is not blank
+ dispatch(
+ timelineActions.applyKqlFilterQuery({
+ id: TimelineId.test,
+ filterQuery: {
+ kuery: {
+ kind: 'kuery',
+ expression: '*',
+ },
+ serializedQuery: '*',
+ },
+ })
+ );
+ }, [dispatch]);
+
+ return ;
+};
+
+const customColumnOrder = [
+ ...defaultUdtHeaders,
+ {
+ columnHeaderType: defaultColumnHeaderType,
+ id: 'event.severity',
+ },
+];
+
+const mockState = {
+ ...structuredClone(mockGlobalState),
+};
+
+mockState.timeline.timelineById[TimelineId.test].columns = customColumnOrder;
+
+const TestWrapper: FunctionComponent> = ({ children }) => {
+ return {children};
+};
+
+const renderTestComponents = (props?: Partial>) => {
+ return render(, {
+ wrapper: TestWrapper,
+ });
+};
-const useIsExperimentalFeatureEnabledMock = useIsExperimentalFeatureEnabled as jest.Mock;
-
-describe('Timeline', () => {
- let props = {} as QueryTabContentComponentProps;
- const sort: Sort[] = [
- {
- columnId: '@timestamp',
- columnType: 'date',
- esTypes: ['date'],
- sortDirection: Direction.desc,
- },
- ];
- const startDate = '2018-03-23T18:49:23.132Z';
- const endDate = '2018-03-24T03:33:52.253Z';
-
- const mount = useMountAppended();
- const getWrapper = async (childrenComponent: JSX.Element) => {
- const wrapper = mount(childrenComponent);
- await waitFor(() => wrapper.find('[data-test-subj="timelineHeader"]').exists());
- return wrapper;
+const loadPageMock = jest.fn();
+
+const useSourcererDataViewMocked = jest.fn().mockReturnValue({
+ ...mockSourcererScope,
+});
+
+const { storage: storageMock } = createSecuritySolutionStorageMock();
+
+let useTimelineEventsMock = jest.fn();
+
+describe('query tab with unified timeline', () => {
+ beforeAll(() => {
+ jest.mocked(useExpandableFlyoutApi).mockImplementation(() => ({
+ ...createExpandableFlyoutApiMock(),
+ openFlyout: mockOpenFlyout,
+ closeFlyout: mockCloseFlyout,
+ }));
+
+ // https://github.com/atlassian/react-beautiful-dnd/blob/4721a518356f72f1dac45b5fd4ee9d466aa2996b/docs/guides/setup-problem-detection-and-error-recovery.md#disable-logging
+ Object.defineProperty(window, '__@hello-pangea/dnd-disable-dev-warnings', {
+ get() {
+ return true;
+ },
+ });
+ });
+ const kibanaServiceMock: StartServices = {
+ ...createStartServicesMock(),
+ storage: storageMock,
};
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ storageMock.clear();
+ cleanup();
+ localStorage.clear();
+ });
+
beforeEach(() => {
- (useTimelineEvents as jest.Mock).mockReturnValue([
+ useTimelineEventsMock = jest.fn(() => [
false,
{
- events: mockTimelineData,
+ events: structuredClone(mockTimelineData.slice(0, 1)),
pageInfo: {
activePage: 0,
- totalPages: 10,
+ totalPages: 3,
},
+ refreshedAt: Date.now(),
+ totalCount: 3,
+ loadPage: loadPageMock,
},
]);
- (useTimelineEventsDetails as jest.Mock).mockReturnValue([false, {}]);
- (useSourcererDataView as jest.Mock).mockReturnValue(mockSourcererScope);
+ HTMLElement.prototype.getBoundingClientRect = jest.fn(() => {
+ return {
+ width: 1000,
+ height: 1000,
+ x: 0,
+ y: 0,
+ } as DOMRect;
+ });
+
+ (useKibana as jest.Mock).mockImplementation(() => {
+ return {
+ services: kibanaServiceMock,
+ };
+ });
+
+ (useTimelineEvents as jest.Mock).mockImplementation(useTimelineEventsMock);
+
+ (useTimelineEventsDetails as jest.Mock).mockImplementation(() => [false, {}]);
- (useIsExperimentalFeatureEnabledMock as jest.Mock).mockImplementation(
- (feature: keyof ExperimentalFeatures) => {
- if (feature === 'unifiedComponentsInTimelineDisabled') {
- return true;
- }
- return allowedExperimentalValues[feature];
- }
+ (useSourcererDataView as jest.Mock).mockImplementation(useSourcererDataViewMocked);
+
+ (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
+ useIsExperimentalFeatureEnabledMock
);
- props = {
- dispatch: {} as Dispatch,
- columns: defaultHeaders,
- dataProviders: mockDataProviders,
- end: endDate,
- filters: [],
- timelineId: TimelineId.test,
- isLive: false,
- itemsPerPage: 5,
- itemsPerPageOptions: [5, 10, 20],
- kqlMode: 'search' as QueryTabContentComponentProps['kqlMode'],
- kqlQueryExpression: ' ',
- kqlQueryLanguage: 'kuery',
- renderCellValue: DefaultCellRenderer,
- rowRenderers: defaultRowRenderers,
- showCallOutUnauthorizedMsg: false,
- sort,
- start: startDate,
- status: TimelineStatusEnum.active,
- timerangeKind: 'absolute',
- activeTab: TimelineTabs.query,
- show: true,
- pinnedEventIds: {},
- eventIdToNoteIds: {},
- };
+ (useUserPrivileges as jest.Mock).mockReturnValue({
+ kibanaSecuritySolutionsPrivileges: { crud: true, read: true },
+ endpointPrivileges: getEndpointPrivilegesInitialStateMock(),
+ detectionEnginePrivileges: { loading: false, error: undefined, result: undefined },
+ });
});
- // Failing: see https://github.com/elastic/kibana/issues/193103
- describe.skip('rendering', () => {
- let spyCombineQueries: jest.SpyInstance;
+ describe('render', () => {
+ it(
+ 'should render unifiedDataTable in timeline',
+ async () => {
+ renderTestComponents();
+ await waitFor(() => {
+ expect(screen.getByTestId('discoverDocTable')).toBeVisible();
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+
+ it(
+ 'should render unified-field-list in timeline',
+ async () => {
+ renderTestComponents();
+ await waitFor(() => {
+ expect(screen.getByTestId('timeline-sidebar')).toBeVisible();
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+ it(
+ 'should show row-renderers correctly by default',
+ async () => {
+ renderTestComponents();
+ await waitFor(() => {
+ expect(screen.getByTestId('discoverDocTable')).toBeVisible();
+ });
+
+ expect(screen.getByTestId('timeline-row-renderer-0')).toBeVisible();
+ },
+
+ SPECIAL_TEST_TIMEOUT
+ );
+
+ it(
+ 'should hide row-renderers when disabled',
+ async () => {
+ renderTestComponents();
+ await waitFor(() => {
+ expect(screen.getByTestId('discoverDocTable')).toBeVisible();
+ });
+
+ expect(screen.getByTestId('timeline-row-renderer-0')).toBeVisible();
+
+ fireEvent.click(screen.getByTestId('show-row-renderers-gear'));
+ expect(screen.getByTestId('row-renderers-modal')).toBeVisible();
+
+ fireEvent.click(screen.getByTestId('disable-all'));
+
+ expect(
+ within(screen.getAllByTestId('renderer-checkbox')[0]).getByRole('checkbox')
+ ).not.toBeChecked();
+ fireEvent.click(screen.getByLabelText('Closes this modal window'));
+
+ expect(screen.queryByTestId('row-renderers-modal')).not.toBeInTheDocument();
+
+ expect(screen.queryByTestId('timeline-row-renderer-0')).not.toBeInTheDocument();
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+ });
+
+ describe('pagination', () => {
beforeEach(() => {
- spyCombineQueries = jest.spyOn(helpers, 'combineQueries');
+ // pagination tests need more than 1 record so here
+ // we return 5 records instead of just 1.
+ useTimelineEventsMock = jest.fn(() => [
+ false,
+ {
+ events: structuredClone(mockTimelineData.slice(0, 5)),
+ pageInfo: {
+ activePage: 0,
+ totalPages: 5,
+ },
+ refreshedAt: Date.now(),
+ /*
+ * `totalCount` could be any number w.r.t this test
+ * and actually means total hits on elastic search
+ * and not the fecthed number of records.
+ *
+ * This helps in testing `sampleSize` and `loadMore`
+ */
+ totalCount: 50,
+ loadPage: loadPageMock,
+ },
+ ]);
+
+ (useTimelineEvents as jest.Mock).mockImplementation(useTimelineEventsMock);
});
+
afterEach(() => {
- spyCombineQueries.mockClear();
+ jest.clearAllMocks();
});
- test('should trim kqlQueryExpression', async () => {
- await getWrapper(
-
-
-
+ it(
+ 'should paginate correctly',
+ async () => {
+ const mockStateWithNoteInTimeline = {
+ ...mockGlobalState,
+ timeline: {
+ ...mockGlobalState.timeline,
+ timelineById: {
+ [TimelineId.test]: {
+ ...mockGlobalState.timeline.timelineById[TimelineId.test],
+ /* 1 record for each page */
+ itemsPerPage: 1,
+ itemsPerPageOptions: [1, 2, 3, 4, 5],
+ savedObjectId: 'timeline-1', // match timelineId in mocked notes data
+ pinnedEventIds: { '1': true },
+ },
+ },
+ },
+ };
+
+ render(
+
+
+
+ );
+
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
+ expect(screen.getByTestId('pagination-button-previous')).toBeVisible();
+
+ expect(screen.getByTestId('tablePaginationPopoverButton')).toHaveTextContent(
+ 'Rows per page: 1'
+ );
+
+ expect(screen.getByTestId('pagination-button-0')).toHaveAttribute('aria-current', 'true');
+ expect(screen.getByTestId('pagination-button-4')).toBeVisible();
+ expect(screen.queryByTestId('pagination-button-5')).toBeNull();
+
+ fireEvent.click(screen.getByTestId('pagination-button-4'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('pagination-button-4')).toHaveAttribute('aria-current', 'true');
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+
+ it(
+ 'should load more records according to sample size correctly',
+ async () => {
+ const mockStateWithNoteInTimeline = {
+ ...mockGlobalState,
+ timeline: {
+ ...mockGlobalState.timeline,
+ timelineById: {
+ [TimelineId.test]: {
+ ...mockGlobalState.timeline.timelineById[TimelineId.test],
+ itemsPerPage: 1,
+ /*
+ * `sampleSize` is the max number of records that are fetched from elasticsearch
+ * in one request. If hits > sampleSize, you can fetch more records ( <= sampleSize)
+ */
+ sampleSize: 5,
+ itemsPerPageOptions: [1, 2, 3, 4, 5],
+ savedObjectId: 'timeline-1', // match timelineId in mocked notes data
+ pinnedEventIds: { '1': true },
+ },
+ },
+ },
+ };
+
+ render(
+
+
+
+ );
+
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('pagination-button-0')).toHaveAttribute('aria-current', 'true');
+ expect(screen.getByTestId('pagination-button-4')).toBeVisible();
+ });
+ // Go to last page
+ fireEvent.click(screen.getByTestId('pagination-button-4'));
+ await waitFor(() => {
+ expect(screen.getByTestId('dscGridSampleSizeFetchMoreLink')).toBeVisible();
+ });
+ fireEvent.click(screen.getByTestId('dscGridSampleSizeFetchMoreLink'));
+ expect(loadPageMock).toHaveBeenNthCalledWith(1, 1);
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+ });
+
+ describe('columns', () => {
+ it(
+ 'should move column left/right correctly ',
+ async () => {
+ const { container } = renderTestComponents();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('discoverDocTable')).toBeVisible();
+ });
+
+ const messageColumnIndex =
+ customColumnOrder.findIndex((header) => header.id === 'message') + 3;
+ // 3 is the offset for additional leading columns on left
+
+ expect(container.querySelector('[data-gridcell-column-id="message"]')).toHaveAttribute(
+ 'data-gridcell-column-index',
+ String(messageColumnIndex)
+ );
+
+ expect(container.querySelector('[data-gridcell-column-id="message"]')).toBeInTheDocument();
+
+ fireEvent.click(screen.getByTestId('dataGridHeaderCellActionButton-message'));
+
+ await waitFor(() => {
+ expect(screen.getByTitle('Move left')).toBeEnabled();
+ });
+
+ fireEvent.click(screen.getByTitle('Move left'));
+
+ await waitFor(() => {
+ expect(container.querySelector('[data-gridcell-column-id="message"]')).toHaveAttribute(
+ 'data-gridcell-column-index',
+ String(messageColumnIndex - 1)
+ );
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+
+ it(
+ 'should remove column',
+ async () => {
+ const { container } = renderTestComponents();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('discoverDocTable')).toBeVisible();
+ });
+
+ expect(container.querySelector('[data-gridcell-column-id="message"]')).toBeInTheDocument();
+
+ fireEvent.click(screen.getByTestId('dataGridHeaderCellActionButton-message'));
+
+ await waitFor(() => {
+ expect(screen.getByTitle('Remove column')).toBeVisible();
+ });
+
+ fireEvent.click(screen.getByTitle('Remove column'));
+
+ await waitFor(() => {
+ expect(
+ container.querySelector('[data-gridcell-column-id="message"]')
+ ).not.toBeInTheDocument();
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+
+ it(
+ 'should sort date column',
+ async () => {
+ const { container } = renderTestComponents();
+ await waitFor(() => {
+ expect(screen.getByTestId('discoverDocTable')).toBeVisible();
+ });
+
+ expect(
+ container.querySelector('[data-gridcell-column-id="@timestamp"]')
+ ).toBeInTheDocument();
+
+ fireEvent.click(screen.getByTestId('dataGridHeaderCellActionButton-@timestamp'));
+
+ await waitFor(() => {
+ expect(screen.getByTitle('Sort Old-New')).toBeVisible();
+ });
+ expect(screen.getByTitle('Unsort New-Old')).toBeVisible();
+
+ useTimelineEventsMock.mockClear();
+
+ fireEvent.click(screen.getByTitle('Sort Old-New'));
+
+ await waitFor(() => {
+ expect(useTimelineEventsMock).toHaveBeenNthCalledWith(
+ 1,
+ expect.objectContaining({
+ sort: [
+ {
+ direction: 'asc',
+ esTypes: ['date'],
+ field: '@timestamp',
+ type: 'date',
+ },
+ ],
+ })
+ );
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+
+ it(
+ 'should sort string column correctly',
+ async () => {
+ const { container } = renderTestComponents();
+ await waitFor(() => {
+ expect(screen.getByTestId('discoverDocTable')).toBeVisible();
+ });
+
+ expect(
+ container.querySelector('[data-gridcell-column-id="host.name"]')
+ ).toBeInTheDocument();
+
+ fireEvent.click(screen.getByTestId('dataGridHeaderCellActionButton-host.name'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('dataGridHeaderCellActionGroup-host.name')).toBeVisible();
+ });
+
+ expect(screen.getByTitle('Sort A-Z')).toBeVisible();
+ expect(screen.getByTitle('Sort Z-A')).toBeVisible();
+
+ useTimelineEventsMock.mockClear();
+
+ fireEvent.click(screen.getByTitle('Sort A-Z'));
+
+ await waitFor(() => {
+ expect(useTimelineEventsMock).toHaveBeenNthCalledWith(
+ 1,
+ expect.objectContaining({
+ sort: [
+ {
+ direction: 'desc',
+ esTypes: ['date'],
+ field: '@timestamp',
+ type: 'date',
+ },
+ {
+ direction: 'asc',
+ esTypes: [],
+ field: 'host.name',
+ type: 'string',
+ },
+ ],
+ })
+ );
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+
+ it(
+ 'should sort number column',
+ async () => {
+ const field = {
+ name: 'event.severity',
+ type: 'number',
+ };
+
+ const { container } = renderTestComponents();
+ await waitFor(() => {
+ expect(screen.getByTestId('discoverDocTable')).toBeVisible();
+ });
+
+ expect(
+ container.querySelector(`[data-gridcell-column-id="${field.name}"]`)
+ ).toBeInTheDocument();
+
+ fireEvent.click(screen.getByTestId(`dataGridHeaderCellActionButton-${field.name}`));
+
+ await waitFor(() => {
+ expect(screen.getByTestId(`dataGridHeaderCellActionGroup-${field.name}`)).toBeVisible();
+ });
+
+ expect(screen.getByTitle('Sort Low-High')).toBeVisible();
+ expect(screen.getByTitle('Sort High-Low')).toBeVisible();
+
+ useTimelineEventsMock.mockClear();
+
+ fireEvent.click(screen.getByTitle('Sort Low-High'));
+
+ await waitFor(() => {
+ expect(useTimelineEventsMock).toHaveBeenNthCalledWith(
+ 1,
+ expect.objectContaining({
+ sort: [
+ {
+ direction: 'desc',
+ esTypes: ['date'],
+ field: '@timestamp',
+ type: 'date',
+ },
+ {
+ direction: 'asc',
+ esTypes: [],
+ field: field.name,
+ type: field.type,
+ },
+ ],
+ })
+ );
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+ });
+
+ describe('left controls', () => {
+ it(
+ 'should clear all sorting',
+ async () => {
+ renderTestComponents();
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
+
+ expect(screen.getByTestId('dataGridColumnSortingButton')).toBeVisible();
+ expect(
+ within(screen.getByTestId('dataGridColumnSortingButton')).getByRole('marquee')
+ ).toHaveTextContent('1');
+
+ fireEvent.click(screen.getByTestId('dataGridColumnSortingButton'));
+
+ // // timestamp sorting indicators
+ expect(
+ await screen.findByTestId('euiDataGridColumnSorting-sortColumn-@timestamp')
+ ).toBeInTheDocument();
+
+ expect(screen.getByTestId('dataGridHeaderCellSortingIcon-@timestamp')).toBeInTheDocument();
+
+ fireEvent.click(screen.getByTestId('dataGridColumnSortingClearButton'));
+
+ await waitFor(() => {
+ expect(screen.queryByTestId('dataGridHeaderCellSortingIcon-@timestamp')).toBeNull();
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+
+ it(
+ 'should be able to sort by multiple columns',
+ async () => {
+ renderTestComponents();
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
+
+ expect(screen.getByTestId('dataGridColumnSortingButton')).toBeVisible();
+ expect(
+ within(screen.getByTestId('dataGridColumnSortingButton')).getByRole('marquee')
+ ).toHaveTextContent('1');
+
+ fireEvent.click(screen.getByTestId('dataGridColumnSortingButton'));
+
+ // // timestamp sorting indicators
+ expect(
+ await screen.findByTestId('euiDataGridColumnSorting-sortColumn-@timestamp')
+ ).toBeInTheDocument();
+
+ expect(screen.getByTestId('dataGridHeaderCellSortingIcon-@timestamp')).toBeInTheDocument();
+
+ // add more columns to sorting
+ fireEvent.click(screen.getByText(/Pick fields to sort by/));
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('dataGridColumnSortingPopoverColumnSelection-event.severity')
+ ).toBeInTheDocument();
+ });
+
+ fireEvent.click(
+ screen.getByTestId('dataGridColumnSortingPopoverColumnSelection-event.severity')
+ );
+
+ // check new columns for sorting validity
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('dataGridHeaderCellSortingIcon-event.severity')
+ ).toBeInTheDocument();
+ });
+ expect(
+ screen.getByTestId('euiDataGridColumnSorting-sortColumn-event.severity')
+ ).toBeInTheDocument();
+
+ expect(screen.getByTestId('dataGridHeaderCellSortingIcon-@timestamp')).toBeInTheDocument();
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+ });
+
+ describe('unified fields list', () => {
+ it(
+ 'should remove the column when clicked on X sign',
+ async () => {
+ const field = {
+ name: 'event.severity',
+ };
+
+ renderTestComponents();
+ expect(await screen.findByTestId('timeline-sidebar')).toBeVisible();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('fieldListGroupedSelectedFields-count')).toHaveTextContent(
+ String(customColumnOrder.length)
+ );
+ });
+
+ // column exists in the table
+ expect(screen.getByTestId(`dataGridHeaderCell-${field.name}`)).toBeVisible();
+
+ fireEvent.click(screen.getAllByTestId(`fieldToggle-${field.name}`)[0]);
+
+ // column not longer exists in the table
+ await waitFor(() => {
+ expect(screen.getByTestId('fieldListGroupedSelectedFields-count')).toHaveTextContent(
+ String(customColumnOrder.length - 1)
+ );
+ });
+ expect(screen.queryAllByTestId(`dataGridHeaderCell-${field.name}`)).toHaveLength(0);
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+
+ it(
+ 'should add the column when clicked on ⊕ sign',
+ async () => {
+ const field = {
+ name: 'agent.id',
+ };
+
+ renderTestComponents();
+ expect(await screen.findByTestId('timeline-sidebar')).toBeVisible();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('fieldListGroupedSelectedFields-count')).toHaveTextContent(
+ String(customColumnOrder.length)
+ );
+ });
+
+ expect(screen.queryAllByTestId(`dataGridHeaderCell-${field.name}`)).toHaveLength(0);
+
+ // column exists in the table
+ const availableFields = screen.getByTestId('fieldListGroupedAvailableFields');
+
+ fireEvent.click(within(availableFields).getByTestId(`fieldToggle-${field.name}`));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('fieldListGroupedSelectedFields-count')).toHaveTextContent(
+ String(customColumnOrder.length + 1)
+ );
+ });
+ expect(screen.queryAllByTestId(`dataGridHeaderCell-${field.name}`)).toHaveLength(1);
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+
+ it(
+ 'should should show callout when field search does not matches any field',
+ async () => {
+ renderTestComponents();
+ expect(await screen.findByTestId('timeline-sidebar')).toBeVisible();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('fieldListGroupedAvailableFields-count')).toHaveTextContent(
+ '37'
+ );
+ });
+
+ fireEvent.change(screen.getByTestId('fieldListFiltersFieldSearch'), {
+ target: { value: 'fake_field' },
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('fieldListGroupedAvailableFieldsNoFieldsCallout-noFieldsMatch')
+ ).toBeVisible();
+ });
+
+ expect(screen.getByTestId('fieldListGroupedAvailableFields-count')).toHaveTextContent('0');
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+
+ it(
+ 'should toggle side bar correctly',
+ async () => {
+ renderTestComponents();
+ expect(await screen.findByTestId('timeline-sidebar')).toBeVisible();
+
+ expect(screen.getByTestId('fieldListGroupedFieldGroups')).toBeVisible();
+
+ fireEvent.click(screen.getByTitle('Hide sidebar'));
+
+ await waitFor(() => {
+ expect(screen.queryByTestId('fieldListGroupedFieldGroups')).not.toBeInTheDocument();
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+ });
+
+ describe('Leading actions - expand event', () => {
+ it(
+ 'should expand and collapse event correctly',
+ async () => {
+ renderTestComponents();
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
+
+ expect(screen.getByTestId('docTableExpandToggleColumn').firstChild).toHaveAttribute(
+ 'data-euiicon-type',
+ 'expand'
+ );
+
+ // Open Flyout
+ fireEvent.click(screen.getByTestId('docTableExpandToggleColumn'));
+
+ await waitFor(() => {
+ expect(mockOpenFlyout).toHaveBeenNthCalledWith(1, {
+ right: {
+ id: 'document-details-right',
+ params: {
+ id: '1',
+ indexName: '',
+ scopeId: TimelineId.test,
+ },
+ },
+ });
+ });
+
+ expect(screen.getByTestId('docTableExpandToggleColumn').firstChild).toHaveAttribute(
+ 'data-euiicon-type',
+ 'minimize'
+ );
+
+ // Close Flyout
+ fireEvent.click(screen.getByTestId('docTableExpandToggleColumn'));
+
+ await waitFor(() => {
+ expect(mockCloseFlyout).toHaveBeenNthCalledWith(1);
+ expect(screen.getByTestId('docTableExpandToggleColumn').firstChild).toHaveAttribute(
+ 'data-euiicon-type',
+ 'expand'
+ );
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
+ });
+
+ describe('Leading actions - notes', () => {
+ describe('securitySolutionNotesEnabled = true', () => {
+ beforeEach(() => {
+ (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
+ jest.fn((feature: keyof ExperimentalFeatures) => {
+ if (feature === 'securitySolutionNotesEnabled') {
+ return true;
+ }
+ return allowedExperimentalValues[feature];
+ })
+ );
+ });
+
+ // Flaky: https://github.com/elastic/kibana/issues/189794
+ it.skip(
+ 'should have the notification dot & correct tooltip',
+ async () => {
+ renderTestComponents();
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
+
+ expect(screen.getAllByTestId('timeline-notes-button-small')).toHaveLength(1);
+ expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
+
+ expect(screen.getByTestId('timeline-notes-notification-dot')).toBeVisible();
+
+ userEvent.hover(screen.getByTestId('timeline-notes-button-small'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('timeline-notes-tool-tip')).toBeInTheDocument();
+ expect(screen.getByTestId('timeline-notes-tool-tip')).toHaveTextContent(
+ '1 Note available. Click to view it & add more.'
+ );
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
);
+ it(
+ 'should be able to add notes through expandable flyout',
+ async () => {
+ renderTestComponents();
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
+ });
- expect(spyCombineQueries.mock.calls[0][0].kqlQuery.query).toEqual(
- props.kqlQueryExpression.trim()
+ fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
+
+ await waitFor(() => {
+ expect(mockOpenFlyout).toHaveBeenCalled();
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
);
});
- test('renders correctly against snapshot', () => {
- const wrapper = shallow(
-
-
-
- );
+ describe('securitySolutionNotesEnabled = false', () => {
+ beforeEach(() => {
+ (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
+ jest.fn((feature: keyof ExperimentalFeatures) => {
+ if (feature === 'securitySolutionNotesEnabled') {
+ return false;
+ }
+ return allowedExperimentalValues[feature];
+ })
+ );
+ });
- expect(wrapper.find('QueryTabContentComponent')).toMatchSnapshot();
- });
+ // Flaky: https://github.com/elastic/kibana/issues/189794
+ it.skip(
+ 'should have the notification dot & correct tooltip',
+ async () => {
+ renderTestComponents();
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
- test('it renders the timeline header', async () => {
- const wrapper = await getWrapper(
-
-
-
- );
+ expect(screen.getAllByTestId('timeline-notes-button-small')).toHaveLength(1);
+ expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
- expect(wrapper.find('[data-test-subj="timelineHeader"]').exists()).toEqual(true);
- });
+ expect(screen.getByTestId('timeline-notes-notification-dot')).toBeVisible();
+
+ fireEvent.mouseOver(screen.getByTestId('timeline-notes-button-small'));
- test('it renders the timeline table', async () => {
- const wrapper = await getWrapper(
-
-
-
+ await waitFor(() => {
+ expect(screen.getByTestId('timeline-notes-tool-tip')).toBeVisible();
+ expect(screen.getByTestId('timeline-notes-tool-tip')).toHaveTextContent(
+ '1 Note available. Click to view it & add more.'
+ );
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
);
+ it(
+ 'should be able to add notes using EuiFlyout',
+ async () => {
+ renderTestComponents();
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
- expect(
- wrapper.find(`[data-test-subj="${TimelineTabs.query}-events-table"]`).exists()
- ).toEqual(true);
- });
+ await waitFor(() => {
+ expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
+ });
- test('it does NOT render the timeline table when start is empty', async () => {
- const wrapper = await getWrapper(
-
-
-
+ fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('add-note-container')).toBeVisible();
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
);
- expect(
- wrapper.find(`[data-test-subj="${TimelineTabs.query}-events-table"]`).exists()
- ).toEqual(true);
- expect(wrapper.find('[data-test-subj="events"]').exists()).toEqual(false);
- });
+ it(
+ 'should cancel adding notes',
+ async () => {
+ renderTestComponents();
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
+ });
+
+ fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('add-note-container')).toBeVisible();
+ });
+
+ expect(screen.getByTestId('cancel')).not.toBeDisabled();
+
+ fireEvent.click(screen.getByTestId('cancel'));
- test('it does NOT render the timeline table when end is empty', async () => {
- const wrapper = await getWrapper(
-
-
-
+ await waitFor(() => {
+ expect(screen.queryByTestId('add-note-container')).not.toBeInTheDocument();
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
);
- expect(
- wrapper.find(`[data-test-subj="${TimelineTabs.query}-events-table"]`).exists()
- ).toEqual(true);
- expect(wrapper.find('[data-test-subj="events"]').exists()).toEqual(false);
- });
+ it(
+ 'should be able to delete notes',
+ async () => {
+ renderTestComponents();
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
+ });
+
+ fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
- test('it does NOT render the paging footer when you do NOT have any data providers', async () => {
- const wrapper = await getWrapper(
-
-
-
+ await waitFor(() => {
+ expect(screen.getByTestId('delete-note')).toBeVisible();
+ });
+
+ const noteDeleteSpy = jest.spyOn(timelineActions, 'setConfirmingNoteId');
+
+ fireEvent.click(screen.getByTestId('delete-note'));
+
+ await waitFor(() => {
+ expect(noteDeleteSpy).toHaveBeenCalled();
+ expect(noteDeleteSpy).toHaveBeenCalledWith({
+ confirmingNoteId: '1',
+ id: TimelineId.test,
+ });
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
);
- expect(wrapper.find('[data-test-subj="table-pagination"]').exists()).toEqual(false);
+ it(
+ 'should not show toggle event details action',
+ async () => {
+ renderTestComponents();
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
+
+ await waitFor(() => {
+ expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
+ });
+
+ fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
+
+ await waitFor(() => {
+ expect(screen.queryByTestId(OPEN_FLYOUT_BUTTON_TEST_ID)).not.toBeInTheDocument();
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
});
+ });
+
+ describe('Leading actions - pin', () => {
+ describe('securitySolutionNotesEnabled = true', () => {
+ beforeEach(() => {
+ (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
+ jest.fn((feature: keyof ExperimentalFeatures) => {
+ if (feature === 'securitySolutionNotesEnabled') {
+ return true;
+ }
+ return allowedExperimentalValues[feature];
+ })
+ );
+ });
+ it(
+ 'should disable pinning when event has notes attached in timeline',
+ async () => {
+ const mockStateWithNoteInTimeline = {
+ ...mockGlobalState,
+ timeline: {
+ ...mockGlobalState.timeline,
+ timelineById: {
+ [TimelineId.test]: {
+ ...mockGlobalState.timeline.timelineById[TimelineId.test],
+ savedObjectId: 'timeline-1', // match timelineId in mocked notes data
+ pinnedEventIds: { '1': true },
+ },
+ },
+ },
+ };
- it('it shows the timeline footer', async () => {
- const wrapper = await getWrapper(
-
-
-
+ render(
+
+
+
+ );
+
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
+
+ expect(screen.getAllByTestId('pin')).toHaveLength(1);
+ // disabled because it is already pinned
+ expect(screen.getByTestId('pin')).toBeDisabled();
+
+ fireEvent.mouseOver(screen.getByTestId('pin'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('timeline-action-pin-tool-tip')).toBeVisible();
+ expect(screen.getByTestId('timeline-action-pin-tool-tip')).toHaveTextContent(
+ 'This event cannot be unpinned because it has notes in Timeline'
+ );
+ /*
+ * Above event is alert and not an event but `getEventType` in
+ *x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx
+ * returns it has event and not an alert even though, it has event.kind as signal.
+ * Need to see if it is okay
+ *
+ * */
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
);
- expect(wrapper.find('[data-test-subj="timeline-footer"]').exists()).toEqual(true);
+ it(
+ 'should allow pinning when event has notes but notes are not attached in current timeline',
+ async () => {
+ renderTestComponents();
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
+
+ expect(screen.getAllByTestId('pin')).toHaveLength(1);
+ expect(screen.getByTestId('pin')).not.toBeDisabled();
+
+ fireEvent.mouseOver(screen.getByTestId('pin'));
+ await waitFor(() => {
+ expect(screen.getByTestId('timeline-action-pin-tool-tip')).toBeVisible();
+ expect(screen.getByTestId('timeline-action-pin-tool-tip')).toHaveTextContent(
+ 'Pin event'
+ );
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
});
- test('it does render the timeline table when the source is loading with no events', async () => {
- (useSourcererDataView as jest.Mock).mockReturnValue({
- browserFields: {},
- loading: true,
- indexPattern: {},
- selectedPatterns: [],
- missingPatterns: [],
+ describe('securitySolutionNotesEnabled = false', () => {
+ beforeEach(() => {
+ (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
+ jest.fn((feature: keyof ExperimentalFeatures) => {
+ if (feature === 'securitySolutionNotesEnabled') {
+ return false;
+ }
+ return allowedExperimentalValues[feature];
+ })
+ );
});
- const wrapper = await getWrapper(
-
-
-
- );
- expect(
- wrapper.find(`[data-test-subj="${TimelineTabs.query}-events-table"]`).exists()
- ).toEqual(true);
- expect(wrapper.find('[data-test-subj="events"]').exists()).toEqual(false);
+ it(
+ 'should have the pin button with correct tooltip',
+ async () => {
+ renderTestComponents();
+
+ expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
+
+ expect(screen.getAllByTestId('pin')).toHaveLength(1);
+ // disabled because it is already pinned
+ expect(screen.getByTestId('pin')).toBeDisabled();
+
+ fireEvent.mouseOver(screen.getByTestId('pin'));
+
+ await waitFor(() => {
+ expect(screen.getByTestId('timeline-action-pin-tool-tip')).toBeVisible();
+ expect(screen.getByTestId('timeline-action-pin-tool-tip')).toHaveTextContent(
+ 'This event cannot be unpinned because it has notes'
+ );
+ /*
+ * Above event is alert and not an event but `getEventType` in
+ * x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx
+ * returns it has event and not an alert even though, it has event.kind as signal.
+ * Need to see if it is okay
+ *
+ * */
+ });
+ },
+ SPECIAL_TEST_TIMEOUT
+ );
});
});
});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx
index 42463e13f24ac..478c13db7de73 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx
@@ -27,18 +27,13 @@ import { InputsModelId } from '../../../../../common/store/inputs/constants';
import { useInvalidFilterQuery } from '../../../../../common/hooks/use_invalid_filter_query';
import { timelineActions, timelineSelectors } from '../../../../store';
import type { Direction } from '../../../../../../common/search_strategy';
-import type { ControlColumnProps } from '../../../../../../common/types';
import { useTimelineEvents } from '../../../../containers';
import { useKibana } from '../../../../../common/lib/kibana';
-import { StatefulBody } from '../../body';
-import { Footer, footerHeight } from '../../footer';
import { QueryTabHeader } from './header';
-import { calculateTotalPages } from '../../helpers';
import { combineQueries } from '../../../../../common/lib/kuery';
import { TimelineRefetch } from '../../refetch_timeline';
import type { KueryFilterQueryKind } from '../../../../../../common/types/timeline';
import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline';
-import { EventDetailsWidthProvider } from '../../../../../common/components/events_viewer/event_details_width_context';
import type { inputsModel, State } from '../../../../../common/store';
import { inputsSelectors } from '../../../../../common/store';
import { SourcererScopeName } from '../../../../../sourcerer/store/model';
@@ -48,17 +43,7 @@ import { isActiveTimeline } from '../../../../../helpers';
import type { TimelineModel } from '../../../../store/model';
import { UnifiedTimelineBody } from '../../body/unified_timeline_body';
-import {
- FullWidthFlexGroup,
- ScrollableFlexItem,
- StyledEuiFlyoutBody,
- StyledEuiFlyoutFooter,
-} from '../shared/layout';
-import {
- TIMELINE_EMPTY_EVENTS,
- isTimerangeSame,
- timelineEmptyTrailingControlColumns,
-} from '../shared/utils';
+import { isTimerangeSame } from '../shared/utils';
import type { TimelineTabCommonProps } from '../shared/types';
import { useTimelineColumns } from '../shared/use_timeline_columns';
import { useTimelineControlColumn } from '../shared/use_timeline_control_columns';
@@ -113,10 +98,6 @@ export const QueryTabContentComponent: React.FC = ({
query: { filterManager: timelineFilterManager },
} = timelineDataService;
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
-
const getManageTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
const currentTimeline = useDeepEqualSelector((state) =>
@@ -195,7 +176,7 @@ export const QueryTabContentComponent: React.FC = ({
id: timelineId,
indexNames: selectedPatterns,
language: kqlQuery.language,
- limit: !unifiedComponentsInTimelineDisabled ? sampleSize : itemsPerPage,
+ limit: sampleSize,
runtimeMappings: sourcererDataView?.runtimeFieldMap as RunTimeMappings,
skip: !canQueryTimeline,
sort: timelineQuerySortField,
@@ -347,53 +328,6 @@ export const QueryTabContentComponent: React.FC = ({
);
}, [associateNote, closeNotesFlyout, isNotesFlyoutVisible, noteEventId, notes, timelineId]);
- if (!unifiedComponentsInTimelineDisabled) {
- return (
- <>
-
- {NotesFlyoutMemo}
-
-
- }
- columns={augmentedColumnHeaders}
- rowRenderers={rowRenderers}
- timelineId={timelineId}
- itemsPerPage={itemsPerPage}
- itemsPerPageOptions={itemsPerPageOptions}
- sort={sort}
- events={events}
- refetch={refetch}
- dataLoadingState={dataLoadingState}
- totalCount={isBlankTimeline ? 0 : totalCount}
- leadingControlColumns={leadingControlColumns as EuiDataGridControlColumn[]}
- onChangePage={loadPage}
- activeTab={activeTab}
- updatedAt={refreshedAt}
- isTextBasedQuery={false}
- pageInfo={pageInfo}
- />
- >
- );
- }
-
return (
<>
= ({
skip={!canQueryTimeline}
/>
{NotesFlyoutMemo}
-
-
+
+ = ({
showEventsCountBadge={showEventsCountBadge}
totalCount={totalCount}
/>
-
-
-
-
-
-
- {!isBlankTimeline && (
-
- )}
-
-
-
-
+ }
+ columns={augmentedColumnHeaders}
+ rowRenderers={rowRenderers}
+ timelineId={timelineId}
+ itemsPerPage={itemsPerPage}
+ itemsPerPageOptions={itemsPerPageOptions}
+ sort={sort}
+ events={events}
+ refetch={refetch}
+ dataLoadingState={dataLoadingState}
+ totalCount={isBlankTimeline ? 0 : totalCount}
+ leadingControlColumns={leadingControlColumns as EuiDataGridControlColumn[]}
+ onChangePage={loadPage}
+ activeTab={activeTab}
+ updatedAt={refreshedAt}
+ isTextBasedQuery={false}
+ pageInfo={pageInfo}
+ />
>
);
};
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/query_tab_unified_components.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/query_tab_unified_components.test.tsx
deleted file mode 100644
index 107c166183647..0000000000000
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/query_tab_unified_components.test.tsx
+++ /dev/null
@@ -1,1210 +0,0 @@
-/*
- * 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 { ComponentProps, FunctionComponent } from 'react';
-import React, { useEffect } from 'react';
-import QueryTabContent from '.';
-import { defaultRowRenderers } from '../../body/renderers';
-import { TimelineId } from '../../../../../../common/types/timeline';
-import { useTimelineEvents } from '../../../../containers';
-import { useTimelineEventsDetails } from '../../../../containers/details';
-import { useSourcererDataView } from '../../../../../sourcerer/containers';
-import { mockSourcererScope } from '../../../../../sourcerer/containers/mocks';
-import {
- createMockStore,
- createSecuritySolutionStorageMock,
- mockGlobalState,
- mockTimelineData,
- TestProviders,
-} from '../../../../../common/mock';
-import { DefaultCellRenderer } from '../../cell_rendering/default_cell_renderer';
-import { render, screen, waitFor, fireEvent, within, cleanup } from '@testing-library/react';
-import { createStartServicesMock } from '../../../../../common/lib/kibana/kibana_react.mock';
-import type { StartServices } from '../../../../../types';
-import { useKibana } from '../../../../../common/lib/kibana';
-import { useDispatch } from 'react-redux';
-import type { ExperimentalFeatures } from '../../../../../../common';
-import { allowedExperimentalValues } from '../../../../../../common';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { defaultUdtHeaders } from '../../unified_components/default_headers';
-import { defaultColumnHeaderType } from '../../body/column_headers/default_headers';
-import { useUserPrivileges } from '../../../../../common/components/user_privileges';
-import { getEndpointPrivilegesInitialStateMock } from '../../../../../common/components/user_privileges/endpoint/mocks';
-import * as timelineActions from '../../../../store/actions';
-import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
-import { createExpandableFlyoutApiMock } from '../../../../../common/mock/expandable_flyout';
-import { OPEN_FLYOUT_BUTTON_TEST_ID } from '../../../../../notes/components/test_ids';
-import { userEvent } from '@testing-library/user-event';
-
-jest.mock('../../../../../common/components/user_privileges');
-
-jest.mock('../../../../containers', () => ({
- useTimelineEvents: jest.fn(),
-}));
-
-jest.mock('../../../../containers/details');
-
-jest.mock('../../../fields_browser', () => ({
- useFieldBrowserOptions: jest.fn(),
-}));
-
-jest.mock('../../body/events', () => ({
- Events: () => <>>,
-}));
-
-jest.mock('../../../../../sourcerer/containers');
-jest.mock('../../../../../sourcerer/containers/use_signal_helpers', () => ({
- useSignalHelpers: () => ({ signalIndexNeedsInit: false }),
-}));
-
-jest.mock('../../../../../common/lib/kuery');
-
-jest.mock('../../../../../common/hooks/use_experimental_features');
-
-jest.mock('react-router-dom', () => ({
- ...jest.requireActual('react-router-dom'),
- useLocation: jest.fn(() => ({
- pathname: '',
- search: '',
- })),
-}));
-
-// These tests can take more than standard timeout of 5s
-// that is why we are increasing it.
-const SPECIAL_TEST_TIMEOUT = 50000;
-
-const useIsExperimentalFeatureEnabledMock = jest.fn((feature: keyof ExperimentalFeatures) => {
- if (feature === 'unifiedComponentsInTimelineDisabled') {
- return false;
- }
- return allowedExperimentalValues[feature];
-});
-
-jest.mock('../../../../../common/lib/kibana');
-
-// unified-field-list is reporting multiple analytics events
-jest.mock(`@elastic/ebt/client`);
-
-const mockOpenFlyout = jest.fn();
-const mockCloseFlyout = jest.fn();
-jest.mock('@kbn/expandable-flyout');
-
-const TestComponent = (props: Partial>) => {
- const testComponentDefaultProps: ComponentProps = {
- timelineId: TimelineId.test,
- renderCellValue: DefaultCellRenderer,
- rowRenderers: defaultRowRenderers,
- };
-
- const dispatch = useDispatch();
-
- useEffect(() => {
- // Unified field list can be a culprit for long load times, so we wait for the timeline to be interacted with to load
- dispatch(timelineActions.showTimeline({ id: TimelineId.test, show: true }));
-
- // populating timeline so that it is not blank
- dispatch(
- timelineActions.applyKqlFilterQuery({
- id: TimelineId.test,
- filterQuery: {
- kuery: {
- kind: 'kuery',
- expression: '*',
- },
- serializedQuery: '*',
- },
- })
- );
- }, [dispatch]);
-
- return ;
-};
-
-const customColumnOrder = [
- ...defaultUdtHeaders,
- {
- columnHeaderType: defaultColumnHeaderType,
- id: 'event.severity',
- },
-];
-
-const mockState = {
- ...structuredClone(mockGlobalState),
-};
-
-mockState.timeline.timelineById[TimelineId.test].columns = customColumnOrder;
-
-const TestWrapper: FunctionComponent> = ({ children }) => {
- return {children};
-};
-
-const renderTestComponents = (props?: Partial>) => {
- return render(, {
- wrapper: TestWrapper,
- });
-};
-
-const loadPageMock = jest.fn();
-
-const useSourcererDataViewMocked = jest.fn().mockReturnValue({
- ...mockSourcererScope,
-});
-
-const { storage: storageMock } = createSecuritySolutionStorageMock();
-
-let useTimelineEventsMock = jest.fn();
-
-describe('query tab with unified timeline', () => {
- beforeAll(() => {
- // https://github.com/atlassian/react-beautiful-dnd/blob/4721a518356f72f1dac45b5fd4ee9d466aa2996b/docs/guides/setup-problem-detection-and-error-recovery.md#disable-logging
-
- jest.mocked(useExpandableFlyoutApi).mockImplementation(() => ({
- ...createExpandableFlyoutApiMock(),
- openFlyout: mockOpenFlyout,
- closeFlyout: mockCloseFlyout,
- }));
-
- Object.defineProperty(window, '__@hello-pangea/dnd-disable-dev-warnings', {
- get() {
- return true;
- },
- });
- });
- const kibanaServiceMock: StartServices = {
- ...createStartServicesMock(),
- storage: storageMock,
- };
-
- afterEach(() => {
- jest.clearAllMocks();
- storageMock.clear();
- cleanup();
- localStorage.clear();
- });
-
- beforeEach(() => {
- useTimelineEventsMock = jest.fn(() => [
- false,
- {
- events: structuredClone(mockTimelineData.slice(0, 1)),
- pageInfo: {
- activePage: 0,
- totalPages: 3,
- },
- refreshedAt: Date.now(),
- totalCount: 3,
- loadPage: loadPageMock,
- },
- ]);
-
- HTMLElement.prototype.getBoundingClientRect = jest.fn(() => {
- return {
- width: 1000,
- height: 1000,
- x: 0,
- y: 0,
- } as DOMRect;
- });
-
- (useKibana as jest.Mock).mockImplementation(() => {
- return {
- services: kibanaServiceMock,
- };
- });
-
- (useTimelineEvents as jest.Mock).mockImplementation(useTimelineEventsMock);
-
- (useTimelineEventsDetails as jest.Mock).mockImplementation(() => [false, {}]);
-
- (useSourcererDataView as jest.Mock).mockImplementation(useSourcererDataViewMocked);
-
- (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
- useIsExperimentalFeatureEnabledMock
- );
-
- (useUserPrivileges as jest.Mock).mockReturnValue({
- kibanaSecuritySolutionsPrivileges: { crud: true, read: true },
- endpointPrivileges: getEndpointPrivilegesInitialStateMock(),
- detectionEnginePrivileges: { loading: false, error: undefined, result: undefined },
- });
- });
-
- describe('render', () => {
- it(
- 'should render unifiedDataTable in timeline',
- async () => {
- renderTestComponents();
- await waitFor(() => {
- expect(screen.getByTestId('discoverDocTable')).toBeVisible();
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
-
- it(
- 'should render unified-field-list in timeline',
- async () => {
- renderTestComponents();
- await waitFor(() => {
- expect(screen.getByTestId('timeline-sidebar')).toBeVisible();
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
- it(
- 'should show row-renderers correctly by default',
- async () => {
- renderTestComponents();
- await waitFor(() => {
- expect(screen.getByTestId('discoverDocTable')).toBeVisible();
- });
-
- expect(screen.getByTestId('timeline-row-renderer-0')).toBeVisible();
- },
-
- SPECIAL_TEST_TIMEOUT
- );
-
- it(
- 'should hide row-renderers when disabled',
- async () => {
- renderTestComponents();
- await waitFor(() => {
- expect(screen.getByTestId('discoverDocTable')).toBeVisible();
- });
-
- expect(screen.getByTestId('timeline-row-renderer-0')).toBeVisible();
-
- fireEvent.click(screen.getByTestId('show-row-renderers-gear'));
- expect(screen.getByTestId('row-renderers-modal')).toBeVisible();
-
- fireEvent.click(screen.getByTestId('disable-all'));
-
- expect(
- within(screen.getAllByTestId('renderer-checkbox')[0]).getByRole('checkbox')
- ).not.toBeChecked();
-
- fireEvent.click(screen.getByLabelText('Closes this modal window'));
-
- expect(screen.queryByTestId('row-renderers-modal')).not.toBeInTheDocument();
-
- expect(screen.queryByTestId('timeline-row-renderer-0')).not.toBeInTheDocument();
- },
- SPECIAL_TEST_TIMEOUT
- );
- });
-
- describe('pagination', () => {
- beforeEach(() => {
- // pagination tests need more than 1 record so here
- // we return 5 records instead of just 1.
- useTimelineEventsMock = jest.fn(() => [
- false,
- {
- events: structuredClone(mockTimelineData.slice(0, 5)),
- pageInfo: {
- activePage: 0,
- totalPages: 5,
- },
- refreshedAt: Date.now(),
- /*
- * `totalCount` could be any number w.r.t this test
- * and actually means total hits on elastic search
- * and not the fecthed number of records.
- *
- * This helps in testing `sampleSize` and `loadMore`
- */
- totalCount: 50,
- loadPage: loadPageMock,
- },
- ]);
-
- (useTimelineEvents as jest.Mock).mockImplementation(useTimelineEventsMock);
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- it(
- 'should paginate correctly',
- async () => {
- const mockStateWithNoteInTimeline = {
- ...mockGlobalState,
- timeline: {
- ...mockGlobalState.timeline,
- timelineById: {
- [TimelineId.test]: {
- ...mockGlobalState.timeline.timelineById[TimelineId.test],
- /* 1 record for each page */
- itemsPerPage: 1,
- itemsPerPageOptions: [1, 2, 3, 4, 5],
- savedObjectId: 'timeline-1', // match timelineId in mocked notes data
- pinnedEventIds: { '1': true },
- },
- },
- },
- };
-
- render(
-
-
-
- );
-
- expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
- expect(screen.getByTestId('pagination-button-previous')).toBeVisible();
-
- expect(screen.getByTestId('tablePaginationPopoverButton')).toHaveTextContent(
- 'Rows per page: 1'
- );
-
- expect(screen.getByTestId('pagination-button-0')).toHaveAttribute('aria-current', 'true');
- expect(screen.getByTestId('pagination-button-4')).toBeVisible();
- expect(screen.queryByTestId('pagination-button-5')).toBeNull();
-
- fireEvent.click(screen.getByTestId('pagination-button-4'));
-
- await waitFor(() => {
- expect(screen.getByTestId('pagination-button-4')).toHaveAttribute('aria-current', 'true');
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
-
- it(
- 'should load more records according to sample size correctly',
- async () => {
- const mockStateWithNoteInTimeline = {
- ...mockGlobalState,
- timeline: {
- ...mockGlobalState.timeline,
- timelineById: {
- [TimelineId.test]: {
- ...mockGlobalState.timeline.timelineById[TimelineId.test],
- itemsPerPage: 1,
- /*
- * `sampleSize` is the max number of records that are fetched from elasticsearch
- * in one request. If hits > sampleSize, you can fetch more records ( <= sampleSize)
- */
- sampleSize: 5,
- itemsPerPageOptions: [1, 2, 3, 4, 5],
- savedObjectId: 'timeline-1', // match timelineId in mocked notes data
- pinnedEventIds: { '1': true },
- },
- },
- },
- };
-
- render(
-
-
-
- );
-
- expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
-
- await waitFor(() => {
- expect(screen.getByTestId('pagination-button-0')).toHaveAttribute('aria-current', 'true');
- expect(screen.getByTestId('pagination-button-4')).toBeVisible();
- });
- // Go to last page
- fireEvent.click(screen.getByTestId('pagination-button-4'));
- await waitFor(() => {
- expect(screen.getByTestId('dscGridSampleSizeFetchMoreLink')).toBeVisible();
- });
- fireEvent.click(screen.getByTestId('dscGridSampleSizeFetchMoreLink'));
- expect(loadPageMock).toHaveBeenNthCalledWith(1, 1);
- },
- SPECIAL_TEST_TIMEOUT
- );
- });
-
- describe('columns', () => {
- it(
- 'should move column left/right correctly ',
- async () => {
- const { container } = renderTestComponents();
-
- await waitFor(() => {
- expect(screen.getByTestId('discoverDocTable')).toBeVisible();
- });
-
- const messageColumnIndex =
- customColumnOrder.findIndex((header) => header.id === 'message') + 3;
- // 3 is the offset for additional leading columns on left
-
- expect(container.querySelector('[data-gridcell-column-id="message"]')).toHaveAttribute(
- 'data-gridcell-column-index',
- String(messageColumnIndex)
- );
-
- expect(container.querySelector('[data-gridcell-column-id="message"]')).toBeInTheDocument();
-
- fireEvent.click(screen.getByTestId('dataGridHeaderCellActionButton-message'));
-
- await waitFor(() => {
- expect(screen.getByTitle('Move left')).toBeEnabled();
- });
-
- fireEvent.click(screen.getByTitle('Move left'));
-
- await waitFor(() => {
- expect(container.querySelector('[data-gridcell-column-id="message"]')).toHaveAttribute(
- 'data-gridcell-column-index',
- String(messageColumnIndex - 1)
- );
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
-
- it(
- 'should remove column',
- async () => {
- const { container } = renderTestComponents();
-
- await waitFor(() => {
- expect(screen.getByTestId('discoverDocTable')).toBeVisible();
- });
-
- expect(container.querySelector('[data-gridcell-column-id="message"]')).toBeInTheDocument();
-
- fireEvent.click(screen.getByTestId('dataGridHeaderCellActionButton-message'));
-
- await waitFor(() => {
- expect(screen.getByTitle('Remove column')).toBeVisible();
- });
-
- fireEvent.click(screen.getByTitle('Remove column'));
-
- await waitFor(() => {
- expect(
- container.querySelector('[data-gridcell-column-id="message"]')
- ).not.toBeInTheDocument();
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
-
- it(
- 'should sort date column',
- async () => {
- const { container } = renderTestComponents();
- await waitFor(() => {
- expect(screen.getByTestId('discoverDocTable')).toBeVisible();
- });
-
- expect(
- container.querySelector('[data-gridcell-column-id="@timestamp"]')
- ).toBeInTheDocument();
-
- fireEvent.click(screen.getByTestId('dataGridHeaderCellActionButton-@timestamp'));
-
- await waitFor(() => {
- expect(screen.getByTitle('Sort Old-New')).toBeVisible();
- });
- expect(screen.getByTitle('Unsort New-Old')).toBeVisible();
-
- useTimelineEventsMock.mockClear();
-
- fireEvent.click(screen.getByTitle('Sort Old-New'));
-
- await waitFor(() => {
- expect(useTimelineEventsMock).toHaveBeenNthCalledWith(
- 1,
- expect.objectContaining({
- sort: [
- {
- direction: 'asc',
- esTypes: ['date'],
- field: '@timestamp',
- type: 'date',
- },
- ],
- })
- );
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
-
- it(
- 'should sort string column correctly',
- async () => {
- const { container } = renderTestComponents();
- await waitFor(() => {
- expect(screen.getByTestId('discoverDocTable')).toBeVisible();
- });
-
- expect(
- container.querySelector('[data-gridcell-column-id="host.name"]')
- ).toBeInTheDocument();
-
- fireEvent.click(screen.getByTestId('dataGridHeaderCellActionButton-host.name'));
-
- await waitFor(() => {
- expect(screen.getByTestId('dataGridHeaderCellActionGroup-host.name')).toBeVisible();
- });
-
- expect(screen.getByTitle('Sort A-Z')).toBeVisible();
- expect(screen.getByTitle('Sort Z-A')).toBeVisible();
-
- useTimelineEventsMock.mockClear();
-
- fireEvent.click(screen.getByTitle('Sort A-Z'));
-
- await waitFor(() => {
- expect(useTimelineEventsMock).toHaveBeenNthCalledWith(
- 1,
- expect.objectContaining({
- sort: [
- {
- direction: 'desc',
- esTypes: ['date'],
- field: '@timestamp',
- type: 'date',
- },
- {
- direction: 'asc',
- esTypes: [],
- field: 'host.name',
- type: 'string',
- },
- ],
- })
- );
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
-
- it(
- 'should sort number column',
- async () => {
- const field = {
- name: 'event.severity',
- type: 'number',
- };
-
- const { container } = renderTestComponents();
- await waitFor(() => {
- expect(screen.getByTestId('discoverDocTable')).toBeVisible();
- });
-
- expect(
- container.querySelector(`[data-gridcell-column-id="${field.name}"]`)
- ).toBeInTheDocument();
-
- fireEvent.click(screen.getByTestId(`dataGridHeaderCellActionButton-${field.name}`));
-
- await waitFor(() => {
- expect(screen.getByTestId(`dataGridHeaderCellActionGroup-${field.name}`)).toBeVisible();
- });
-
- expect(screen.getByTitle('Sort Low-High')).toBeVisible();
- expect(screen.getByTitle('Sort High-Low')).toBeVisible();
-
- useTimelineEventsMock.mockClear();
-
- fireEvent.click(screen.getByTitle('Sort Low-High'));
-
- await waitFor(() => {
- expect(useTimelineEventsMock).toHaveBeenNthCalledWith(
- 1,
- expect.objectContaining({
- sort: [
- {
- direction: 'desc',
- esTypes: ['date'],
- field: '@timestamp',
- type: 'date',
- },
- {
- direction: 'asc',
- esTypes: [],
- field: field.name,
- type: field.type,
- },
- ],
- })
- );
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
- });
-
- describe('left controls', () => {
- it(
- 'should clear all sorting',
- async () => {
- renderTestComponents();
- expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
-
- expect(screen.getByTestId('dataGridColumnSortingButton')).toBeVisible();
- expect(
- within(screen.getByTestId('dataGridColumnSortingButton')).getByRole('marquee')
- ).toHaveTextContent('1');
-
- fireEvent.click(screen.getByTestId('dataGridColumnSortingButton'));
-
- // // timestamp sorting indicators
- expect(
- await screen.findByTestId('euiDataGridColumnSorting-sortColumn-@timestamp')
- ).toBeInTheDocument();
-
- expect(screen.getByTestId('dataGridHeaderCellSortingIcon-@timestamp')).toBeInTheDocument();
-
- fireEvent.click(screen.getByTestId('dataGridColumnSortingClearButton'));
-
- await waitFor(() => {
- expect(screen.queryByTestId('dataGridHeaderCellSortingIcon-@timestamp')).toBeNull();
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
-
- it(
- 'should be able to sort by multiple columns',
- async () => {
- renderTestComponents();
- expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
-
- expect(screen.getByTestId('dataGridColumnSortingButton')).toBeVisible();
- expect(
- within(screen.getByTestId('dataGridColumnSortingButton')).getByRole('marquee')
- ).toHaveTextContent('1');
-
- fireEvent.click(screen.getByTestId('dataGridColumnSortingButton'));
-
- // // timestamp sorting indicators
- expect(
- await screen.findByTestId('euiDataGridColumnSorting-sortColumn-@timestamp')
- ).toBeInTheDocument();
-
- expect(screen.getByTestId('dataGridHeaderCellSortingIcon-@timestamp')).toBeInTheDocument();
-
- // add more columns to sorting
- fireEvent.click(screen.getByText(/Pick fields to sort by/));
-
- await waitFor(() => {
- expect(
- screen.getByTestId('dataGridColumnSortingPopoverColumnSelection-event.severity')
- ).toBeInTheDocument();
- });
-
- fireEvent.click(
- screen.getByTestId('dataGridColumnSortingPopoverColumnSelection-event.severity')
- );
-
- // check new columns for sorting validity
- await waitFor(() => {
- expect(
- screen.getByTestId('dataGridHeaderCellSortingIcon-event.severity')
- ).toBeInTheDocument();
- });
- expect(
- screen.getByTestId('euiDataGridColumnSorting-sortColumn-event.severity')
- ).toBeInTheDocument();
-
- expect(screen.getByTestId('dataGridHeaderCellSortingIcon-@timestamp')).toBeInTheDocument();
- },
- SPECIAL_TEST_TIMEOUT
- );
- });
-
- describe('unified fields list', () => {
- it(
- 'should remove the column when clicked on X sign',
- async () => {
- const field = {
- name: 'event.severity',
- };
-
- renderTestComponents();
- expect(await screen.findByTestId('timeline-sidebar')).toBeVisible();
-
- await waitFor(() => {
- expect(screen.getByTestId('fieldListGroupedSelectedFields-count')).toHaveTextContent(
- String(customColumnOrder.length)
- );
- });
-
- // column exists in the table
- expect(screen.getByTestId(`dataGridHeaderCell-${field.name}`)).toBeVisible();
-
- fireEvent.click(screen.getAllByTestId(`fieldToggle-${field.name}`)[0]);
-
- // column not longer exists in the table
- await waitFor(() => {
- expect(screen.getByTestId('fieldListGroupedSelectedFields-count')).toHaveTextContent(
- String(customColumnOrder.length - 1)
- );
- });
- expect(screen.queryAllByTestId(`dataGridHeaderCell-${field.name}`)).toHaveLength(0);
- },
- SPECIAL_TEST_TIMEOUT
- );
-
- it(
- 'should add the column when clicked on ⊕ sign',
- async () => {
- const field = {
- name: 'agent.id',
- };
-
- renderTestComponents();
- expect(await screen.findByTestId('timeline-sidebar')).toBeVisible();
-
- await waitFor(() => {
- expect(screen.getByTestId('fieldListGroupedSelectedFields-count')).toHaveTextContent(
- String(customColumnOrder.length)
- );
- });
-
- expect(screen.queryAllByTestId(`dataGridHeaderCell-${field.name}`)).toHaveLength(0);
-
- // column exists in the table
- const availableFields = screen.getByTestId('fieldListGroupedAvailableFields');
-
- fireEvent.click(within(availableFields).getByTestId(`fieldToggle-${field.name}`));
-
- await waitFor(() => {
- expect(screen.getByTestId('fieldListGroupedSelectedFields-count')).toHaveTextContent(
- String(customColumnOrder.length + 1)
- );
- });
- expect(screen.queryAllByTestId(`dataGridHeaderCell-${field.name}`)).toHaveLength(1);
- },
- SPECIAL_TEST_TIMEOUT
- );
-
- it(
- 'should should show callout when field search does not matches any field',
- async () => {
- renderTestComponents();
- expect(await screen.findByTestId('timeline-sidebar')).toBeVisible();
-
- await waitFor(() => {
- expect(screen.getByTestId('fieldListGroupedAvailableFields-count')).toHaveTextContent(
- '37'
- );
- });
-
- fireEvent.change(screen.getByTestId('fieldListFiltersFieldSearch'), {
- target: { value: 'fake_field' },
- });
-
- await waitFor(() => {
- expect(
- screen.getByTestId('fieldListGroupedAvailableFieldsNoFieldsCallout-noFieldsMatch')
- ).toBeVisible();
- });
-
- expect(screen.getByTestId('fieldListGroupedAvailableFields-count')).toHaveTextContent('0');
- },
- SPECIAL_TEST_TIMEOUT
- );
-
- it(
- 'should toggle side bar correctly',
- async () => {
- renderTestComponents();
- expect(await screen.findByTestId('timeline-sidebar')).toBeVisible();
-
- expect(screen.getByTestId('fieldListGroupedFieldGroups')).toBeVisible();
-
- fireEvent.click(screen.getByTitle('Hide sidebar'));
-
- await waitFor(() => {
- expect(screen.queryByTestId('fieldListGroupedFieldGroups')).not.toBeInTheDocument();
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
- });
-
- describe('Leading actions - expand event', () => {
- it(
- 'should expand and collapse event correctly',
- async () => {
- renderTestComponents();
- expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
-
- expect(screen.getByTestId('docTableExpandToggleColumn').firstChild).toHaveAttribute(
- 'data-euiicon-type',
- 'expand'
- );
-
- // Open Flyout
- fireEvent.click(screen.getByTestId('docTableExpandToggleColumn'));
-
- await waitFor(() => {
- expect(mockOpenFlyout).toHaveBeenNthCalledWith(1, {
- right: {
- id: 'document-details-right',
- params: {
- id: '1',
- indexName: '',
- scopeId: TimelineId.test,
- },
- },
- });
- });
-
- expect(screen.getByTestId('docTableExpandToggleColumn').firstChild).toHaveAttribute(
- 'data-euiicon-type',
- 'minimize'
- );
-
- // Close Flyout
- fireEvent.click(screen.getByTestId('docTableExpandToggleColumn'));
-
- await waitFor(() => {
- expect(mockCloseFlyout).toHaveBeenNthCalledWith(1);
- expect(screen.getByTestId('docTableExpandToggleColumn').firstChild).toHaveAttribute(
- 'data-euiicon-type',
- 'expand'
- );
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
- });
-
- describe('Leading actions - notes', () => {
- describe('securitySolutionNotesEnabled = true', () => {
- beforeEach(() => {
- (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
- jest.fn((feature: keyof ExperimentalFeatures) => {
- if (feature === 'unifiedComponentsInTimelineDisabled') {
- return false;
- }
- if (feature === 'securitySolutionNotesEnabled') {
- return true;
- }
- return allowedExperimentalValues[feature];
- })
- );
- });
-
- // Flaky: https://github.com/elastic/kibana/issues/189794
- it.skip(
- 'should have the notification dot & correct tooltip',
- async () => {
- renderTestComponents();
- expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
-
- expect(screen.getAllByTestId('timeline-notes-button-small')).toHaveLength(1);
- expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
-
- expect(screen.getByTestId('timeline-notes-notification-dot')).toBeVisible();
-
- userEvent.hover(screen.getByTestId('timeline-notes-button-small'));
-
- await waitFor(() => {
- expect(screen.getByTestId('timeline-notes-tool-tip')).toBeInTheDocument();
- expect(screen.getByTestId('timeline-notes-tool-tip')).toHaveTextContent(
- '1 note available. Click to view it and add more.'
- );
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
- it(
- 'should be able to add notes through expandable flyout',
- async () => {
- renderTestComponents();
- expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
-
- await waitFor(() => {
- expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
- });
-
- fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
-
- await waitFor(() => {
- expect(mockOpenFlyout).toHaveBeenCalled();
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
- });
-
- describe('securitySolutionNotesEnabled = false', () => {
- beforeEach(() => {
- (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
- jest.fn((feature: keyof ExperimentalFeatures) => {
- if (feature === 'unifiedComponentsInTimelineDisabled') {
- return false;
- }
- if (feature === 'securitySolutionNotesEnabled') {
- return false;
- }
- return allowedExperimentalValues[feature];
- })
- );
- });
-
- it(
- 'should have the notification dot & correct tooltip',
- async () => {
- renderTestComponents();
- expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
-
- expect(screen.getAllByTestId('timeline-notes-button-small')).toHaveLength(1);
- expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
-
- expect(screen.getByTestId('timeline-notes-notification-dot')).toBeVisible();
-
- fireEvent.mouseOver(screen.getByTestId('timeline-notes-button-small'));
-
- await waitFor(() => {
- expect(screen.getByTestId('timeline-notes-tool-tip')).toBeVisible();
- expect(screen.getByTestId('timeline-notes-tool-tip')).toHaveTextContent(
- '1 note available. Click to view it and add more.'
- );
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
- it(
- 'should be able to add notes using EuiFlyout',
- async () => {
- renderTestComponents();
- expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
-
- await waitFor(() => {
- expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
- });
-
- fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
-
- await waitFor(() => {
- expect(screen.getByTestId('add-note-container')).toBeVisible();
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
-
- it(
- 'should cancel adding notes',
- async () => {
- renderTestComponents();
- expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
-
- await waitFor(() => {
- expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
- });
-
- fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
-
- await waitFor(() => {
- expect(screen.getByTestId('add-note-container')).toBeVisible();
- });
-
- expect(screen.getByTestId('cancel')).not.toBeDisabled();
-
- fireEvent.click(screen.getByTestId('cancel'));
-
- await waitFor(() => {
- expect(screen.queryByTestId('add-note-container')).not.toBeInTheDocument();
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
-
- it(
- 'should be able to delete notes',
- async () => {
- renderTestComponents();
- expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
-
- await waitFor(() => {
- expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
- });
-
- fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
-
- await waitFor(() => {
- expect(screen.getByTestId('delete-note')).toBeVisible();
- });
-
- const noteDeleteSpy = jest.spyOn(timelineActions, 'setConfirmingNoteId');
-
- fireEvent.click(screen.getByTestId('delete-note'));
-
- await waitFor(() => {
- expect(noteDeleteSpy).toHaveBeenCalled();
- expect(noteDeleteSpy).toHaveBeenCalledWith({
- confirmingNoteId: '1',
- id: TimelineId.test,
- });
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
-
- it(
- 'should not show toggle event details action',
- async () => {
- renderTestComponents();
- expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
-
- await waitFor(() => {
- expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
- });
-
- fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
-
- await waitFor(() => {
- expect(screen.queryByTestId(OPEN_FLYOUT_BUTTON_TEST_ID)).not.toBeInTheDocument();
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
- });
- });
-
- describe('Leading actions - pin', () => {
- describe('securitySolutionNotesEnabled = true', () => {
- beforeEach(() => {
- (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
- jest.fn((feature: keyof ExperimentalFeatures) => {
- if (feature === 'securitySolutionNotesEnabled') {
- return true;
- }
- return allowedExperimentalValues[feature];
- })
- );
- });
- it(
- 'should disable pinning when event has notes attached in timeline',
- async () => {
- const mockStateWithNoteInTimeline = {
- ...mockGlobalState,
- timeline: {
- ...mockGlobalState.timeline,
- timelineById: {
- [TimelineId.test]: {
- ...mockGlobalState.timeline.timelineById[TimelineId.test],
- savedObjectId: 'timeline-1', // match timelineId in mocked notes data
- pinnedEventIds: { '1': true },
- },
- },
- },
- };
-
- render(
-
-
-
- );
-
- expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
-
- expect(screen.getAllByTestId('pin')).toHaveLength(1);
- // disabled because it is already pinned
- expect(screen.getByTestId('pin')).toBeDisabled();
-
- fireEvent.mouseOver(screen.getByTestId('pin'));
-
- await waitFor(() => {
- expect(screen.getByTestId('timeline-action-pin-tool-tip')).toBeVisible();
- expect(screen.getByTestId('timeline-action-pin-tool-tip')).toHaveTextContent(
- 'This event cannot be unpinned because it has notes in Timeline'
- );
- /*
- * Above event is alert and not an event but `getEventType` in
- *x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx
- * returns it has event and not an alert even though, it has event.kind as signal.
- * Need to see if it is okay
- *
- * */
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
-
- it(
- 'should allow pinning when event has notes but notes are not attached in current timeline',
- async () => {
- renderTestComponents();
- expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
-
- expect(screen.getAllByTestId('pin')).toHaveLength(1);
- expect(screen.getByTestId('pin')).not.toBeDisabled();
-
- fireEvent.mouseOver(screen.getByTestId('pin'));
- await waitFor(() => {
- expect(screen.getByTestId('timeline-action-pin-tool-tip')).toBeVisible();
- expect(screen.getByTestId('timeline-action-pin-tool-tip')).toHaveTextContent(
- 'Pin event'
- );
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
- });
-
- describe('securitySolutionNotesEnabled = false', () => {
- beforeEach(() => {
- (useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
- jest.fn((feature: keyof ExperimentalFeatures) => {
- if (feature === 'securitySolutionNotesEnabled') {
- return false;
- }
- return allowedExperimentalValues[feature];
- })
- );
- });
-
- it(
- 'should have the pin button with correct tooltip',
- async () => {
- renderTestComponents();
-
- expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
-
- expect(screen.getAllByTestId('pin')).toHaveLength(1);
- // disabled because it is already pinned
- expect(screen.getByTestId('pin')).toBeDisabled();
-
- fireEvent.mouseOver(screen.getByTestId('pin'));
-
- await waitFor(() => {
- expect(screen.getByTestId('timeline-action-pin-tool-tip')).toBeVisible();
- expect(screen.getByTestId('timeline-action-pin-tool-tip')).toHaveTextContent(
- 'This event cannot be unpinned because it has notes'
- );
- /*
- * Above event is alert and not an event but `getEventType` in
- * x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx
- * returns it has event and not an alert even though, it has event.kind as signal.
- * Need to see if it is okay
- *
- * */
- });
- },
- SPECIAL_TEST_TIMEOUT
- );
- });
- });
-});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/__snapshots__/use_timeline_columns.test.ts.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/__snapshots__/use_timeline_columns.test.ts.snap
index fa44f008158ce..41a31431cb67f 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/__snapshots__/use_timeline_columns.test.ts.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/__snapshots__/use_timeline_columns.test.ts.snap
@@ -1,7 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`useTimelineColumns augmentedColumnHeaders should return the default columns 1`] = `Array []`;
-
exports[`useTimelineColumns augmentedColumnHeaders should return the default unified data table (udt) columns 1`] = `Array []`;
exports[`useTimelineColumns augmentedColumnHeaders should return the provided columns 1`] = `
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/__snapshots__/use_timeline_control_columns.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/__snapshots__/use_timeline_control_columns.test.tsx.snap
index e96dd27082bd0..48af9ed99c469 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/__snapshots__/use_timeline_control_columns.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/__snapshots__/use_timeline_control_columns.test.tsx.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`useTimelineColumns leadingControlColumns should return the leading control columns 1`] = `
+exports[`useTimelineControlColumns leadingControlColumns should return the leading control columns 1`] = `
Array [
Object {
"headerCellRender": [Function],
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_columns.test.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_columns.test.ts
index 53e6401747ca5..8bbda9a255a09 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_columns.test.ts
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_columns.test.ts
@@ -6,19 +6,15 @@
*/
import { TestProviders } from '../../../../../common/mock';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
import { renderHook } from '@testing-library/react-hooks';
import { useTimelineColumns } from './use_timeline_columns';
import { defaultUdtHeaders } from '../../unified_components/default_headers';
-import { defaultHeaders } from '../../body/column_headers/default_headers';
import type { ColumnHeaderOptions } from '../../../../../../common/types/timeline/columns';
jest.mock('../../../../../common/hooks/use_experimental_features', () => ({
useIsExperimentalFeatureEnabled: jest.fn().mockReturnValue(true),
}));
-const useIsExperimentalFeatureEnabledMock = useIsExperimentalFeatureEnabled as jest.Mock;
-
describe('useTimelineColumns', () => {
const mockColumns: ColumnHeaderOptions[] = [
{
@@ -33,15 +29,7 @@ describe('useTimelineColumns', () => {
},
];
describe('defaultColumns', () => {
- it('should return the default columns', () => {
- const { result } = renderHook(() => useTimelineColumns([]), {
- wrapper: TestProviders,
- });
- expect(result.current.defaultColumns).toEqual(defaultHeaders);
- });
-
it('should return the default unified data table (udt) columns', () => {
- useIsExperimentalFeatureEnabledMock.mockReturnValue(false);
const { result } = renderHook(() => useTimelineColumns([]), {
wrapper: TestProviders,
});
@@ -50,16 +38,7 @@ describe('useTimelineColumns', () => {
});
describe('localColumns', () => {
- it('should return the default columns', () => {
- useIsExperimentalFeatureEnabledMock.mockReturnValue(true);
- const { result } = renderHook(() => useTimelineColumns([]), {
- wrapper: TestProviders,
- });
- expect(result.current.localColumns).toEqual([]);
- });
-
it('should return the default unified data table (udt) columns', () => {
- useIsExperimentalFeatureEnabledMock.mockReturnValue(false);
const { result } = renderHook(() => useTimelineColumns([]), {
wrapper: TestProviders,
});
@@ -75,16 +54,7 @@ describe('useTimelineColumns', () => {
});
describe('augmentedColumnHeaders', () => {
- it('should return the default columns', () => {
- useIsExperimentalFeatureEnabledMock.mockReturnValue(false);
- const { result } = renderHook(() => useTimelineColumns([]), {
- wrapper: TestProviders,
- });
- expect(result.current.augmentedColumnHeaders).toMatchSnapshot();
- });
-
it('should return the default unified data table (udt) columns', () => {
- useIsExperimentalFeatureEnabledMock.mockReturnValue(false);
const { result } = renderHook(() => useTimelineColumns([]), {
wrapper: TestProviders,
});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_columns.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_columns.tsx
index 966039181561a..f42bf47c76423 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_columns.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_columns.tsx
@@ -8,8 +8,6 @@
import { useMemo } from 'react';
import { SourcererScopeName } from '../../../../../sourcerer/store/model';
import { useSourcererDataView } from '../../../../../sourcerer/containers';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { defaultHeaders } from '../../body/column_headers/default_headers';
import { requiredFieldsForActions } from '../../../../../detections/components/alerts_table/default_config';
import { defaultUdtHeaders } from '../../unified_components/default_headers';
import type { ColumnHeaderOptions } from '../../../../../../common/types';
@@ -18,16 +16,7 @@ import { memoizedGetTimelineColumnHeaders } from './utils';
export const useTimelineColumns = (columns: ColumnHeaderOptions[]) => {
const { browserFields } = useSourcererDataView(SourcererScopeName.timeline);
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
-
- const defaultColumns = useMemo(
- () => (!unifiedComponentsInTimelineDisabled ? defaultUdtHeaders : defaultHeaders),
- [unifiedComponentsInTimelineDisabled]
- );
-
- const localColumns = useMemo(() => columns ?? defaultColumns, [columns, defaultColumns]);
+ const localColumns = useMemo(() => columns ?? defaultUdtHeaders, [columns]);
const augmentedColumnHeaders = memoizedGetTimelineColumnHeaders(
localColumns,
@@ -43,11 +32,11 @@ export const useTimelineColumns = (columns: ColumnHeaderOptions[]) => {
return useMemo(
() => ({
- defaultColumns,
+ defaultColumns: defaultUdtHeaders,
localColumns,
augmentedColumnHeaders,
timelineQueryFieldsFromColumns,
}),
- [augmentedColumnHeaders, defaultColumns, timelineQueryFieldsFromColumns, localColumns]
+ [augmentedColumnHeaders, timelineQueryFieldsFromColumns, localColumns]
);
};
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_control_columns.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_control_columns.test.tsx
index bf064eb6ed480..efbe954250037 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_control_columns.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_control_columns.test.tsx
@@ -21,7 +21,7 @@ jest.mock('../../../../../common/hooks/use_license', () => ({
const useLicenseMock = useLicense as jest.Mock;
-describe('useTimelineColumns', () => {
+describe('useTimelineControlColumns', () => {
const mockColumns: ColumnHeaderOptions[] = [
{
columnHeaderType: 'not-filtered',
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_control_columns.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_control_columns.tsx
index beeaadb7829c8..ecbc8b75aeecb 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_control_columns.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/shared/use_timeline_control_columns.tsx
@@ -13,7 +13,6 @@ import { JEST_ENVIRONMENT } from '../../../../../../common/constants';
import { useLicense } from '../../../../../common/hooks/use_license';
import { SourcererScopeName } from '../../../../../sourcerer/store/model';
import { useSourcererDataView } from '../../../../../sourcerer/containers';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
import { getDefaultControlColumn } from '../../body/control_columns';
import type { UnifiedActionProps } from '../../unified_components/data_table/control_column_cell_render';
import type { TimelineTabs } from '../../../../../../common/types/timeline';
@@ -53,12 +52,8 @@ export const useTimelineControlColumn = ({
}: UseTimelineControlColumnArgs) => {
const { browserFields } = useSourcererDataView(SourcererScopeName.timeline);
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
-
const isEnterprisePlus = useLicense().isEnterprise();
- const ACTION_BUTTON_COUNT = isEnterprisePlus ? 6 : 5;
+ const ACTION_BUTTON_COUNT = useMemo(() => (isEnterprisePlus ? 6 : 5), [isEnterprisePlus]);
const { localColumns } = useTimelineColumns(columns);
const RowCellRender = useMemo(
@@ -116,45 +111,36 @@ export const useTimelineControlColumn = ({
// We need one less when the unified components are enabled because the document expand is provided by the unified data table
const UNIFIED_COMPONENTS_ACTION_BUTTON_COUNT = ACTION_BUTTON_COUNT - 1;
return useMemo(() => {
- if (!unifiedComponentsInTimelineDisabled) {
- return getDefaultControlColumn(UNIFIED_COMPONENTS_ACTION_BUTTON_COUNT).map((x) => ({
- ...x,
- headerCellRender: function HeaderCellRender(props: UnifiedActionProps) {
- return (
-
- );
- },
- rowCellRender: JEST_ENVIRONMENT ? RowCellRender : React.memo(RowCellRender),
- }));
- } else {
- return getDefaultControlColumn(ACTION_BUTTON_COUNT).map((x) => ({
- ...x,
- headerCellRender: HeaderActions,
- })) as unknown as ColumnHeaderOptions[];
- }
+ return getDefaultControlColumn(UNIFIED_COMPONENTS_ACTION_BUTTON_COUNT).map((x) => ({
+ ...x,
+ headerCellRender: function HeaderCellRender(props: UnifiedActionProps) {
+ return (
+
+ );
+ },
+ rowCellRender: JEST_ENVIRONMENT ? RowCellRender : React.memo(RowCellRender),
+ }));
}, [
- unifiedComponentsInTimelineDisabled,
UNIFIED_COMPONENTS_ACTION_BUTTON_COUNT,
browserFields,
localColumns,
sort,
activeTab,
timelineId,
- ACTION_BUTTON_COUNT,
RowCellRender,
]);
};
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/index.test.tsx
index 93524ac50e245..9703efd8d5bb3 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/index.test.tsx
@@ -67,9 +67,6 @@ jest.mock('react-router-dom', () => ({
}));
const useIsExperimentalFeatureEnabledMock = jest.fn((feature: keyof ExperimentalFeatures) => {
- if (feature === 'unifiedComponentsInTimelineDisabled') {
- return false;
- }
return allowedExperimentalValues[feature];
});
diff --git a/x-pack/plugins/security_solution/public/timelines/hooks/use_create_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/hooks/use_create_timeline.tsx
index e0783522f5dd1..527f372c1a447 100644
--- a/x-pack/plugins/security_solution/public/timelines/hooks/use_create_timeline.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/hooks/use_create_timeline.tsx
@@ -8,7 +8,6 @@
import { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { InputsModelId } from '../../common/store/inputs/constants';
-import { defaultHeaders } from '../components/timeline/body/column_headers/default_headers';
import { timelineActions } from '../store';
import { useTimelineFullScreen } from '../../common/containers/use_full_screen';
import { TimelineId } from '../../../common/types/timeline';
@@ -20,7 +19,6 @@ import { SourcererScopeName } from '../../sourcerer/store/model';
import { appActions } from '../../common/store/app';
import type { TimeRange } from '../../common/store/inputs/model';
import { useDiscoverInTimelineContext } from '../../common/components/discover_in_timeline/use_discover_in_timeline_context';
-import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features';
import { defaultUdtHeaders } from '../components/timeline/unified_components/default_headers';
import { timelineDefaults } from '../store/defaults';
@@ -50,9 +48,6 @@ export const useCreateTimeline = ({
onClick,
}: UseCreateTimelineParams): ((options?: { timeRange?: TimeRange }) => Promise) => {
const dispatch = useDispatch();
- const unifiedComponentsInTimelineDisabled = useIsExperimentalFeatureEnabled(
- 'unifiedComponentsInTimelineDisabled'
- );
const { id: dataViewId, patternList: selectedPatterns } = useSelector(
sourcererSelectors.defaultDataView
) ?? { id: '', patternList: [] };
@@ -87,7 +82,7 @@ export const useCreateTimeline = ({
dispatch(
timelineActions.createTimeline({
- columns: !unifiedComponentsInTimelineDisabled ? defaultUdtHeaders : defaultHeaders,
+ columns: defaultUdtHeaders,
dataViewId,
id,
indexNames: selectedPatterns,
@@ -95,7 +90,7 @@ export const useCreateTimeline = ({
timelineType,
updated: undefined,
excludedRowRendererIds:
- !unifiedComponentsInTimelineDisabled && timelineType !== TimelineTypeEnum.template
+ timelineType !== TimelineTypeEnum.template
? timelineDefaults.excludedRowRendererIds
: [],
})
@@ -132,7 +127,6 @@ export const useCreateTimeline = ({
setTimelineFullScreen,
timelineFullScreen,
timelineType,
- unifiedComponentsInTimelineDisabled,
]
);
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/fields_browser.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/fields_browser.cy.ts
deleted file mode 100644
index 68bab9778346f..0000000000000
--- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/fields_browser.cy.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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 {
- FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER,
- FIELDS_BROWSER_HEADER_HOST_GEO_CONTINENT_NAME_HEADER,
- FIELDS_BROWSER_MESSAGE_HEADER,
- FIELDS_BROWSER_FILTER_INPUT,
-} from '../../../screens/fields_browser';
-import { TIMELINE_FIELDS_BUTTON } from '../../../screens/timeline';
-
-import {
- addsHostGeoCityNameToTimeline,
- addsHostGeoContinentNameToTimeline,
- closeFieldsBrowser,
- filterFieldsBrowser,
- removesMessageField,
- resetFields,
-} from '../../../tasks/fields_browser';
-import { login } from '../../../tasks/login';
-import { visitWithTimeRange } from '../../../tasks/navigation';
-import { openTimelineUsingToggle } from '../../../tasks/security_main';
-import { openTimelineFieldsBrowser } from '../../../tasks/timeline';
-
-import { hostsUrl } from '../../../urls/navigation';
-
-describe(
- 'Fields Browser',
- {
- tags: ['@ess', '@serverless', '@skipInServerlessMKI'],
- env: {
- ftrConfig: {
- kbnServerArgs: [
- `--xpack.securitySolution.enableExperimental=${JSON.stringify([
- 'unifiedComponentsInTimelineDisabled',
- ])}`,
- ],
- },
- },
- },
- () => {
- beforeEach(() => {
- login();
- visitWithTimeRange(hostsUrl('allHosts'));
- openTimelineUsingToggle();
- openTimelineFieldsBrowser();
- });
-
- describe('Editing the timeline', () => {
- it('should add/remove columns from the alerts table when the user checks/un-checks them', () => {
- const filterInput = 'host.geo.c';
-
- cy.log('removing the message column');
-
- cy.get(FIELDS_BROWSER_MESSAGE_HEADER).should('exist');
-
- removesMessageField();
- closeFieldsBrowser();
-
- cy.get(FIELDS_BROWSER_MESSAGE_HEADER).should('not.exist');
-
- cy.log('add host.geo.city_name column');
-
- cy.get(FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER).should('not.exist');
-
- openTimelineFieldsBrowser();
- filterFieldsBrowser(filterInput);
- addsHostGeoCityNameToTimeline();
- closeFieldsBrowser();
-
- cy.get(FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER).should('exist');
- });
-
- it('should reset all fields in the timeline when `Reset Fields` is clicked', () => {
- const filterInput = 'host.geo.c';
-
- filterFieldsBrowser(filterInput);
-
- cy.get(FIELDS_BROWSER_HEADER_HOST_GEO_CONTINENT_NAME_HEADER).should('not.exist');
-
- addsHostGeoContinentNameToTimeline();
- closeFieldsBrowser();
-
- cy.get(FIELDS_BROWSER_HEADER_HOST_GEO_CONTINENT_NAME_HEADER).should('exist');
-
- openTimelineFieldsBrowser();
- resetFields();
-
- cy.get(FIELDS_BROWSER_HEADER_HOST_GEO_CONTINENT_NAME_HEADER).should('not.exist');
-
- cy.log('restores focus to the Customize Columns button when `Reset Fields` is clicked');
-
- cy.get(TIMELINE_FIELDS_BUTTON).should('have.focus');
-
- cy.log('restores focus to the Customize Columns button when Esc is pressed');
-
- openTimelineFieldsBrowser();
-
- cy.get(FIELDS_BROWSER_FILTER_INPUT).type('{esc}');
- cy.get(TIMELINE_FIELDS_BUTTON).should('have.focus');
- });
- });
- }
-);
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/pagination.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/pagination.cy.ts
deleted file mode 100644
index 150ab83f8aab1..0000000000000
--- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/pagination.cy.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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 {
- TIMELINE_EVENT,
- TIMELINE_EVENTS_COUNT_NEXT_PAGE,
- TIMELINE_EVENTS_COUNT_PER_PAGE,
- TIMELINE_EVENTS_COUNT_PER_PAGE_BTN,
- TIMELINE_EVENTS_COUNT_PER_PAGE_OPTION,
- TIMELINE_EVENTS_COUNT_PREV_PAGE,
- TIMELINE_FLYOUT,
-} from '../../../screens/timeline';
-
-import { login } from '../../../tasks/login';
-import { visitWithTimeRange } from '../../../tasks/navigation';
-import { openTimelineUsingToggle } from '../../../tasks/security_main';
-import { populateTimeline } from '../../../tasks/timeline';
-
-import { hostsUrl } from '../../../urls/navigation';
-
-// Flaky on serverless
-const defaultPageSize = 25;
-
-describe(
- 'Timeline Pagination',
- {
- /*
- * Tests with feature flag should not be enabled on serverless mki
- * so skipping it. When you remove the feature flag, remove the
- * skipInServerlessMKI tag as well.
- * */
- tags: ['@ess', '@serverless', '@skipInServerlessMKI'],
- env: {
- ftrConfig: {
- kbnServerArgs: [
- `--xpack.securitySolution.enableExperimental=${JSON.stringify([
- 'unifiedComponentsInTimelineDisabled',
- ])}`,
- ],
- },
- },
- },
- () => {
- beforeEach(() => {
- cy.task('esArchiverLoad', { archiveName: 'timeline' });
- login();
- visitWithTimeRange(hostsUrl('allHosts'));
- openTimelineUsingToggle();
- populateTimeline();
- });
-
- afterEach(() => {
- cy.task('esArchiverUnload', { archiveName: 'timeline' });
- });
-
- it(`should paginate records correctly`, () => {
- // should have ${defaultPageSize} events in the page by default
- cy.get(TIMELINE_EVENT).should('have.length', defaultPageSize);
-
- // should be able to go to next / previous page
- cy.get(`${TIMELINE_FLYOUT} ${TIMELINE_EVENTS_COUNT_NEXT_PAGE}`).first().click();
- cy.get(`${TIMELINE_FLYOUT} ${TIMELINE_EVENTS_COUNT_PREV_PAGE}`).first().click();
-
- // should select ${defaultPageSize} items per page by default
- cy.get(TIMELINE_EVENTS_COUNT_PER_PAGE).should('contain.text', defaultPageSize);
-
- // should be able to change items count per page with the dropdown
- const itemsPerPage = 100;
- cy.get(TIMELINE_EVENTS_COUNT_PER_PAGE_BTN).first().click();
- cy.get(TIMELINE_EVENTS_COUNT_PER_PAGE_OPTION(itemsPerPage)).click();
- cy.get(TIMELINE_EVENTS_COUNT_PER_PAGE).should('not.have.text', '0');
- cy.get(TIMELINE_EVENTS_COUNT_PER_PAGE)
- .invoke('text')
- .then((events) => {
- cy.wrap(parseInt(events, 10)).should('be.gt', defaultPageSize);
- });
- });
- }
-);
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/query_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/query_tab.cy.ts
deleted file mode 100644
index 09f3bad1963bf..0000000000000
--- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/query_tab.cy.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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 { getTimeline } from '../../../objects/timeline';
-
-import {
- UNLOCKED_ICON,
- PIN_EVENT,
- TIMELINE_FILTER,
- TIMELINE_QUERY,
- NOTE_CARD_CONTENT,
-} from '../../../screens/timeline';
-import { deleteTimelines } from '../../../tasks/api_calls/timelines';
-import { addNoteToTimeline } from '../../../tasks/api_calls/notes';
-import { createTimeline } from '../../../tasks/api_calls/timelines';
-
-import { login } from '../../../tasks/login';
-import { visit } from '../../../tasks/navigation';
-import {
- addFilter,
- addNoteToFirstRowEvent,
- openTimelineById,
- pinFirstEvent,
-} from '../../../tasks/timeline';
-
-import { TIMELINES_URL } from '../../../urls/navigation';
-
-const mockTimeline = getTimeline();
-
-describe(
- 'Timeline query tab',
- {
- tags: ['@ess', '@serverless', '@skipInServerlessMKI'],
- env: {
- ftrConfig: {
- kbnServerArgs: [
- `--xpack.securitySolution.enableExperimental=${JSON.stringify([
- 'unifiedComponentsInTimelineDisabled',
- ])}`,
- ],
- },
- },
- },
- () => {
- beforeEach(() => {
- login();
- visit(TIMELINES_URL);
- deleteTimelines();
- createTimeline(mockTimeline)
- .then((response) => response.body.data.persistTimeline.timeline.savedObjectId)
- .then((timelineId: string) => {
- cy.wrap(timelineId).as('timelineId');
- addNoteToTimeline(mockTimeline.notes, timelineId);
- openTimelineById(timelineId);
- pinFirstEvent();
- addFilter(mockTimeline.filter);
- });
- });
-
- it('should display the right query and filters', () => {
- cy.get(TIMELINE_QUERY).should('have.text', `${mockTimeline.query}`);
- cy.get(TIMELINE_FILTER(mockTimeline.filter)).should('exist');
- });
-
- it('should be able to add event note', () => {
- const note = 'event note';
- addNoteToFirstRowEvent(note);
- cy.get(NOTE_CARD_CONTENT).should('contain', 'event note');
- });
-
- it('should display pinned events', () => {
- cy.get(PIN_EVENT)
- .should('have.attr', 'aria-label')
- .and('match', /Unpin the event in row 2/);
- });
-
- it('should have an unlock icon', { tags: '@skipInServerless' }, () => {
- cy.get(UNLOCKED_ICON).should('be.visible');
- });
- }
-);
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/table_row_actions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/table_row_actions.cy.ts
deleted file mode 100644
index 400290ef1ed5a..0000000000000
--- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/table_row_actions.cy.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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 { getNewRule } from '../../../objects/rule';
-import { deleteAlertsAndRules } from '../../../tasks/api_calls/common';
-import { createRule } from '../../../tasks/api_calls/rules';
-import { login } from '../../../tasks/login';
-import { visitWithTimeRange } from '../../../tasks/navigation';
-import { openTimelineUsingToggle } from '../../../tasks/security_main';
-import { ALERTS_URL } from '../../../urls/navigation';
-import {
- createNewTimeline,
- executeTimelineKQL,
- executeTimelineSearch,
- openTimelineEventContextMenu,
-} from '../../../tasks/timeline';
-import { MARK_ALERT_ACKNOWLEDGED_BTN } from '../../../screens/alerts';
-import { GET_TIMELINE_GRID_CELL } from '../../../screens/timeline';
-
-describe(
- 'Timeline table Row Actions',
- {
- tags: ['@ess', '@serverless', '@skipInServerlessMKI'],
- env: {
- ftrConfig: {
- kbnServerArgs: [
- `--xpack.securitySolution.enableExperimental=${JSON.stringify([
- 'unifiedComponentsInTimelineDisabled',
- ])}`,
- ],
- },
- },
- },
- () => {
- beforeEach(() => {
- deleteAlertsAndRules();
- createRule(getNewRule());
- login();
- visitWithTimeRange(ALERTS_URL);
- openTimelineUsingToggle();
- createNewTimeline();
- executeTimelineSearch('*');
- });
-
- it('should refresh the table when alert status is changed', () => {
- executeTimelineKQL('kibana.alert.workflow_status:open');
- cy.get(GET_TIMELINE_GRID_CELL('@timestamp')).should('have.length', 1);
- openTimelineEventContextMenu();
- cy.get(MARK_ALERT_ACKNOWLEDGED_BTN).click();
- cy.get(GET_TIMELINE_GRID_CELL('@timestamp')).should('have.length', 0);
- });
- }
-);