diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.test.tsx new file mode 100644 index 0000000000000..f4ee33446d504 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.test.tsx @@ -0,0 +1,80 @@ +/* + * 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 React from 'react'; +import { ThemeProvider } from 'styled-components'; +import { I18nProvider } from '@kbn/i18n/react'; +import { ExceptionItemsSummary } from './exception_items_summary'; +import * as reactTestingLibrary from '@testing-library/react'; +import { getMockTheme } from '../../../../../../../../public/common/lib/kibana/kibana_react.mock'; +import { GetExceptionSummaryResponse } from '../../../../../../../../common/endpoint/types'; + +const mockTheme = getMockTheme({ + eui: { + paddingSizes: { m: '2' }, + }, +}); + +const getStatValue = (el: reactTestingLibrary.RenderResult, stat: string) => { + return el.getByText(stat)!.nextSibling?.lastChild?.textContent; +}; + +describe('Fleet event filters card', () => { + const renderComponent: ( + stats: GetExceptionSummaryResponse + ) => reactTestingLibrary.RenderResult = (stats) => { + const Wrapper: React.FC = ({ children }) => ( + + {children} + + ); + const component = reactTestingLibrary.render(, { + wrapper: Wrapper, + }); + return component; + }; + it('should renders correctly', () => { + const summary: GetExceptionSummaryResponse = { + windows: 3, + linux: 2, + macos: 2, + total: 7, + }; + const component = renderComponent(summary); + + expect(component.getByText('Windows')).not.toBeNull(); + expect(getStatValue(component, 'Windows')).toEqual(summary.windows.toString()); + + expect(component.getByText('Linux')).not.toBeNull(); + expect(getStatValue(component, 'Linux')).toEqual(summary.linux.toString()); + + expect(component.getByText('Mac')).not.toBeNull(); + expect(getStatValue(component, 'Mac')).toEqual(summary.macos.toString()); + + expect(component.getByText('Total')).not.toBeNull(); + expect(getStatValue(component, 'Total')).toEqual(summary.total.toString()); + }); + it('should renders correctly when missing some stats', () => { + const summary: Partial = { + windows: 3, + total: 3, + }; + const component = renderComponent(summary as GetExceptionSummaryResponse); + + expect(component.getByText('Windows')).not.toBeNull(); + expect(getStatValue(component, 'Windows')).toEqual('3'); + + expect(component.getByText('Linux')).not.toBeNull(); + expect(getStatValue(component, 'Linux')).toEqual('0'); + + expect(component.getByText('Mac')).not.toBeNull(); + expect(getStatValue(component, 'Mac')).toEqual('0'); + + expect(component.getByText('Total')).not.toBeNull(); + expect(getStatValue(component, 'Total')).toEqual('3'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.tsx index f42304ffb89ae..ed3d9967f318e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.tsx @@ -47,7 +47,7 @@ export const ExceptionItemsSummary = memo(({ stats } {SUMMARY_KEYS.map((stat) => { return ( - + { + const originalModule = jest.requireActual( + '../../../../../../../../../../../src/plugins/kibana_react/public' + ); + const useKibana = jest.fn().mockImplementation(() => ({ + services: { + http: {}, + data: {}, + notifications: {}, + application: { + getUrlForApp: jest.fn(), + }, + }, + })); + + return { + ...originalModule, + useKibana, + }; +}); + +jest.mock('../../../../../../../common/lib/kibana'); + +const mockTheme = getMockTheme({ + eui: { + paddingSizes: { m: '2' }, + }, +}); + +const EventFiltersHttpServiceMock = EventFiltersHttpService as jest.Mock; +const useToastsMock = useToasts as jest.Mock; + +const summary: GetExceptionSummaryResponse = { + windows: 3, + linux: 2, + macos: 2, + total: 7, +}; + +describe('Fleet event filters card', () => { + let promise: Promise; + let addDanger: jest.Mock = jest.fn(); + const renderComponent: () => Promise = async () => { + const Wrapper: React.FC = ({ children }) => ( + + {children} + + ); + // @ts-ignore + const component = reactTestingLibrary.render(, { wrapper: Wrapper }); + try { + // @ts-ignore + await reactTestingLibrary.act(() => promise); + } catch (err) { + return component; + } + return component; + }; + beforeAll(() => { + useToastsMock.mockImplementation(() => { + return { + addDanger, + }; + }); + }); + beforeEach(() => { + promise = Promise.resolve(summary); + addDanger = jest.fn(); + }); + afterEach(() => { + EventFiltersHttpServiceMock.mockReset(); + }); + it('should render correctly', async () => { + EventFiltersHttpServiceMock.mockImplementationOnce(() => { + return { + getSummary: () => jest.fn(() => promise), + }; + }); + const component = await renderComponent(); + expect(component.getByText('Event Filters')).not.toBeNull(); + expect(component.getByText('Manage event filters')).not.toBeNull(); + }); + it('should render an error toast when api call fails', async () => { + expect(addDanger).toBeCalledTimes(0); + promise = Promise.reject(new Error('error test')); + EventFiltersHttpServiceMock.mockImplementationOnce(() => { + return { + getSummary: () => promise, + }; + }); + const component = await renderComponent(); + expect(component.getByText('Event Filters')).not.toBeNull(); + expect(component.getByText('Manage event filters')).not.toBeNull(); + await reactTestingLibrary.waitFor(() => expect(addDanger).toBeCalledTimes(1)); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx index 6f368a89eb5f9..40d10004788e4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { memo, useMemo, useState, useEffect } from 'react'; +import React, { memo, useMemo, useState, useEffect, useRef } from 'react'; import { ApplicationStart, CoreStart } from 'kibana/public'; import { EuiPanel, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -36,12 +36,16 @@ export const FleetEventFiltersCard = memo( const [stats, setStats] = useState(); const eventFiltersListUrlPath = getEventFiltersListPath(); const eventFiltersApi = useMemo(() => new EventFiltersHttpService(http), [http]); + const isMounted = useRef(); useEffect(() => { + isMounted.current = true; const fetchStats = async () => { try { const summary = await eventFiltersApi.getSummary(); - setStats(summary); + if (isMounted.current) { + setStats(summary); + } } catch (error) { toasts.addDanger( i18n.translate( @@ -55,6 +59,9 @@ export const FleetEventFiltersCard = memo( } }; fetchStats(); + return () => { + isMounted.current = false; + }; }, [eventFiltersApi, toasts]); const eventFiltersRouteState = useMemo(() => { @@ -79,7 +86,7 @@ export const FleetEventFiltersCard = memo( return ( - +

(

- + - + <> { + const originalModule = jest.requireActual( + '../../../../../../../../../../../src/plugins/kibana_react/public' + ); + const useKibana = jest.fn().mockImplementation(() => ({ + services: { + http: {}, + data: {}, + notifications: {}, + application: { + getUrlForApp: jest.fn(), + }, + }, + })); + + return { + ...originalModule, + useKibana, + }; +}); + +jest.mock('../../../../../../../common/lib/kibana'); + +const mockTheme = getMockTheme({ + eui: { + paddingSizes: { m: '2' }, + }, +}); + +const TrustedAppsHttpServiceMock = TrustedAppsHttpService as jest.Mock; +const useToastsMock = useToasts as jest.Mock; + +const summary: GetExceptionSummaryResponse = { + windows: 3, + linux: 2, + macos: 2, + total: 7, +}; + +describe('Fleet trusted apps card', () => { + let promise: Promise; + let addDanger: jest.Mock = jest.fn(); + const renderComponent: () => Promise = async () => { + const Wrapper: React.FC = ({ children }) => ( + + {children} + + ); + // @ts-ignore + const component = reactTestingLibrary.render(, { wrapper: Wrapper }); + try { + // @ts-ignore + await reactTestingLibrary.act(() => promise); + } catch (err) { + return component; + } + return component; + }; + + beforeAll(() => { + useToastsMock.mockImplementation(() => { + return { + addDanger, + }; + }); + }); + beforeEach(() => { + promise = Promise.resolve(summary); + addDanger = jest.fn(); + }); + afterEach(() => { + TrustedAppsHttpServiceMock.mockReset(); + }); + it('should render correctly', async () => { + TrustedAppsHttpServiceMock.mockImplementationOnce(() => { + return { + getTrustedAppsSummary: () => jest.fn(() => promise), + }; + }); + const component = await renderComponent(); + expect(component.getByText('Trusted Applications')).not.toBeNull(); + expect(component.getByText('Manage trusted applications')).not.toBeNull(); + }); + it('should render an error toast when api call fails', async () => { + expect(addDanger).toBeCalledTimes(0); + promise = Promise.reject(new Error('error test')); + TrustedAppsHttpServiceMock.mockImplementationOnce(() => { + return { + getTrustedAppsSummary: () => promise, + }; + }); + const component = await renderComponent(); + expect(component.getByText('Trusted Applications')).not.toBeNull(); + expect(component.getByText('Manage trusted applications')).not.toBeNull(); + await reactTestingLibrary.waitFor(() => expect(addDanger).toBeCalledTimes(1)); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx index ec1479643999a..b1464d23e00fb 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { memo, useMemo, useState, useEffect } from 'react'; +import React, { memo, useMemo, useState, useEffect, useRef } from 'react'; import { ApplicationStart, CoreStart } from 'kibana/public'; import { EuiPanel, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -38,12 +38,16 @@ export const FleetTrustedAppsCard = memo(( const toasts = useToasts(); const [stats, setStats] = useState(); const trustedAppsApi = useMemo(() => new TrustedAppsHttpService(http), [http]); + const isMounted = useRef(); useEffect(() => { + isMounted.current = true; const fetchStats = async () => { try { const response = await trustedAppsApi.getTrustedAppsSummary(); - setStats(response); + if (isMounted) { + setStats(response); + } } catch (error) { toasts.addDanger( i18n.translate( @@ -57,6 +61,9 @@ export const FleetTrustedAppsCard = memo(( } }; fetchStats(); + return () => { + isMounted.current = false; + }; }, [toasts, trustedAppsApi]); const trustedAppsListUrlPath = getTrustedAppsListPath(); @@ -82,7 +89,7 @@ export const FleetTrustedAppsCard = memo(( return ( - +

((

- + - + <> ` - grid-area: ${({ gridArea }) => gridArea}; - align-items: ${({ alignItems }) => alignItems ?? 'center'}; + grid-area: ${({ gridarea }) => gridarea}; + align-items: ${({ alignitems }) => alignitems ?? 'center'}; margin: 0px; padding: 12px; `;