Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution][Sourcerer] Replace Sourcerer with Discover Data View Picker #210585

Open
wants to merge 96 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 80 commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
6a1d3e3
--wip-- [skip ci]
lgestc Feb 11, 2025
f26acdf
--wip-- [skip ci]
lgestc Feb 11, 2025
cc44f6c
--wip-- [skip ci]
lgestc Feb 11, 2025
d0c1970
--wip-- [skip ci]
lgestc Feb 11, 2025
99f434a
--wip-- [skip ci]
lgestc Feb 11, 2025
85d7233
--wip-- [skip ci]
lgestc Feb 11, 2025
73db16d
--wip-- [skip ci]
lgestc Feb 11, 2025
eadb7e6
--wip-- [skip ci]
lgestc Feb 11, 2025
a0b30c7
--wip-- [skip ci]
lgestc Feb 11, 2025
73ee667
--wip-- [skip ci]
lgestc Feb 11, 2025
df8d885
--wip-- [skip ci]
lgestc Feb 11, 2025
6cf58d6
fix stephbeat
lgestc Feb 11, 2025
c39eb3b
--wip-- [skip ci]
lgestc Feb 12, 2025
7020ef4
--wip-- [skip ci]
lgestc Feb 12, 2025
3d47008
--wip-- [skip ci]
lgestc Feb 12, 2025
83667ba
--wip-- [skip ci]
lgestc Feb 12, 2025
28aa4f0
--wip-- [skip ci]
lgestc Feb 12, 2025
f92eab5
--wip-- [skip ci]
lgestc Feb 12, 2025
0805163
--wip-- [skip ci]
lgestc Feb 12, 2025
aeeaf71
--wip-- [skip ci]
lgestc Feb 12, 2025
f49c643
fix types
lgestc Feb 14, 2025
1f4aebf
Merge branch 'main' into discover_dp
lgestc Feb 14, 2025
63054f5
remove todo
lgestc Feb 14, 2025
c7e0665
fixing tests
lgestc Feb 14, 2025
f261ab8
remove unused import
lgestc Feb 14, 2025
8c3e9a0
--wip-- [skip ci]
lgestc Feb 14, 2025
23439aa
--wip-- [skip ci]
lgestc Feb 14, 2025
770993c
--wip-- [skip ci]
lgestc Feb 14, 2025
fa80256
fix data view test
lgestc Feb 17, 2025
2baf50b
fix create timeline tests
lgestc Feb 17, 2025
40b395c
fix unit tests
lgestc Feb 17, 2025
c26acc7
fix timeline pages tests
lgestc Feb 17, 2025
1de086e
fix header tests
lgestc Feb 17, 2025
b088e6b
fix tests
lgestc Feb 17, 2025
c85dec4
Merge branch 'main' into discover_dp
lgestc Feb 17, 2025
09daf62
fix new timeline button tests
lgestc Feb 17, 2025
829f255
update unified tests
lgestc Feb 17, 2025
639fee5
Merge branch 'main' into discover_dp
lgestc Feb 17, 2025
8e1dee0
support prefilling multiple scopes
lgestc Feb 17, 2025
10c8e8e
--wip-- [skip ci]
lgestc Feb 18, 2025
1b8774f
fix broken test
lgestc Feb 18, 2025
d58905c
Merge branch 'main' into discover_dp
lgestc Feb 18, 2025
0fc0db5
--wip-- [skip ci]
lgestc Feb 18, 2025
77be180
--wip-- [skip ci]
lgestc Feb 18, 2025
19a8011
skip osquery test
lgestc Feb 18, 2025
83b194a
fix flyout e2e
lgestc Feb 19, 2025
3b25db5
remove useEffect
lgestc Feb 19, 2025
815ba6e
fixing more tests
lgestc Feb 19, 2025
ac38c51
Merge branch 'main' into discover_dp
lgestc Feb 19, 2025
0e1e7a0
skip use_show_timeline tests based on the useDataView
lgestc Feb 19, 2025
d3814a0
skip test that should no longer apply to current sourcerer logic
lgestc Feb 20, 2025
0506aac
revert unused change
lgestc Feb 20, 2025
ed274ee
Merge branch 'main' into discover_dp
lgestc Feb 20, 2025
1ec5817
revert changes made to the timeline
lgestc Feb 20, 2025
d503cb6
Merge branch 'main' into discover_dp
lgestc Feb 21, 2025
3e0c647
add feature flag
lgestc Feb 21, 2025
90c5561
put the new dataview picker behind a flag
lgestc Feb 21, 2025
0f95d1e
Merge branch 'main' into discover_dp
lgestc Feb 24, 2025
4540f96
fix tests
lgestc Feb 24, 2025
abf09aa
fix tests
lgestc Feb 24, 2025
b3ecd5b
more test fixing
lgestc Feb 24, 2025
61e0787
fix more tests
lgestc Feb 24, 2025
787a836
fix esql test
lgestc Feb 24, 2025
871e2a3
add redux tests
lgestc Feb 25, 2025
eaabb1a
preload default data view for all scopes
lgestc Feb 25, 2025
ce3e2b6
fix tests
lgestc Feb 25, 2025
90fd3ae
more tests
lgestc Feb 26, 2025
128bfb9
add use_browser_fields test
lgestc Feb 26, 2025
517b20b
more tests
lgestc Feb 26, 2025
79868ca
add init listener test
lgestc Feb 26, 2025
742c340
add missing tests
lgestc Feb 27, 2025
1e82688
add useDataView test
lgestc Feb 27, 2025
dd84f99
more tests
lgestc Feb 27, 2025
bec10ae
more tests
lgestc Feb 28, 2025
ddf2573
Merge branch 'main' into discover_dp
lgestc Feb 28, 2025
5f68a90
add error test
lgestc Feb 28, 2025
baecafd
linter errors
lgestc Feb 28, 2025
38df1eb
Merge branch 'main' into discover_dp
lgestc Mar 3, 2025
119d6a2
remove middleware from data view picker
lgestc Mar 3, 2025
6ff0437
usecallback instead of usememo
lgestc Mar 3, 2025
d31acdc
fix shared loading state
lgestc Mar 3, 2025
5ac74b7
naming
lgestc Mar 4, 2025
bac953a
naming
lgestc Mar 4, 2025
7b9b2df
naming
lgestc Mar 4, 2025
c96766b
naming
lgestc Mar 4, 2025
f5beeca
update readme
lgestc Mar 4, 2025
a6381fd
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Mar 4, 2025
b804725
naming
lgestc Mar 5, 2025
23f23d6
remove eslint overrides
lgestc Mar 5, 2025
b904a03
use isEmpty from lodash
lgestc Mar 5, 2025
8eb497e
docs
lgestc Mar 5, 2025
33406cf
Merge branch 'main' into discover_dp
lgestc Mar 6, 2025
082fc42
remove indicesExist from the data view manager api
lgestc Mar 6, 2025
069393d
use isEmpty
lgestc Mar 6, 2025
ded2d6e
cache full data view
lgestc Mar 6, 2025
1cf669e
simplify useFullDataView api
lgestc Mar 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ export const allowedExperimentalValues = Object.freeze({
* Enables banner for informing users about changes in data collection.
*/
eventCollectionDataReductionBannerEnabled: false,

/** Enables new Data View Picker */
newDataViewPickerEnabled: false,
});

