diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/__snapshots__/page_view.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/endpoint/__snapshots__/page_view.test.tsx.snap deleted file mode 100644 index bed5ac6950a2b..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/__snapshots__/page_view.test.tsx.snap +++ /dev/null @@ -1,802 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`PageView component should display body header custom element 1`] = ` -.c0.endpoint--isListView { - padding: 0 24px; -} - -.c0.endpoint--isListView .endpoint-header { - padding: 24px; - margin-bottom: 0; -} - -.c0.endpoint--isListView .endpoint-page-content { - border-left: none; - border-right: none; -} - -.c0.endpoint--isDetailsView .endpoint-page-content { - padding: 0; - border: none; - background: none; -} - -.c0 .endpoint-navTabs { - margin-left: 12px; -} - -.c0 .endpoint-header-leftSection { - overflow: hidden; -} - - - body header -

- } - viewType="list" -> - - -
- -
- - -
- -
- -
-

- body header -

-
-
-
-
- -
- body content -
-
-
-
-
-
-
-
-
-
-
-`; - -exports[`PageView component should display body header wrapped in EuiTitle 1`] = ` -.c0.endpoint--isListView { - padding: 0 24px; -} - -.c0.endpoint--isListView .endpoint-header { - padding: 24px; - margin-bottom: 0; -} - -.c0.endpoint--isListView .endpoint-page-content { - border-left: none; - border-right: none; -} - -.c0.endpoint--isDetailsView .endpoint-page-content { - padding: 0; - border: none; - background: none; -} - -.c0 .endpoint-navTabs { - margin-left: 12px; -} - -.c0 .endpoint-header-leftSection { - overflow: hidden; -} - - - - -
- -
- - -
- -
- -
- - -

- body header -

-
-
-
-
-
-
- -
- body content -
-
-
-
-
-
-
-
-
-
-
-`; - -exports[`PageView component should display header left and right 1`] = ` -.c0.endpoint--isListView { - padding: 0 24px; -} - -.c0.endpoint--isListView .endpoint-header { - padding: 24px; - margin-bottom: 0; -} - -.c0.endpoint--isListView .endpoint-page-content { - border-left: none; - border-right: none; -} - -.c0.endpoint--isDetailsView .endpoint-page-content { - padding: 0; - border: none; - background: none; -} - -.c0 .endpoint-navTabs { - margin-left: 12px; -} - -.c0 .endpoint-header-leftSection { - overflow: hidden; -} - - - - -
- -
- -
- -
- - -

- page title -

-
-
-
-
- -
- right side actions -
-
-
-
- - -
- -
- body content -
-
-
-
-
-
-
-
-
-
-
-`; - -exports[`PageView component should display only body if not header props used 1`] = ` -.c0.endpoint--isListView { - padding: 0 24px; -} - -.c0.endpoint--isListView .endpoint-header { - padding: 24px; - margin-bottom: 0; -} - -.c0.endpoint--isListView .endpoint-page-content { - border-left: none; - border-right: none; -} - -.c0.endpoint--isDetailsView .endpoint-page-content { - padding: 0; - border: none; - background: none; -} - -.c0 .endpoint-navTabs { - margin-left: 12px; -} - -.c0 .endpoint-header-leftSection { - overflow: hidden; -} - - - - -
- -
- - -
- -
- body content -
-
-
-
-
-
-
-
-
-
-
-`; - -exports[`PageView component should display only header left 1`] = ` -.c0.endpoint--isListView { - padding: 0 24px; -} - -.c0.endpoint--isListView .endpoint-header { - padding: 24px; - margin-bottom: 0; -} - -.c0.endpoint--isListView .endpoint-page-content { - border-left: none; - border-right: none; -} - -.c0.endpoint--isDetailsView .endpoint-page-content { - padding: 0; - border: none; - background: none; -} - -.c0 .endpoint-navTabs { - margin-left: 12px; -} - -.c0 .endpoint-header-leftSection { - overflow: hidden; -} - - - - -
- -
- -
- -
- - -

- page title -

-
-
-
-
-
-
- - -
- -
- body content -
-
-
-
-
-
-
-
-
-
-
-`; - -exports[`PageView component should display only header right but include an empty left side 1`] = ` -.c0.endpoint--isListView { - padding: 0 24px; -} - -.c0.endpoint--isListView .endpoint-header { - padding: 24px; - margin-bottom: 0; -} - -.c0.endpoint--isListView .endpoint-page-content { - border-left: none; - border-right: none; -} - -.c0.endpoint--isDetailsView .endpoint-page-content { - padding: 0; - border: none; - background: none; -} - -.c0 .endpoint-navTabs { - margin-left: 12px; -} - -.c0 .endpoint-header-leftSection { - overflow: hidden; -} - - - - -
- -
- -
- -
- - -
- right side actions -
-
-
- - - -
- -
- body content -
-
-
-
-
-
-
-
-
-
-
-`; - -exports[`PageView component should pass through EuiPage props 1`] = ` -.c0.endpoint--isListView { - padding: 0 24px; -} - -.c0.endpoint--isListView .endpoint-header { - padding: 24px; - margin-bottom: 0; -} - -.c0.endpoint--isListView .endpoint-page-content { - border-left: none; - border-right: none; -} - -.c0.endpoint--isDetailsView .endpoint-page-content { - padding: 0; - border: none; - background: none; -} - -.c0 .endpoint-navTabs { - margin-left: 12px; -} - -.c0 .endpoint-header-leftSection { - overflow: hidden; -} - - - - -
- -
- - -
- -
- body content -
-
-
-
-
-
-
-
-
-
-
-`; - -exports[`PageView component should use custom element for header left and not wrap in EuiTitle 1`] = ` -.c0.endpoint--isListView { - padding: 0 24px; -} - -.c0.endpoint--isListView .endpoint-header { - padding: 24px; - margin-bottom: 0; -} - -.c0.endpoint--isListView .endpoint-page-content { - border-left: none; - border-right: none; -} - -.c0.endpoint--isDetailsView .endpoint-page-content { - padding: 0; - border: none; - background: none; -} - -.c0 .endpoint-navTabs { - margin-left: 12px; -} - -.c0 .endpoint-header-leftSection { - overflow: hidden; -} - - - title here -

- } - viewType="list" -> - - -
- -
- -
- -
-

- title here -

-
-
-
-
- - -
- -
- body content -
-
-
-
-
-
-
-
-
-
-
-`; diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/page_view.test.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/page_view.test.tsx deleted file mode 100644 index 2c14f66b64865..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/page_view.test.tsx +++ /dev/null @@ -1,88 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { mount } from 'enzyme'; -import { PageView } from './page_view'; -import { EuiThemeProvider } from '../../../../../../legacy/common/eui_styled_components'; - -describe('PageView component', () => { - const render = (ui: Parameters[0]) => - mount(ui, { wrappingComponent: EuiThemeProvider }); - - it('should display only body if not header props used', () => { - expect(render({'body content'})).toMatchSnapshot(); - }); - it('should display header left and right', () => { - expect( - render( - - {'body content'} - - ) - ).toMatchSnapshot(); - }); - it('should display only header left', () => { - expect( - render( - - {'body content'} - - ) - ).toMatchSnapshot(); - }); - it('should display only header right but include an empty left side', () => { - expect( - render( - - {'body content'} - - ) - ).toMatchSnapshot(); - }); - it(`should use custom element for header left and not wrap in EuiTitle`, () => { - expect( - render( - {'title here'}

}> - {'body content'} -
- ) - ).toMatchSnapshot(); - }); - it('should display body header wrapped in EuiTitle', () => { - expect( - render( - - {'body content'} - - ) - ).toMatchSnapshot(); - }); - it('should display body header custom element', () => { - expect( - render( - {'body header'}

}> - {'body content'} -
- ) - ).toMatchSnapshot(); - }); - it('should pass through EuiPage props', () => { - expect( - render( - - {'body content'} - - ) - ).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/page_view.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/page_view.tsx deleted file mode 100644 index d4753b3a64e24..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/page_view.tsx +++ /dev/null @@ -1,184 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - EuiPage, - EuiPageBody, - EuiPageContent, - EuiPageContentBody, - EuiPageContentHeader, - EuiPageContentHeaderSection, - EuiPageHeader, - EuiPageHeaderSection, - EuiPageProps, - EuiTab, - EuiTabs, - EuiTitle, - EuiTitleProps, -} from '@elastic/eui'; -import React, { memo, MouseEventHandler, ReactNode, useMemo } from 'react'; -import styled from 'styled-components'; -import { EuiTabProps } from '@elastic/eui/src/components/tabs/tab'; - -const StyledEuiPage = styled(EuiPage)` - &.endpoint--isListView { - padding: 0 ${(props) => props.theme.eui.euiSizeL}; - - .endpoint-header { - padding: ${(props) => props.theme.eui.euiSizeL}; - margin-bottom: 0; - } - .endpoint-page-content { - border-left: none; - border-right: none; - } - } - &.endpoint--isDetailsView { - .endpoint-page-content { - padding: 0; - border: none; - background: none; - } - } - .endpoint-navTabs { - margin-left: ${(props) => props.theme.eui.euiSizeM}; - } - .endpoint-header-leftSection { - overflow: hidden; - } -`; - -const isStringOrNumber = /(string|number)/; - -/** - * The `PageView` component used to render `headerLeft` when it is set as a `string` - * Can be used when wanting to customize the `headerLeft` value but still use the standard - * title component - */ -export const PageViewHeaderTitle = memo & { children: ReactNode }>( - ({ children, size = 'l', ...otherProps }) => { - return ( - -

{children}

-
- ); - } -); - -PageViewHeaderTitle.displayName = 'PageViewHeaderTitle'; - -/** - * The `PageView` component used to render `bodyHeader` when it is set as a `string` - * Can be used when wanting to customize the `bodyHeader` value but still use the standard - * title component - */ -export const PageViewBodyHeaderTitle = memo<{ children: ReactNode }>( - ({ children, ...otherProps }) => { - return ( - -

{children}

-
- ); - } -); -PageViewBodyHeaderTitle.displayName = 'PageViewBodyHeaderTitle'; - -export type PageViewProps = EuiPageProps & { - /** - * The type of view - */ - viewType: 'list' | 'details'; - /** - * content to be placed on the left side of the header. If a `string` is used, then it will - * be wrapped with `

`, else it will just be used as is. - */ - headerLeft?: ReactNode; - /** Content for the right side of the header */ - headerRight?: ReactNode; - /** - * body (sub-)header section. If a `string` is used, then it will be wrapped with - * `

` - */ - bodyHeader?: ReactNode; - /** - * The list of tab navigation items - */ - tabs?: Array< - EuiTabProps & { - name: ReactNode; - id: string; - href?: string; - onClick?: MouseEventHandler; - } - >; - children?: ReactNode; -}; - -/** - * Page View layout for use in Endpoint - */ -export const PageView = memo( - ({ viewType, children, headerLeft, headerRight, bodyHeader, tabs, ...otherProps }) => { - const tabComponents = useMemo(() => { - if (!tabs) { - return []; - } - return tabs.map(({ name, id, ...otherEuiTabProps }) => ( - - {name} - - )); - }, [tabs]); - - return ( - - - {(headerLeft || headerRight) && ( - - - {isStringOrNumber.test(typeof headerLeft) ? ( - {headerLeft} - ) : ( - headerLeft - )} - - {headerRight && ( - - {headerRight} - - )} - - )} - {tabComponents.length > 0 && ( - {tabComponents} - )} - - {bodyHeader && ( - - - {isStringOrNumber.test(typeof bodyHeader) ? ( - {bodyHeader} - ) : ( - bodyHeader - )} - - - )} - {children} - - - - ); - } -); - -PageView.displayName = 'PageView'; diff --git a/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.tsx b/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.tsx index 373c1f7aaec75..f3136b0a40b3e 100644 --- a/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.tsx @@ -7,14 +7,15 @@ import classNames from 'classnames'; import React, { useEffect } from 'react'; import styled from 'styled-components'; +import { CommonProps } from '@elastic/eui'; import { useFullScreen } from '../../containers/use_full_screen'; import { gutterTimeline } from '../../lib/helpers'; import { AppGlobalStyle } from '../page/index'; const Wrapper = styled.div` - padding: ${({ theme }) => - `${theme.eui.paddingSizes.l} ${gutterTimeline} ${theme.eui.paddingSizes.l} ${theme.eui.paddingSizes.l}`}; + padding: ${(props) => `${props.theme.eui.paddingSizes.l}`}; + &.siemWrapperPage--restrictWidthDefault, &.siemWrapperPage--restrictWidthCustom { box-sizing: content-box; @@ -29,6 +30,10 @@ const Wrapper = styled.div` height: 100%; } + &.siemWrapperPage--withTimeline { + padding-right: ${gutterTimeline}; + } + &.siemWrapperPage--noPadding { padding: 0; } @@ -38,18 +43,20 @@ Wrapper.displayName = 'Wrapper'; interface WrapperPageProps { children: React.ReactNode; - className?: string; restrictWidth?: boolean | number | string; style?: Record; noPadding?: boolean; + noTimeline?: boolean; } -const WrapperPageComponent: React.FC = ({ +const WrapperPageComponent: React.FC = ({ children, className, restrictWidth, style, noPadding, + noTimeline, + ...otherProps }) => { const { globalFullScreen, setGlobalFullScreen } = useFullScreen(); useEffect(() => { @@ -59,6 +66,7 @@ const WrapperPageComponent: React.FC = ({ const classes = classNames(className, { siemWrapperPage: true, 'siemWrapperPage--noPadding': noPadding, + 'siemWrapperPage--withTimeline': !noTimeline, 'siemWrapperPage--fullHeight': globalFullScreen, 'siemWrapperPage--restrictWidthDefault': restrictWidth && typeof restrictWidth === 'boolean' && restrictWidth === true, @@ -73,7 +81,7 @@ const WrapperPageComponent: React.FC = ({ } return ( - + {children} diff --git a/x-pack/plugins/security_solution/public/management/common/constants.ts b/x-pack/plugins/security_solution/public/management/common/constants.ts index 88396cc24a5e2..39d42114f9939 100644 --- a/x-pack/plugins/security_solution/public/management/common/constants.ts +++ b/x-pack/plugins/security_solution/public/management/common/constants.ts @@ -13,6 +13,7 @@ export const MANAGEMENT_ROUTING_ROOT_PATH = ''; export const MANAGEMENT_ROUTING_ENDPOINTS_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${AdministrationSubTab.endpoints})`; export const MANAGEMENT_ROUTING_POLICIES_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${AdministrationSubTab.policies})`; export const MANAGEMENT_ROUTING_POLICY_DETAILS_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${AdministrationSubTab.policies})/:policyId`; +export const MANAGEMENT_ROUTING_TRUSTED_APPS_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${AdministrationSubTab.trustedApps})`; // --[ STORE ]--------------------------------------------------------------------------- /** The SIEM global store namespace where the management state will be mounted */ diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index ea162422abb6f..c5ced6f3bcf55 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -13,6 +13,7 @@ import { MANAGEMENT_ROUTING_ENDPOINTS_PATH, MANAGEMENT_ROUTING_POLICIES_PATH, MANAGEMENT_ROUTING_POLICY_DETAILS_PATH, + MANAGEMENT_ROUTING_TRUSTED_APPS_PATH, } from './constants'; import { AdministrationSubTab } from '../types'; import { appendSearch } from '../../common/components/link_to/helpers'; @@ -72,13 +73,21 @@ export const getEndpointDetailsPath = ( })}${appendSearch(`${urlQueryParams ? `${urlQueryParams}${urlSearch}` : urlSearch}`)}`; }; -export const getPoliciesPath = (search?: string) => - `${generatePath(MANAGEMENT_ROUTING_POLICIES_PATH, { +export const getPoliciesPath = (search?: string) => { + return `${generatePath(MANAGEMENT_ROUTING_POLICIES_PATH, { tabName: AdministrationSubTab.policies, })}${appendSearch(search)}`; +}; -export const getPolicyDetailPath = (policyId: string, search?: string) => - `${generatePath(MANAGEMENT_ROUTING_POLICY_DETAILS_PATH, { +export const getPolicyDetailPath = (policyId: string, search?: string) => { + return `${generatePath(MANAGEMENT_ROUTING_POLICY_DETAILS_PATH, { tabName: AdministrationSubTab.policies, policyId, })}${appendSearch(search)}`; +}; + +export const getTrustedAppsListPath = (search?: string) => { + return `${generatePath(MANAGEMENT_ROUTING_TRUSTED_APPS_PATH, { + tabName: AdministrationSubTab.trustedApps, + })}${appendSearch(search)}`; +}; diff --git a/x-pack/plugins/security_solution/public/management/common/translations.ts b/x-pack/plugins/security_solution/public/management/common/translations.ts index 03f6a80ef99a4..d24eb1bd315fa 100644 --- a/x-pack/plugins/security_solution/public/management/common/translations.ts +++ b/x-pack/plugins/security_solution/public/management/common/translations.ts @@ -13,3 +13,11 @@ export const ENDPOINTS_TAB = i18n.translate('xpack.securitySolution.endpointsTab export const POLICIES_TAB = i18n.translate('xpack.securitySolution.policiesTab', { defaultMessage: 'Policies', }); + +export const TRUSTED_APPS_TAB = i18n.translate('xpack.securitySolution.trustedAppsTab', { + defaultMessage: 'Trusted applications', +}); + +export const BETA_BADGE_LABEL = i18n.translate('xpack.securitySolution.administration.list.beta', { + defaultMessage: 'Beta', +}); diff --git a/x-pack/plugins/security_solution/public/management/components/administration_list_page.tsx b/x-pack/plugins/security_solution/public/management/components/administration_list_page.tsx new file mode 100644 index 0000000000000..3df525b4d59d6 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/administration_list_page.tsx @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { FC, memo } from 'react'; +import { EuiPanel, EuiSpacer, CommonProps } from '@elastic/eui'; +import { SecurityPageName } from '../../../common/constants'; +import { WrapperPage } from '../../common/components/wrapper_page'; +import { HeaderPage } from '../../common/components/header_page'; +import { SiemNavigation } from '../../common/components/navigation'; +import { SpyRoute } from '../../common/utils/route/spy_routes'; +import { AdministrationSubTab } from '../types'; +import { ENDPOINTS_TAB, TRUSTED_APPS_TAB, BETA_BADGE_LABEL } from '../common/translations'; +import { getEndpointListPath, getTrustedAppsListPath } from '../common/routing'; + +interface AdministrationListPageProps { + beta: boolean; + title: React.ReactNode; + subtitle: React.ReactNode; + actions?: React.ReactNode; +} + +export const AdministrationListPage: FC = memo( + ({ beta, title, subtitle, actions, children, ...otherProps }) => { + const badgeOptions = !beta ? undefined : { beta: true, text: BETA_BADGE_LABEL }; + + return ( + + + {actions} + + + + + + + {children} + + + + ); + } +); + +AdministrationListPage.displayName = 'AdministrationListPage'; diff --git a/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx b/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx deleted file mode 100644 index 54d9131209d0d..0000000000000 --- a/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx +++ /dev/null @@ -1,19 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { memo } from 'react'; -import { EuiErrorBoundary } from '@elastic/eui'; -import { PageView, PageViewProps } from '../../common/components/endpoint/page_view'; - -export const ManagementPageView = memo>((options) => { - return ( - - - - ); -}); - -ManagementPageView.displayName = 'ManagementPageView'; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index 09df6d6ece042..fe06bcc8131f8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -8,6 +8,7 @@ import React from 'react'; import * as reactTestingLibrary from '@testing-library/react'; import { EndpointList } from './index'; +import '../../../../common/mock/match_media.ts'; import { mockEndpointDetailsApiResult, mockEndpointResultList, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index a923d49012d70..611e69391ab06 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -10,15 +10,10 @@ import { EuiBasicTable, EuiBasicTableColumn, EuiText, - EuiTitle, - EuiSpacer, EuiLink, EuiHealth, EuiToolTip, EuiSelectableProps, - EuiBetaBadge, - EuiFlexGroup, - EuiFlexItem, } from '@elastic/eui'; import { useHistory } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; @@ -36,8 +31,6 @@ import { import { useNavigateByRouterEventHandler } from '../../../../common/hooks/endpoint/use_navigate_by_router_event_handler'; import { CreateStructuredSelector } from '../../../../common/store'; import { Immutable, HostInfo } from '../../../../../common/endpoint/types'; -import { SpyRoute } from '../../../../common/utils/route/spy_routes'; -import { ManagementPageView } from '../../../components/management_page_view'; import { PolicyEmptyState, HostsEmptyState } from '../../../components/management_empty_state'; import { FormattedDate } from '../../../../common/components/formatted_date'; import { useNavigateToAppEventHandler } from '../../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; @@ -50,6 +43,7 @@ import { getEndpointListPath, getEndpointDetailsPath } from '../../../common/rou import { useFormatUrl } from '../../../../common/components/link_to'; import { EndpointAction } from '../store/action'; import { EndpointPolicyLink } from './components/endpoint_policy_link'; +import { AdministrationListPage } from '../../../components/administration_list_page'; const EndpointListNavLink = memo<{ name: string; @@ -375,40 +369,20 @@ export const EndpointList = () => { ]); return ( - - - - -

- -

-
-
- - - -
- - -

- -

-
- + beta={true} + title={ + + } + subtitle={ + } > {hasSelectedEndpoint && } @@ -425,7 +399,6 @@ export const EndpointList = () => { )} {renderTableOrEmptyState} - -
+ ); }; diff --git a/x-pack/plugins/security_solution/public/management/pages/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/index.test.tsx index 9e496ce6c0b50..c04d3b1ec1a90 100644 --- a/x-pack/plugins/security_solution/public/management/pages/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/index.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { ManagementContainer } from './index'; +import '../../common/mock/match_media.ts'; import { AppContextTestRender, createAppRootMockRenderer } from '../../common/mock/endpoint'; import { useIngestEnabledCheck } from '../../common/hooks/endpoint/ingest_enabled'; @@ -22,15 +23,13 @@ describe('when in the Admistration tab', () => { it('should display the No Permissions view when Ingest is OFF', async () => { (useIngestEnabledCheck as jest.Mock).mockReturnValue({ allEnabled: false }); - const renderResult = render(); - const noIngestPermissions = await renderResult.findByTestId('noIngestPermissions'); - expect(noIngestPermissions).not.toBeNull(); + + expect(await render().findByTestId('noIngestPermissions')).not.toBeNull(); }); it('should display the Management view when Ingest is ON', async () => { (useIngestEnabledCheck as jest.Mock).mockReturnValue({ allEnabled: true }); - const renderResult = render(); - const endpointPage = await renderResult.findByTestId('endpointPage'); - expect(endpointPage).not.toBeNull(); + + expect(await render().findByTestId('endpointPage')).not.toBeNull(); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/index.tsx b/x-pack/plugins/security_solution/public/management/pages/index.tsx index c20a3dd31d6a4..959753cba7bd7 100644 --- a/x-pack/plugins/security_solution/public/management/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/index.tsx @@ -11,55 +11,50 @@ import { useHistory, Route, Switch } from 'react-router-dom'; import { ChromeBreadcrumb } from 'kibana/public'; import { EuiText, EuiEmptyPrompt } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { PolicyContainer } from './policy'; import { MANAGEMENT_ROUTING_ENDPOINTS_PATH, MANAGEMENT_ROUTING_POLICIES_PATH, MANAGEMENT_ROUTING_ROOT_PATH, + MANAGEMENT_ROUTING_TRUSTED_APPS_PATH, } from '../common/constants'; import { NotFoundPage } from '../../app/404'; import { EndpointsContainer } from './endpoint_hosts'; +import { PolicyContainer } from './policy'; +import { TrustedAppsContainer } from './trusted_apps'; import { getEndpointListPath } from '../common/routing'; import { APP_ID, SecurityPageName } from '../../../common/constants'; import { GetUrlForApp } from '../../common/components/navigation/types'; import { AdministrationRouteSpyState } from '../../common/utils/route/types'; import { ADMINISTRATION } from '../../app/home/translations'; import { AdministrationSubTab } from '../types'; -import { ENDPOINTS_TAB, POLICIES_TAB } from '../common/translations'; +import { ENDPOINTS_TAB, POLICIES_TAB, TRUSTED_APPS_TAB } from '../common/translations'; import { SpyRoute } from '../../common/utils/route/spy_routes'; import { useIngestEnabledCheck } from '../../common/hooks/endpoint/ingest_enabled'; -const TabNameMappedToI18nKey: Record = { +const TabNameMappedToI18nKey: Record = { [AdministrationSubTab.endpoints]: ENDPOINTS_TAB, [AdministrationSubTab.policies]: POLICIES_TAB, + [AdministrationSubTab.trustedApps]: TRUSTED_APPS_TAB, }; -export const getBreadcrumbs = ( +export function getBreadcrumbs( params: AdministrationRouteSpyState, search: string[], getUrlForApp: GetUrlForApp -): ChromeBreadcrumb[] => { - let breadcrumb = [ +): ChromeBreadcrumb[] { + return [ { text: ADMINISTRATION, href: getUrlForApp(`${APP_ID}:${SecurityPageName.administration}`, { path: !isEmpty(search[0]) ? search[0] : '', }), }, - ]; - - const tabName = params?.tabName; - if (!tabName) return breadcrumb; - - breadcrumb = [ - ...breadcrumb, - { + ...(params?.tabName ? [params?.tabName] : []).map((tabName) => ({ text: TabNameMappedToI18nKey[tabName], href: '', - }, + })), ]; - return breadcrumb; -}; +} const NoPermissions = memo(() => { return ( @@ -104,6 +99,7 @@ export const ManagementContainer = memo(() => { + { it('should display back to list button and policy title', () => { policyView.update(); - const pageHeaderLeft = policyView.find( - 'EuiPageHeaderSection[data-test-subj="pageViewHeaderLeft"]' - ); - const backToListButton = pageHeaderLeft.find('EuiButtonEmpty'); - expect(backToListButton.prop('iconType')).toBe('arrowLeft'); - expect(backToListButton.prop('href')).toBe(endpointListPath); - expect(backToListButton.text()).toBe('Back to endpoint hosts'); + const backToListLink = policyView.find('LinkIcon[dataTestSubj="policyDetailsBackLink"]'); + expect(backToListLink.prop('iconType')).toBe('arrowLeft'); + expect(backToListLink.prop('href')).toBe(endpointListPath); + expect(backToListLink.text()).toBe('Back to endpoint hosts'); - const pageTitle = pageHeaderLeft.find('[data-test-subj="pageViewHeaderLeftTitle"]'); + const pageTitle = policyView.find('h1[data-test-subj="header-page-title"]'); expect(pageTitle).toHaveLength(1); expect(pageTitle.text()).toEqual(policyPackageConfig.name); }); it('should navigate to list if back to link is clicked', async () => { policyView.update(); - const backToListButton = policyView.find( - 'EuiPageHeaderSection[data-test-subj="pageViewHeaderLeft"] EuiButtonEmpty' - ); + + const backToListLink = policyView.find('a[data-test-subj="policyDetailsBackLink"]'); expect(history.location.pathname).toEqual(policyDetailsPathUrl); - backToListButton.simulate('click', { button: 0 }); + backToListLink.simulate('click', { button: 0 }); expect(history.location.pathname).toEqual(endpointListPath); }); it('should display agent stats', async () => { await asyncActions; policyView.update(); - const headerRight = policyView.find( - 'EuiPageHeaderSection[data-test-subj="pageViewHeaderRight"]' - ); - const agentsSummary = headerRight.find('EuiFlexGroup[data-test-subj="policyAgentsSummary"]'); + + const agentsSummary = policyView.find('EuiFlexGroup[data-test-subj="policyAgentsSummary"]'); expect(agentsSummary).toHaveLength(1); expect(agentsSummary.text()).toBe('Endpoints5Online3Offline1Error1'); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx index e54e684d788c0..636c0e5e6a0c3 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx @@ -38,9 +38,6 @@ import { WindowsEvents, MacEvents, LinuxEvents } from './policy_forms/events'; import { MalwareProtections } from './policy_forms/protections/malware'; import { useToasts } from '../../../../common/lib/kibana'; import { AppAction } from '../../../../common/store/actions'; -import { useNavigateByRouterEventHandler } from '../../../../common/hooks/endpoint/use_navigate_by_router_event_handler'; -import { PageViewHeaderTitle } from '../../../../common/components/endpoint/page_view'; -import { ManagementPageView } from '../../../components/management_page_view'; import { SpyRoute } from '../../../../common/utils/route/spy_routes'; import { SecurityPageName } from '../../../../app/types'; import { getEndpointListPath } from '../../../common/routing'; @@ -48,6 +45,8 @@ import { useFormatUrl } from '../../../../common/components/link_to'; import { useNavigateToAppEventHandler } from '../../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; import { MANAGEMENT_APP_ID } from '../../../common/constants'; import { PolicyDetailsRouteState } from '../../../../../common/endpoint/types'; +import { WrapperPage } from '../../../../common/components/wrapper_page'; +import { HeaderPage } from '../../../../common/components/header_page'; export const PolicyDetails = React.memo(() => { const dispatch = useDispatch<(action: AppAction) => void>(); @@ -109,8 +108,6 @@ export const PolicyDetails = React.memo(() => { } }, [navigateToApp, toasts, policyName, policyUpdateStatus, routeState]); - const handleBackToListOnClick = useNavigateByRouterEventHandler(hostListRouterPath); - const navigateToAppArguments = useMemo((): Parameters => { return routeState?.onCancelNavigateTo ?? [MANAGEMENT_APP_ID, { path: hostListRouterPath }]; }, [hostListRouterPath, routeState?.onCancelNavigateTo]); @@ -142,7 +139,7 @@ export const PolicyDetails = React.memo(() => { // Else, if we have an error, then show error on the page. if (!policyItem) { return ( - + {isPolicyLoading ? ( ) : policyApiError ? ( @@ -151,28 +148,10 @@ export const PolicyDetails = React.memo(() => { ) : null} - + ); } - const headerLeftContent = ( -
- {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} - - - - {policyItem.name} -
- ); - const headerRightContent = ( @@ -222,12 +201,21 @@ export const PolicyDetails = React.memo(() => { onConfirm={handleSaveConfirmation} /> )} - + + + {headerRightContent} + +

{ />

+ +

{ />

+ -
+ + ); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx index 97eaceff91e9c..b0139bf3ecb21 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx @@ -6,6 +6,7 @@ import React from 'react'; import { PolicyList } from './index'; +import '../../../../common/mock/match_media.ts'; import { AppContextTestRender, createAppRootMockRenderer } from '../../../../common/mock/endpoint'; import { setPolicyListApiMockImplementation } from '../store/policy_list/test_mock_utils'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx index 39b77d259add1..b97ea958fd1c2 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx @@ -8,7 +8,6 @@ import React, { useCallback, useEffect, useMemo, CSSProperties, useState } from import { EuiBasicTable, EuiText, - EuiTitle, EuiSpacer, EuiFlexGroup, EuiFlexItem, @@ -23,7 +22,6 @@ import { EuiConfirmModal, EuiCallOut, EuiButton, - EuiBetaBadge, EuiHorizontalRule, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -41,9 +39,7 @@ import { useKibana } from '../../../../../../../../src/plugins/kibana_react/publ import { Immutable, PolicyData } from '../../../../../common/endpoint/types'; import { useNavigateByRouterEventHandler } from '../../../../common/hooks/endpoint/use_navigate_by_router_event_handler'; import { LinkToApp } from '../../../../common/components/endpoint/link_to_app'; -import { ManagementPageView } from '../../../components/management_page_view'; import { PolicyEmptyState } from '../../../components/management_empty_state'; -import { SpyRoute } from '../../../../common/utils/route/spy_routes'; import { FormattedDateAndTime } from '../../../../common/components/endpoint/formatted_date_time'; import { SecurityPageName } from '../../../../app/types'; import { useFormatUrl } from '../../../../common/components/link_to'; @@ -51,6 +47,7 @@ import { getPolicyDetailPath, getPoliciesPath } from '../../../common/routing'; import { useNavigateToAppEventHandler } from '../../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; import { CreatePackageConfigRouteState } from '../../../../../../ingest_manager/public'; import { MANAGEMENT_APP_ID } from '../../../common/constants'; +import { AdministrationListPage } from '../../../components/administration_list_page'; interface TableChangeCallbackArguments { page: { index: number; size: number }; @@ -405,42 +402,22 @@ export const PolicyList = React.memo(() => { }} /> )} - - - - -

- -

-
-
- - - -
- - -

- -

-
- + beta={true} + title={ + + } + subtitle={ + } - headerRight={ + actions={ { )} {bodyContent} - -
+ ); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/index.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/index.tsx new file mode 100644 index 0000000000000..ddc16cc448c8a --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/index.tsx @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Switch, Route } from 'react-router-dom'; +import React from 'react'; +import { TrustedAppsPage } from './view'; +import { MANAGEMENT_ROUTING_TRUSTED_APPS_PATH } from '../../common/constants'; +import { NotFoundPage } from '../../../app/404'; + +export function TrustedAppsContainer() { + return ( + + + + + ); +} diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_apps_page.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_apps_page.test.tsx.snap new file mode 100644 index 0000000000000..6f074f3809036 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_apps_page.test.tsx.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TrustedAppsPage rendering 1`] = ` + + } + title={ + + } +/> +`; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/index.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/index.ts new file mode 100644 index 0000000000000..af6f7e2863dfb --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export * from './trusted_apps_page'; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/routes.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx similarity index 51% rename from x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/routes.tsx rename to x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx index 727d2715f0974..cc7dbd42d7a7d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/routes.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx @@ -3,16 +3,13 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import { shallow } from 'enzyme'; import React from 'react'; -import { Route, Switch } from 'react-router-dom'; -import { EndpointList } from './view'; +import { TrustedAppsPage } from './trusted_apps_page'; -export const EndpointHostsRoutes: React.FC = () => ( - - - - - -); +describe('TrustedAppsPage', () => { + test('rendering', () => { + expect(shallow()).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx new file mode 100644 index 0000000000000..7045fa49ffad3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { AdministrationListPage } from '../../../components/administration_list_page'; + +export function TrustedAppsPage() { + return ( + + } + subtitle={ + + } + /> + ); +} diff --git a/x-pack/plugins/security_solution/public/management/types.ts b/x-pack/plugins/security_solution/public/management/types.ts index c35c9277b4488..21214241d1981 100644 --- a/x-pack/plugins/security_solution/public/management/types.ts +++ b/x-pack/plugins/security_solution/public/management/types.ts @@ -27,6 +27,7 @@ export type ManagementState = CombinedState<{ export enum AdministrationSubTab { endpoints = 'endpoints', policies = 'policy', + trustedApps = 'trusted_apps', } /** diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 8c92e7359b2f7..e21806e1a1e01 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -16251,7 +16251,6 @@ "xpack.securitySolution.endpoint.details.policyResponse.success": "成功", "xpack.securitySolution.endpoint.details.policyResponse.warning": "警告", "xpack.securitySolution.endpoint.details.policyResponse.workflow": "ワークフロー", - "xpack.securitySolution.endpoint.list.beta": "ベータ", "xpack.securitySolution.endpoint.list.loadingPolicies": "ポリシー構成を読み込んでいます…", "xpack.securitySolution.endpoint.list.noEndpointsInstructions": "セキュリティポリシーを作成しました。以下のステップに従い、エージェントでElastic Endpoint Security機能を有効にする必要があります。", "xpack.securitySolution.endpoint.list.noEndpointsPrompt": "エージェントでElastic Endpoint Securityを有効にする", @@ -16310,7 +16309,6 @@ "xpack.securitySolution.endpoint.policyList.actionButtonText": "Endpoint Securityを追加", "xpack.securitySolution.endpoint.policyList.actionMenu": "開く", "xpack.securitySolution.endpoint.policyList.agentConfigAction": "エージェント構成を表示", - "xpack.securitySolution.endpoint.policyList.beta": "ベータ", "xpack.securitySolution.endpoint.policyList.createdAt": "作成日時", "xpack.securitySolution.endpoint.policyList.createdBy": "作成者", "xpack.securitySolution.endpoint.policyList.createNewButton": "新しいポリシーを作成", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 5ab70ff7a9d04..13ae52bdd0af3 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -16257,7 +16257,6 @@ "xpack.securitySolution.endpoint.details.policyResponse.success": "成功", "xpack.securitySolution.endpoint.details.policyResponse.warning": "警告", "xpack.securitySolution.endpoint.details.policyResponse.workflow": "工作流", - "xpack.securitySolution.endpoint.list.beta": "公测版", "xpack.securitySolution.endpoint.list.loadingPolicies": "正在加载政策配置", "xpack.securitySolution.endpoint.list.noEndpointsInstructions": "您已创建安全策略。现在您需要按照下面的步骤在代理上启用 Elastic Endpoint Security 功能。", "xpack.securitySolution.endpoint.list.noEndpointsPrompt": "在您的代理上启用 Elastic Endpoint Security", @@ -16317,7 +16316,6 @@ "xpack.securitySolution.endpoint.policyList.actionButtonText": "添加 Endpoint Security", "xpack.securitySolution.endpoint.policyList.actionMenu": "打开", "xpack.securitySolution.endpoint.policyList.agentConfigAction": "查看代理配置", - "xpack.securitySolution.endpoint.policyList.beta": "公测版", "xpack.securitySolution.endpoint.policyList.createdAt": "创建日期", "xpack.securitySolution.endpoint.policyList.createdBy": "创建者", "xpack.securitySolution.endpoint.policyList.createNewButton": "创建新策略", diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts index 0037c39b8fed2..1843fca29758a 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts @@ -27,8 +27,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('finds page title', async () => { - const title = await testSubjects.getVisibleText('pageViewHeaderLeftTitle'); - expect(title).to.equal('Endpoints'); + const title = await testSubjects.getVisibleText('header-page-title'); + expect(title).to.equal('Endpoints BETA'); }); it('displays table data', async () => { diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts index 1119906ba5cfa..9efba2b595bde 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts @@ -47,7 +47,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it('should display policy view', async () => { - expect(await testSubjects.getVisibleText('pageViewHeaderLeftTitle')).to.equal( + expect(await testSubjects.getVisibleText('header-page-title')).to.equal( policyInfo.packageConfig.name ); }); diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts index 0c5e15ed4104c..cf93ca1b68991 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts @@ -29,8 +29,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await testSubjects.existOrFail('policyListPage'); }); it('displays page title', async () => { - const policyTitle = await testSubjects.getVisibleText('pageViewHeaderLeftTitle'); - expect(policyTitle).to.equal('Policies'); + const policyTitle = await testSubjects.getVisibleText('header-page-title'); + expect(policyTitle).to.equal('Policies BETA'); }); it('shows header create policy button', async () => { const createButtonTitle = await testSubjects.getVisibleText('headerCreateNewPolicyButton'); diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts new file mode 100644 index 0000000000000..5f749ac272474 --- /dev/null +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const pageObjects = getPageObjects(['trustedApps']); + const testSubjects = getService('testSubjects'); + + describe('endpoint list', function () { + this.tags('ciGroup7'); + + describe('when there is data', () => { + before(async () => { + await pageObjects.trustedApps.navigateToTrustedAppsList(); + }); + + it('finds page title', async () => { + expect(await testSubjects.getVisibleText('header-page-title')).to.equal( + 'Trusted applications BETA' + ); + }); + }); + }); +}; diff --git a/x-pack/test/security_solution_endpoint/page_objects/index.ts b/x-pack/test/security_solution_endpoint/page_objects/index.ts index 68e1ad00619c7..05762ba887c6f 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/index.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/index.ts @@ -7,6 +7,7 @@ import { pageObjects as xpackFunctionalPageObjects } from '../../functional/page_objects'; import { EndpointPageProvider } from './endpoint_page'; import { EndpointPolicyPageProvider } from './policy_page'; +import { TrustedAppsPageProvider } from './trusted_apps_page'; import { EndpointPageUtils } from './page_utils'; import { IngestManagerCreatePackageConfig } from './ingest_manager_create_package_config_page'; @@ -14,6 +15,7 @@ export const pageObjects = { ...xpackFunctionalPageObjects, endpoint: EndpointPageProvider, policy: EndpointPolicyPageProvider, + trustedApps: TrustedAppsPageProvider, endpointPageUtils: EndpointPageUtils, ingestManagerCreatePackageConfig: IngestManagerCreatePackageConfig, }; diff --git a/x-pack/test/security_solution_endpoint/page_objects/trusted_apps_page.ts b/x-pack/test/security_solution_endpoint/page_objects/trusted_apps_page.ts new file mode 100644 index 0000000000000..c02ac0ca9ffe0 --- /dev/null +++ b/x-pack/test/security_solution_endpoint/page_objects/trusted_apps_page.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { FtrProviderContext } from '../ftr_provider_context'; + +export function TrustedAppsPageProvider({ getPageObjects }: FtrProviderContext) { + const pageObjects = getPageObjects(['common', 'header', 'endpointPageUtils']); + + return { + async navigateToTrustedAppsList(searchParams?: string) { + await pageObjects.common.navigateToUrlWithBrowserHistory( + 'securitySolutionManagement', + `/trusted_apps${searchParams ? `?${searchParams}` : ''}` + ); + await pageObjects.header.waitUntilLoadingHasFinished(); + }, + }; +}