Skip to content

Commit

Permalink
General improvements and refactor
Browse files Browse the repository at this point in the history
Move everything inside use_url_state and delete the folder
  • Loading branch information
machadoum committed Jul 21, 2022
1 parent c183739 commit afa6112
Show file tree
Hide file tree
Showing 46 changed files with 383 additions and 945 deletions.
136 changes: 120 additions & 16 deletions x-pack/plugins/security_solution/public/app/home/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import React from 'react';
import { HomePage } from '.';
import type { SavedQuery } from '@kbn/data-plugin/public';
import { FilterManager } from '@kbn/data-plugin/public';
import { CONSTANTS } from '../../common/components/url_state/constants';

import {
createSecuritySolutionStorageMock,
Expand All @@ -30,6 +29,9 @@ import type { Filter } from '@kbn/es-query';
import { createStore } from '../../common/store';
import type { TimeRange, UrlInputsModel } from '../../common/store/inputs/model';
import { SecurityPageName } from '../types';
import type { TimelineUrl } from '../../timelines/store/timeline/model';
import { timelineDefaults } from '../../timelines/store/timeline/defaults';
import { URL_PARAM_KEY } from '../../common/hooks/use_url_state';

jest.mock('../../common/store/inputs/actions');

Expand All @@ -51,12 +53,15 @@ const mockUseInitializeUrlParam = (urlParamKey: string, state: unknown) => {
});
};

const mockUpdateUrlParam = jest.fn();

jest.mock('../../common/utils/global_query_string', () => {
const original = jest.requireActual('../../common/utils/global_query_string');
return {
...original,
useInitializeUrlParam: (...params: unknown[]) => mockedUseInitializeUrlParam(...params),
useSyncGlobalQueryString: jest.fn(),
useUpdateUrlParam: () => mockUpdateUrlParam,
};
});

Expand All @@ -80,6 +85,23 @@ jest.mock('react-router-dom', () => {
};
});

const mockQueryTimelineById = jest.fn();
jest.mock('../../timelines/components/open_timeline/helpers', () => {
const original = jest.requireActual('../../timelines/components/open_timeline/helpers');
return {
...original,
queryTimelineById: (params: unknown) => mockQueryTimelineById(params),
};
});

const mockGetTimiline = jest.fn();

jest.mock('../../timelines/store/timeline', () => ({
timelineSelectors: {
getTimelineByIdSelector: () => mockGetTimiline,
},
}));

const mockedFilterManager = new FilterManager(coreMock.createStart().uiSettings);
const mockGetSavedQuery = jest.fn();

Expand Down Expand Up @@ -152,22 +174,22 @@ describe('HomePage', () => {
);

expect(mockedUseInitializeUrlParam).toHaveBeenCalledWith(
CONSTANTS.appQuery,
URL_PARAM_KEY.appQuery,
expect.any(Function)
);
expect(mockedUseInitializeUrlParam).toHaveBeenCalledWith(
CONSTANTS.filters,
URL_PARAM_KEY.filters,
expect.any(Function)
);
expect(mockedUseInitializeUrlParam).toHaveBeenCalledWith(
CONSTANTS.savedQuery,
URL_PARAM_KEY.savedQuery,
expect.any(Function)
);
});

