Skip to content

Commit

Permalink
[Security solution][Endpoint] Add unit tests for fleet event filters/…
Browse files Browse the repository at this point in the history
…trusted apps cards (#101034)

* Adds new unit tests for fleet card components

* Fixes some warnings on ui

* Adds some syntax and readibility nits comming from pr comments

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
dasansol92 and kibanamachine committed Jun 2, 2021
1 parent 3bdd423 commit bdd7f7e
Show file tree
Hide file tree
Showing 7 changed files with 342 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -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 }) => (
<I18nProvider>
<ThemeProvider theme={mockTheme}>{children}</ThemeProvider>
</I18nProvider>
);
const component = reactTestingLibrary.render(<ExceptionItemsSummary stats={stats} />, {
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<GetExceptionSummaryResponse> = {
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');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const ExceptionItemsSummary = memo<ExceptionItemsSummaryProps>(({ stats }
<EuiFlexGroup alignItems="center" justifyContent="spaceAround">
{SUMMARY_KEYS.map((stat) => {
return (
<EuiFlexItem>
<EuiFlexItem key={stat}>
<SummaryStat
value={stats?.[stat] ?? 0}
color={stat === 'total' ? 'primary' : 'default'}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* 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 { FleetEventFiltersCard } from './fleet_event_filters_card';
import * as reactTestingLibrary from '@testing-library/react';
import { EventFiltersHttpService } from '../../../../../event_filters/service';
import { useToasts } from '../../../../../../../common/lib/kibana';
import { getMockTheme } from '../../../../../../../../public/common/lib/kibana/kibana_react.mock';
import { GetExceptionSummaryResponse } from '../../../../../../../../common/endpoint/types';

jest.mock('./exception_items_summary');
jest.mock('../../../../../event_filters/service');

jest.mock('../../../../../../../../../../../src/plugins/kibana_react/public', () => {
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<GetExceptionSummaryResponse>;
let addDanger: jest.Mock = jest.fn();
const renderComponent: () => Promise<reactTestingLibrary.RenderResult> = async () => {
const Wrapper: React.FC = ({ children }) => (
<I18nProvider>
<ThemeProvider theme={mockTheme}>{children}</ThemeProvider>
</I18nProvider>
);
// @ts-ignore
const component = reactTestingLibrary.render(<FleetEventFiltersCard />, { 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));
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -36,12 +36,16 @@ export const FleetEventFiltersCard = memo<PackageCustomExtensionComponentProps>(
const [stats, setStats] = useState<GetExceptionSummaryResponse | undefined>();
const eventFiltersListUrlPath = getEventFiltersListPath();
const eventFiltersApi = useMemo(() => new EventFiltersHttpService(http), [http]);
const isMounted = useRef<boolean>();

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(
Expand All @@ -55,6 +59,9 @@ export const FleetEventFiltersCard = memo<PackageCustomExtensionComponentProps>(
}
};
fetchStats();
return () => {
isMounted.current = false;
};
}, [eventFiltersApi, toasts]);

const eventFiltersRouteState = useMemo(() => {
Expand All @@ -79,7 +86,7 @@ export const FleetEventFiltersCard = memo<PackageCustomExtensionComponentProps>(
return (
<EuiPanel paddingSize="l">
<StyledEuiFlexGridGroup alignItems="baseline" justifyContent="center">
<StyledEuiFlexGridItem gridArea="title" alignItems="flex-start">
<StyledEuiFlexGridItem gridarea="title" alignitems="flex-start">
<EuiText>
<h4>
<FormattedMessage
Expand All @@ -89,10 +96,10 @@ export const FleetEventFiltersCard = memo<PackageCustomExtensionComponentProps>(
</h4>
</EuiText>
</StyledEuiFlexGridItem>
<StyledEuiFlexGridItem gridArea="summary">
<StyledEuiFlexGridItem gridarea="summary">
<ExceptionItemsSummary stats={stats} />
</StyledEuiFlexGridItem>
<StyledEuiFlexGridItem gridArea="link" alignItems="flex-end">
<StyledEuiFlexGridItem gridarea="link" alignitems="flex-end">
<>
<LinkWithIcon
appId={MANAGEMENT_APP_ID}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* 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 { FleetTrustedAppsCard } from './fleet_trusted_apps_card';
import * as reactTestingLibrary from '@testing-library/react';
import { TrustedAppsHttpService } from '../../../../../trusted_apps/service';
import { useToasts } from '../../../../../../../common/lib/kibana';
import { getMockTheme } from '../../../../../../../../public/common/lib/kibana/kibana_react.mock';
import { GetExceptionSummaryResponse } from '../../../../../../../../common/endpoint/types';

jest.mock('./exception_items_summary');
jest.mock('../../../../../trusted_apps/service');

jest.mock('../../../../../../../../../../../src/plugins/kibana_react/public', () => {
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<GetExceptionSummaryResponse>;
let addDanger: jest.Mock = jest.fn();
const renderComponent: () => Promise<reactTestingLibrary.RenderResult> = async () => {
const Wrapper: React.FC = ({ children }) => (
<I18nProvider>
<ThemeProvider theme={mockTheme}>{children}</ThemeProvider>
</I18nProvider>
);
// @ts-ignore
const component = reactTestingLibrary.render(<FleetTrustedAppsCard />, { 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));
});
});
Loading

0 comments on commit bdd7f7e

Please sign in to comment.