Skip to content

Commit

Permalink
[Security Solution] Update timeline when sourcerer updates (#118958)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephmilovic authored and dmlemeshko committed Nov 29, 2021
1 parent 300f919 commit 0512641
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,26 @@ import { DragDropContextWrapper } from '../../../common/components/drag_and_drop
import '../../../common/mock/match_media';
import { mockBrowserFields, mockDocValueFields } from '../../../common/containers/source/mock';
import { TimelineId } from '../../../../common/types/timeline';
import { mockIndexNames, mockIndexPattern, TestProviders } from '../../../common/mock';
import {
mockGlobalState,
mockIndexNames,
mockIndexPattern,
TestProviders,
} from '../../../common/mock';

import { StatefulTimeline, Props as StatefulTimelineOwnProps } from './index';
import { useTimelineEvents } from '../../containers/index';
import { DefaultCellRenderer } from './cell_rendering/default_cell_renderer';
import { SELECTOR_TIMELINE_GLOBAL_CONTAINER } from './styles';
import { defaultRowRenderers } from './body/renderers';
import { useSourcererDataView } from '../../../common/containers/sourcerer';

jest.mock('../../containers/index', () => ({
useTimelineEvents: jest.fn(),
}));

jest.mock('./tabs_content');

jest.mock('../../../common/lib/kibana');
jest.mock('../../../common/components/url_state/normalize_time_range.ts');
jest.mock('@kbn/i18n/react', () => {
Expand Down Expand Up @@ -54,21 +62,29 @@ jest.mock('react-router-dom', () => {
useHistory: jest.fn(),
};
});
jest.mock('../../../common/containers/sourcerer', () => {
const originalModule = jest.requireActual('../../../common/containers/sourcerer');

const mockDispatch = jest.fn();

jest.mock('react-redux', () => {
const actual = jest.requireActual('react-redux');
return {
...originalModule,
useSourcererDataView: jest.fn().mockReturnValue({
browserFields: mockBrowserFields,
docValueFields: mockDocValueFields,
loading: false,
indexPattern: mockIndexPattern,
pageInfo: { activePage: 0, querySize: 0 },
selectedPatterns: mockIndexNames,
}),
...actual,
useDispatch: () => mockDispatch,
};
});

const mockUseSourcererDataView: jest.Mock = useSourcererDataView as jest.Mock;
jest.mock('../../../common/containers/sourcerer');
const mockDataView = {
dataViewId: mockGlobalState.timeline.timelineById.test?.dataViewId,
browserFields: mockBrowserFields,
docValueFields: mockDocValueFields,
loading: false,
indexPattern: mockIndexPattern,
pageInfo: { activePage: 0, querySize: 0 },
selectedPatterns: mockGlobalState.timeline.timelineById.test?.indexNames,
};
mockUseSourcererDataView.mockReturnValue(mockDataView);
describe('StatefulTimeline', () => {
const props: StatefulTimelineOwnProps = {
renderCellValue: DefaultCellRenderer,
Expand All @@ -77,6 +93,7 @@ describe('StatefulTimeline', () => {
};

beforeEach(() => {
jest.clearAllMocks();
(useTimelineEvents as jest.Mock).mockReturnValue([
false,
{
Expand All @@ -97,6 +114,25 @@ describe('StatefulTimeline', () => {
</TestProviders>
);
expect(wrapper.find('[data-test-subj="timeline"]')).toBeTruthy();
expect(mockDispatch).toBeCalledTimes(1);
});

test('data view updates, updates timeline', () => {
mockUseSourcererDataView.mockReturnValue({ ...mockDataView, selectedPatterns: mockIndexNames });
mount(
<TestProviders>
<StatefulTimeline {...props} />
</TestProviders>
);
expect(mockDispatch).toBeCalledTimes(2);
expect(mockDispatch).toHaveBeenNthCalledWith(2, {
payload: {
id: 'test',
dataViewId: mockDataView.dataViewId,
indexNames: mockIndexNames,
},
type: 'x-pack/security_solution/local/timeline/UPDATE_DATA_VIEW',
});
});

test(`it add attribute data-timeline-id in ${SELECTOR_TIMELINE_GLOBAL_CONTAINER}`, () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,16 @@ const StatefulTimelineComponent: React.FC<Props> = ({
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
const { dataViewId, selectedPatterns } = useSourcererDataView(SourcererScopeName.timeline);

const { graphEventId, savedObjectId, timelineType, description } = useDeepEqualSelector((state) =>
const {
dataViewId: dataViewIdCurrent,
indexNames: selectedPatternsCurrent,
graphEventId,
savedObjectId,
timelineType,
description,
} = useDeepEqualSelector((state) =>
pick(
['graphEventId', 'savedObjectId', 'timelineType', 'description'],
['indexNames', 'dataViewId', 'graphEventId', 'savedObjectId', 'timelineType', 'description'],
getTimeline(state, timelineId) ?? timelineDefaults
)
);
Expand All @@ -88,6 +95,38 @@ const StatefulTimelineComponent: React.FC<Props> = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const onDataViewChange = useCallback(() => {
if (
// initial state will get set on create
(dataViewIdCurrent === '' && selectedPatternsCurrent.length === 0) ||
// don't update if no change
(dataViewIdCurrent === dataViewId &&
selectedPatternsCurrent.sort().join() === selectedPatterns.sort().join())
) {
return;
}

dispatch(
timelineActions.updateDataView({
id: timelineId,
dataViewId,
indexNames: selectedPatterns,
})
);
}, [
dataViewId,
dataViewIdCurrent,
dispatch,
selectedPatterns,
selectedPatternsCurrent,
timelineId,
]);

useEffect(() => {
onDataViewChange();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dataViewId, selectedPatterns]);

const onSkipFocusBeforeEventsTable = useCallback(() => {
const exitFullScreenButton = containerElement.current?.querySelector<HTMLButtonElement>(
EXIT_FULL_SCREEN_CLASS_NAME
Expand Down

0 comments on commit 0512641

Please sign in to comment.