it('dispatches setFilterQuery when initializing appQuery', () => {
const state = { query: 'testQuery', language: 'en' };
mockUseInitializeUrlParam(CONSTANTS.appQuery, state);
mockUseInitializeUrlParam(URL_PARAM_KEY.appQuery, state);

render(
<TestProviders>
Expand Down Expand Up @@ -208,7 +230,7 @@ describe('HomePage', () => {
};

mockGetSavedQuery.mockResolvedValue(savedQueryData);
mockUseInitializeUrlParam(CONSTANTS.savedQuery, state);
mockUseInitializeUrlParam(URL_PARAM_KEY.savedQuery, state);

render(
<TestProviders>
Expand Down Expand Up @@ -239,7 +261,7 @@ describe('HomePage', () => {
describe('Filters', () => {
it('sets filter initial value in the store and filterManager', () => {
const state = [{ testFilter: 'test' }];
mockUseInitializeUrlParam(CONSTANTS.filters, state);
mockUseInitializeUrlParam(URL_PARAM_KEY.filters, state);
const spySetFilters = jest.spyOn(mockedFilterManager, 'setFilters');

render(
Expand All @@ -260,7 +282,7 @@ describe('HomePage', () => {

it('sets filter from store when URL param has no value', () => {
const state = null;
mockUseInitializeUrlParam(CONSTANTS.filters, state);
mockUseInitializeUrlParam(URL_PARAM_KEY.filters, state);
const spySetAppFilters = jest.spyOn(mockedFilterManager, 'setAppFilters');
const { storage } = createSecuritySolutionStorageMock();

Expand Down Expand Up @@ -290,7 +312,7 @@ describe('HomePage', () => {

it('preserves pinned filters when URL param has no value', () => {
const state = null;
mockUseInitializeUrlParam(CONSTANTS.filters, state);
mockUseInitializeUrlParam(URL_PARAM_KEY.filters, state);
// pin filter
mockedFilterManager.setGlobalFilters([dummyFilter]);

Expand Down Expand Up @@ -325,16 +347,16 @@ describe('HomePage', () => {

const state: UrlInputsModel = {
global: {
[CONSTANTS.timerange]: timerange,
[URL_PARAM_KEY.timerange]: timerange,
linkTo: ['timeline'],
},
timeline: {
[CONSTANTS.timerange]: timerange,
[URL_PARAM_KEY.timerange]: timerange,
linkTo: ['global'],
},
};

mockUseInitializeUrlParam(CONSTANTS.timerange, state);
mockUseInitializeUrlParam(URL_PARAM_KEY.timerange, state);

render(
<TestProviders>
Expand Down Expand Up @@ -370,16 +392,16 @@ describe('HomePage', () => {

const state: UrlInputsModel = {
global: {
[CONSTANTS.timerange]: timerange,
[URL_PARAM_KEY.timerange]: timerange,
linkTo: ['timeline'],
},
timeline: {
[CONSTANTS.timerange]: timerange,
[URL_PARAM_KEY.timerange]: timerange,
linkTo: ['global'],
},
};

mockUseInitializeUrlParam(CONSTANTS.timerange, state);
mockUseInitializeUrlParam(URL_PARAM_KEY.timerange, state);

render(
<TestProviders>
Expand Down Expand Up @@ -496,7 +518,6 @@ describe('HomePage', () => {
</TestProviders>
);

// mockedUseInitializeUrlParam.mockImplementation(jest.fn());
const { rerender } = render(<TestComponent />);
jest.clearAllMocks();

Expand All @@ -519,4 +540,87 @@ describe('HomePage', () => {
});
});
});

describe('Timeline', () => {
it('initializes Timeline store', () => {
const timeline: TimelineUrl = {
id: 'testSavedTimelineId',
isOpen: false,
};

mockUseInitializeUrlParam(URL_PARAM_KEY.timeline, timeline);

render(
<TestProviders>
<HomePage onAppLeave={jest.fn()} setHeaderActionMenu={jest.fn()}>
<span />
</HomePage>
</TestProviders>
);

expect(mockQueryTimelineById).toHaveBeenCalledWith(
expect.objectContaining({
timelineId: timeline.id,
openTimeline: timeline.isOpen,
})
);
});

it('it removes empty timeline state from URL', async () => {
const { storage } = createSecuritySolutionStorageMock();
const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);

mockUseInitializeUrlParam(URL_PARAM_KEY.timeline, {
id: 'testSavedTimelineId',
isOpen: false,
});

const TestComponent = () => (
<TestProviders store={store}>
<HomePage onAppLeave={jest.fn()} setHeaderActionMenu={jest.fn()}>
<span />
</HomePage>
</TestProviders>
);

const { rerender } = render(<TestComponent />);

jest.clearAllMocks();
mockGetTimiline.mockReturnValue({ ...timelineDefaults, savedObjectId: null });

rerender(<TestComponent />);

expect(mockUpdateUrlParam).toHaveBeenCalledWith(null);
});

it('it updates URL when timeline store changes', async () => {
const { storage } = createSecuritySolutionStorageMock();
const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
const savedObjectId = 'testTimelineId';

mockUseInitializeUrlParam(URL_PARAM_KEY.timeline, {
id: 'testSavedTimelineId',
isOpen: false,
});

const TestComponent = () => (
<TestProviders store={store}>
<HomePage onAppLeave={jest.fn()} setHeaderActionMenu={jest.fn()}>
<span />
</HomePage>
</TestProviders>
);

const { rerender } = render(<TestComponent />);

jest.clearAllMocks();
mockGetTimiline.mockReturnValue({ ...timelineDefaults, savedObjectId });

rerender(<TestComponent />);

expect(mockUpdateUrlParam).toHaveBeenCalledWith(
expect.objectContaining({ id: savedObjectId })
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jest.mock('../../../timelines/containers', () => ({
useTimelineEvents: jest.fn(),
}));

jest.mock('../url_state/normalize_time_range');
jest.mock('../../utils/normalize_time_range');

const mockUseFieldBrowserOptions = jest.fn();
jest.mock('../../../timelines/components/fields_browser', () => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
import type { Plugin } from 'unified';
import type { RemarkTokenizer } from '@elastic/eui';
import { parse } from 'query-string';
import { decodeRisonUrlState } from '../../../url_state/helpers';
import { decodeRisonUrlState } from '../../../../utils/global_query_string/helpers';

import { ID, PREFIX } from './constants';
import * as i18n from './translations';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ import { getSearch } from './helpers';

describe('helpers', () => {
it('returns the search string', () => {
const serachNavTab: SearchNavTab = { urlKey: 'host', isDetailPage: false };
const searchNavTab: SearchNavTab = { urlKey: 'host', isDetailPage: false };
const globalQueryString = 'test=123';

expect(getSearch(serachNavTab, globalQueryString)).toEqual('?&test=123');
expect(getSearch(searchNavTab, globalQueryString)).toEqual('?test=123');
});

it('returns an empty string when globalQueryString is empty', () => {
const serachNavTab: SearchNavTab = { urlKey: 'host', isDetailPage: false };
const searchNavTab: SearchNavTab = { urlKey: 'host', isDetailPage: false };
const globalQueryString = '';

expect(getSearch(serachNavTab, globalQueryString)).toEqual('');
expect(getSearch(searchNavTab, globalQueryString)).toEqual('');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
* 2.0.
*/

import { isAdministration } from '../url_state/types';

import type { SearchNavTab } from './types';
import type { SearchNavTab, UrlStateType } from './types';
import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features';
import { useUiSetting$ } from '../../lib/kibana';
import { ENABLE_GROUPED_NAVIGATION } from '../../../../common/constants';
Expand All @@ -20,6 +18,8 @@ export const getSearch = (tab: SearchNavTab, globalQueryString: string): string
return '';
};

export const isAdministration = (urlKey: UrlStateType): boolean => 'administration' === urlKey;

/**
* Hook to check if the new grouped navigation is enabled on both experimental flag and advanced settings
* TODO: remove this function when flag and setting not needed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@
*/

import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { compose } from 'redux';

import deepEqual from 'fast-deep-equal';

import { useKibana } from '../../lib/kibana';
import type { RouteSpyState } from '../../utils/route/types';
import { useRouteSpy } from '../../utils/route/use_route_spy';
import { makeMapStateToProps } from '../url_state/helpers';
import { useSetBreadcrumbs } from './breadcrumbs';
import { TabNavigation } from './tab_navigation';
import type { TabNavigationComponentProps, SecuritySolutionTabNavigationProps } from './types';
Expand All @@ -25,18 +23,7 @@ import type { TabNavigationComponentProps, SecuritySolutionTabNavigationProps }
export const TabNavigationComponent: React.FC<
RouteSpyState & SecuritySolutionTabNavigationProps & TabNavigationComponentProps
> = React.memo(
({
detailName,
display,
flowTarget,
navTabs,
pageName,
pathName,
search,
state,
tabName,
urlState,
}) => {
({ detailName, display, flowTarget, navTabs, pageName, pathName, search, state, tabName }) => {
const {
chrome,
application: { getUrlForApp, navigateToUrl },
Expand Down Expand Up @@ -83,25 +70,19 @@ export const TabNavigationComponent: React.FC<
pageName={pageName}
pathName={pathName}
tabName={tabName}
timeline={urlState.timeline}
/>
);
}
);
TabNavigationComponent.displayName = 'TabNavigationComponent';

export const SecuritySolutionTabNavigationRedux = compose<
React.ComponentClass<SecuritySolutionTabNavigationProps & RouteSpyState>
>(connect(makeMapStateToProps))(
React.memo(
TabNavigationComponent,
(prevProps, nextProps) =>
prevProps.pathName === nextProps.pathName &&
prevProps.search === nextProps.search &&
deepEqual(prevProps.navTabs, nextProps.navTabs) &&
deepEqual(prevProps.urlState, nextProps.urlState) &&
deepEqual(prevProps.state, nextProps.state)
)
export const SecuritySolutionTabNavigationRedux = React.memo(
TabNavigationComponent,
(prevProps, nextProps) =>
prevProps.pathName === nextProps.pathName &&
prevProps.search === nextProps.search &&
deepEqual(prevProps.navTabs, nextProps.navTabs) &&
deepEqual(prevProps.state, nextProps.state)
);

export const SecuritySolutionTabNavigation: React.FC<SecuritySolutionTabNavigationProps> =
Expand Down
Loading

0 comments on commit afa6112

Please sign in to comment.