type ExperimentalConfigKeys = Array<keyof ExperimentalFeatures>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { useSetupDetectionEngineHealthApi } from '../../detection_engine/rule_mo
import { TopValuesPopover } from '../components/top_values_popover/top_values_popover';
import { AssistantOverlay } from '../../assistant/overlay';
import { useInitSourcerer } from '../../sourcerer/containers/use_init_sourcerer';
import { useInitDataViewPicker } from '../../data_view_picker/hooks/use_init_data_view_picker';

interface HomePageProps {
children: React.ReactNode;
Expand All @@ -38,6 +39,7 @@ const HomePageComponent: React.FC<HomePageProps> = ({ children }) => {
useUrlState();
useUpdateBrowserTitle();
useUpdateExecutionContext();
useInitDataViewPicker();

const { browserFields } = useSourcererDataView(getScopeFromPath(pathname));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { DataView } from '@kbn/data-views-plugin/common';
import { render, screen } from '@testing-library/react';
import { withDataView } from '.';
import { useGetScopedSourcererDataView } from '../../../sourcerer/components/use_get_sourcerer_data_view';
import { TestProviders } from '../../mock';

interface TestComponentProps {
dataView: DataView;
Expand Down Expand Up @@ -39,18 +40,18 @@ describe('withDataViewId', () => {
});
it('should render default error components when there is not fallback provided and dataViewId is null', async () => {
const RenderedComponent = withDataView(TestComponent);
render(<RenderedComponent />);
render(<RenderedComponent />, { wrapper: TestProviders });
expect(screen.getByTestId(TEST_ID.DATA_VIEW_ERROR_COMPONENT)).toBeVisible();
});
it('should render provided fallback and dataViewId is null', async () => {
const RenderedComponent = withDataView(TestComponent, <FallbackComponent />);
render(<RenderedComponent />);
render(<RenderedComponent />, { wrapper: TestProviders });
expect(screen.getByTestId(TEST_ID.FALLBACK_COMPONENT)).toBeVisible();
});
it('should render provided component when dataViewId is not null', async () => {
(useGetScopedSourcererDataView as jest.Mock).mockReturnValue({ id: 'test' });
const RenderedComponent = withDataView(TestComponent);
render(<RenderedComponent />);
render(<RenderedComponent />, { wrapper: TestProviders });
expect(screen.getByTestId(TEST_ID.TEST_COMPONENT)).toBeVisible();
expect(dataViewMockFn).toHaveBeenCalledWith({ id: 'test' });
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import React from 'react';
import type { ComponentType } from 'react';
import type { ReactElement } from 'react-markdown';
import type { DataView } from '@kbn/data-views-plugin/common';
import { DataViewPickerScopeName } from '../../../data_view_picker/constants';
import { useFullDataView } from '../../../data_view_picker/hooks/use_full_data_view';
import { DataViewErrorComponent } from './data_view_error';
import { useEnableExperimental } from '../../hooks/use_experimental_features';

import { useGetScopedSourcererDataView } from '../../../sourcerer/components/use_get_sourcerer_data_view';
import { SourcererScopeName } from '../../../sourcerer/store/model';

Expand All @@ -30,10 +34,19 @@ export const withDataView = <P extends WithDataViewArg>(
fallback?: ReactElement
) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing wrong with this pattern, but do you think using something like context would be better? Just a quick note, but still working through the code

Copy link
Contributor

@kqualters-elastic kqualters-elastic Mar 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HOCs in general are much harder to reason about imo compared to hooks, and introduce unique to this pattern issues. ComponentWithDataView can and should be memoized, we should also make it a pattern to preserve the displayName of the passed in component, the amount of unknowns popping up in the tree is beginning to be a little confusing. Full suggestion:

export const withDataView = <P extends WithDataViewArg>(
  Component: ComponentType<P & WithDataViewArg>,
  fallback?: ReactElement
) => {
  const ComponentWithDataView = memo((props: OmitDataView<P>) => {
    const dataView = useGetScopedSourcererDataView({
      sourcererScope: SourcererScopeName.timeline,
    });

    if (!dataView) {
      return fallback ?? <DataViewErrorComponent />;
    }

    return <Component {...(props as unknown as P)} dataView={dataView} />;
  });

  ComponentWithDataView.displayName = `withDataView(${
    Component.displayName || Component.name || 'Component'
  })`;

  return ComponentWithDataView;
};

const ComponentWithDataView = (props: OmitDataView<P>) => {
const dataView = useGetScopedSourcererDataView({
const experimentalDataView = useFullDataView({
dataViewPickerScope: DataViewPickerScopeName.timeline,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useFullDataView view called here and then dataView is getting replaced by experimentalDataView later only when newDataViewPickerEnabled is enabled.

It does look, that experimentalDataView is getting initiated every time, in disregard of feature flag.
Should it be avoided?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, avoiding use of let would achieve the same thing, and is imo a good rule of thumb to follow


let dataView = useGetScopedSourcererDataView({
sourcererScope: SourcererScopeName.timeline,
});

const { newDataViewPickerEnabled } = useEnableExperimental();
if (newDataViewPickerEnabled) {
dataView = experimentalDataView;
}

if (!dataView) {
return fallback ?? <DataViewErrorComponent />;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ export const useIsExperimentalFeatureEnabled = jest

throw new Error(`Invalid experimental value ${feature}}`);
});

export const useEnableExperimental = jest.fn(() => ({ newDataViewPickerEnabled: false }));
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@
import { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import type { Filter, Query } from '@kbn/es-query';
import { useSelectDataView } from '../../../data_view_picker/hooks/use_select_data_view';
import { useCreateTimeline } from '../../../timelines/hooks/use_create_timeline';
import { updateProviders, setFilters, applyKqlFilterQuery } from '../../../timelines/store/actions';
import { SourcererScopeName } from '../../../sourcerer/store/model';
import type { DataProvider } from '../../../../common/types';
import { sourcererSelectors } from '../../store';
import { sourcererActions } from '../../store/actions';
import { inputsActions } from '../../store/inputs';
import { InputsModelId } from '../../store/inputs/constants';
import type { TimeRange } from '../../store/inputs/model';
import { TimelineId } from '../../../../common/types/timeline';
import { TimelineTypeEnum } from '../../../../common/api/timeline';
import { sourcererActions } from '../../store/actions';
import { useEnableExperimental } from '../use_experimental_features';

interface InvestigateInTimelineArgs {
/**
Expand Down Expand Up @@ -57,6 +59,7 @@ export const useInvestigateInTimeline = () => {

const signalIndexName = useSelector(sourcererSelectors.signalIndexName);
const defaultDataView = useSelector(sourcererSelectors.defaultDataView);
const { newDataViewPickerEnabled } = useEnableExperimental();

const clearTimelineTemplate = useCreateTimeline({
timelineId: TimelineId.active,
Expand All @@ -68,6 +71,8 @@ export const useInvestigateInTimeline = () => {
timelineType: TimelineTypeEnum.default,
});

const setSelectedDataView = useSelectDataView();

const investigateInTimeline = useCallback(
async ({
query,
Expand Down Expand Up @@ -124,19 +129,35 @@ export const useInvestigateInTimeline = () => {
// Only show detection alerts
// (This is required so the timeline event count matches the prevalence count)
if (!keepDataView) {
dispatch(
sourcererActions.setSelectedDataView({
id: SourcererScopeName.timeline,
selectedDataViewId: defaultDataView.id,
selectedPatterns: [signalIndexName || ''],
})
);
if (newDataViewPickerEnabled) {
setSelectedDataView({
scope: [SourcererScopeName.timeline],
id: defaultDataView.id,
fallbackPatterns: [signalIndexName || ''],
});
} else {
dispatch(
sourcererActions.setSelectedDataView({
id: SourcererScopeName.timeline,
selectedDataViewId: defaultDataView.id,
selectedPatterns: [signalIndexName || ''],
})
);
}
}
// Unlock the time range from the global time range
dispatch(inputsActions.removeLinkTo([InputsModelId.timeline, InputsModelId.global]));
}
},
[clearTimelineTemplate, clearTimelineDefault, dispatch, defaultDataView.id, signalIndexName]
[
clearTimelineTemplate,
clearTimelineDefault,
dispatch,
newDataViewPickerEnabled,
setSelectedDataView,
defaultDataView.id,
signalIndexName,
]
);

return { investigateInTimeline };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { initialGroupingState } from '../store/grouping/reducer';
import type { SourcererState } from '../../sourcerer/store';
import { EMPTY_RESOLVER } from '../../resolver/store/helpers';
import { getMockDiscoverInTimelineState } from './mock_discover_state';
import { mockDataViewPickerState } from '../../data_view_picker/redux/mock';

const mockFieldMap: DataViewSpec['fields'] = Object.fromEntries(
mockIndexFields.map((field) => [field.name, field])
Expand Down Expand Up @@ -554,4 +555,5 @@ export const mockGlobalState: State = {
selectedIds: [],
pendingDeleteIds: [],
},
...mockDataViewPickerState,
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@

import type { CoreStart } from '@kbn/core/public';
import type { Storage } from '@kbn/kibana-utils-plugin/public';
import { createListenerMiddleware } from '@reduxjs/toolkit';

import { createTimelineMiddlewares } from '../../timelines/store/middlewares/create_timeline_middlewares';
import { dataTableLocalStorageMiddleware } from './data_table/middleware_local_storage';
import { userAssetTableLocalStorageMiddleware } from '../../explore/users/store/middleware_storage';

const listenerMiddleware = createListenerMiddleware();

export function createMiddlewares(kibana: CoreStart, storage: Storage) {
return [
listenerMiddleware.middleware,
dataTableLocalStorageMiddleware(storage),
userAssetTableLocalStorageMiddleware(storage),
...createTimelineMiddlewares(kibana),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ import { securitySolutionDiscoverReducer } from './discover/reducer';
import type { AnalyzerState } from '../../resolver/types';
import type { NotesState } from '../../notes/store/notes.slice';
import { notesReducer } from '../../notes/store/notes.slice';
import {
dataViewPickerReducer,
initialDataViewPickerState,
} from '../../data_view_picker/redux/reducer';

enableMapSet();

Expand Down Expand Up @@ -132,6 +136,7 @@ export const createInitialState = (
savedSearch: undefined,
},
notes: notesState,
dataViewPicker: initialDataViewPickerState.dataViewPicker,
};

return preloadedState;
Expand All @@ -155,4 +160,5 @@ export const createReducer: (
discover: securitySolutionDiscoverReducer,
...pluginsReducer,
notes: notesReducer,
dataViewPicker: dataViewPickerReducer,
});
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import type { GroupState } from './grouping/types';
import type { SecuritySolutionDiscoverState } from './discover/model';
import type { AnalyzerState } from '../../resolver/types';
import type { NotesState } from '../../notes/store/notes.slice';
import type { RootState as DataViewPickerState } from '../../data_view_picker/redux/reducer';

export type State = HostsPluginState &
UsersPluginState &
Expand All @@ -40,7 +41,7 @@ export type State = HostsPluginState &
discover: SecuritySolutionDiscoverState;
} & DataTableState &
GroupState &
AnalyzerState & { notes: NotesState };
AnalyzerState & { notes: NotesState } & DataViewPickerState;
/**
* The Redux store type for the Security app.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ import { appLinks } from '../../../app_links';
import { useUserPrivileges } from '../../components/user_privileges';
import { useShowTimeline } from './use_show_timeline';
import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks';
import { TestProviders } from '../../mock';
import { hasAccessToSecuritySolution } from '../../../helpers_access';

jest.mock('../../components/user_privileges');
jest.mock('../../../helpers_access', () => ({ hasAccessToSecuritySolution: jest.fn(() => true) }));

const mockUseLocation = jest.fn().mockReturnValue({ pathname: '/overview' });
jest.mock('react-router-dom', () => {
Expand All @@ -35,30 +38,11 @@ jest.mock('../../../sourcerer/containers', () => ({
useSourcererDataView: () => mockUseSourcererDataView(),
}));

const mockSiemUserCanRead = jest.fn(() => true);
jest.mock('../../lib/kibana', () => {
const original = jest.requireActual('../../lib/kibana');

return {
...original,
useKibana: () => ({
services: {
...original.useKibana().services,
application: {
capabilities: {
siemV2: {
show: mockSiemUserCanRead(),
},
},
},
},
}),
};
});

const mockUpselling = new UpsellingService();
const mockUiSettingsClient = uiSettingsServiceMock.createStartContract();

const renderUseShowTimeline = () => renderHook(() => useShowTimeline(), { wrapper: TestProviders });

describe('use show timeline', () => {
beforeAll(() => {
(useUserPrivileges as unknown as jest.Mock).mockReturnValue({
Expand All @@ -84,33 +68,33 @@ describe('use show timeline', () => {
});

it('shows timeline for routes on default', async () => {
const { result } = renderHook(() => useShowTimeline());
const { result } = renderUseShowTimeline();
await waitFor(() => expect(result.current).toEqual([true]));
});

it('hides timeline for blacklist routes', async () => {
mockUseLocation.mockReturnValueOnce({ pathname: '/rules/add_rules' });
const { result } = renderHook(() => useShowTimeline());
const { result } = renderUseShowTimeline();
await waitFor(() => expect(result.current).toEqual([false]));
});

it('shows timeline for partial blacklist routes', async () => {
mockUseLocation.mockReturnValueOnce({ pathname: '/rules' });
const { result } = renderHook(() => useShowTimeline());
const { result } = renderUseShowTimeline();
await waitFor(() => expect(result.current).toEqual([true]));
});

it('hides timeline for sub blacklist routes', async () => {
mockUseLocation.mockReturnValueOnce({ pathname: '/administration/policy' });
const { result } = renderHook(() => useShowTimeline());
const { result } = renderUseShowTimeline();
await waitFor(() => expect(result.current).toEqual([false]));
});
it('hides timeline for users without timeline access', async () => {
(useUserPrivileges as unknown as jest.Mock).mockReturnValue({
timelinePrivileges: { read: false },
});

const { result } = renderHook(() => useShowTimeline());
const { result } = renderUseShowTimeline();
const showTimeline = result.current;
expect(showTimeline).toEqual([false]);
});
Expand All @@ -120,41 +104,40 @@ it('shows timeline for users with timeline read access', async () => {
timelinePrivileges: { read: true },
});

const { result } = renderHook(() => useShowTimeline());
const { result } = renderUseShowTimeline();
const showTimeline = result.current;
expect(showTimeline).toEqual([true]);
});

describe('sourcererDataView', () => {
it('should show timeline when indices exist', () => {
mockUseSourcererDataView.mockReturnValueOnce({ indicesExist: true, dataViewId: 'test' });
const { result } = renderHook(() => useShowTimeline());
const { result } = renderUseShowTimeline();
expect(result.current).toEqual([true]);
});

it('should show timeline when dataViewId is null', () => {
mockUseSourcererDataView.mockReturnValueOnce({ indicesExist: false, dataViewId: null });
const { result } = renderHook(() => useShowTimeline());
const { result } = renderUseShowTimeline();
expect(result.current).toEqual([true]);
});

it('should not show timeline when dataViewId is not null and indices does not exist', () => {
mockUseSourcererDataView.mockReturnValueOnce({ indicesExist: false, dataViewId: 'test' });
const { result } = renderHook(() => useShowTimeline());
const { result } = renderUseShowTimeline();
expect(result.current).toEqual([false]);
});
});

describe('Security solution capabilities', () => {
it('should show timeline when user has read capabilities', () => {
mockSiemUserCanRead.mockReturnValueOnce(true);
const { result } = renderHook(() => useShowTimeline());
const { result } = renderUseShowTimeline();
expect(result.current).toEqual([true]);
});

it('should not show timeline when user does not have read capabilities', () => {
mockSiemUserCanRead.mockReturnValueOnce(false);
const { result } = renderHook(() => useShowTimeline());
jest.mocked(hasAccessToSecuritySolution).mockReturnValueOnce(false);
const { result } = renderUseShowTimeline();
expect(result.current).toEqual([false]);
});
});
Loading