Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Endpoint] Adds take action dropdown and tests to alert details flyout #59242

Merged
merged 13 commits into from
Mar 17, 2020
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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 * as reactTestingLibrary from '@testing-library/react';
import { appStoreFactory } from '../../store';
import { fireEvent } from '@testing-library/react';
import { MemoryHistory } from 'history';
import { AppAction } from '../../types';
import { mockAlertResultList } from '../../store/alerts/mock_alert_result_list';
import { alertPageTestRender } from './test_helpers/render_alert_page';

describe('when the alert details flyout is open', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This description doesn't match the setup logic. Also, it seems very close to what is here:
https://github.com/elastic/kibana/blob/master/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.test.tsx#L23

I think these specs should be parts of that. The setup logic can be reused that way. Also, the tests are about the same feature set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

abstracted the building render logic out into its own function

let render: () => reactTestingLibrary.RenderResult;
let history: MemoryHistory<never>;
let store: ReturnType<typeof appStoreFactory>;

beforeEach(async () => {
// Creates the render elements for the tests to use
({ render, history, store } = alertPageTestRender);
});
describe('when the alerts details flyout is open', () => {
beforeEach(() => {
reactTestingLibrary.act(() => {
history.push({
search: '?selected_alert=1',
});
});
});
describe('when the data loads', () => {
beforeEach(() => {
reactTestingLibrary.act(() => {
const action: AppAction = {
type: 'serverReturnedAlertDetailsData',
payload: mockAlertResultList().alerts[0],
};
store.dispatch(action);
});
});
it('should display take action button', async () => {
await render().findByTestId('alertDetailTakeActionDropdownButton');
});
describe('when the user clicks the take action button on the flyout', () => {
let renderResult: reactTestingLibrary.RenderResult;
beforeEach(async () => {
renderResult = render();
const takeActionButton = await renderResult.findByTestId(
'alertDetailTakeActionDropdownButton'
);
if (takeActionButton) {
fireEvent.click(takeActionButton);
}
});
it('should display the correct fields in the dropdown', async () => {
await renderResult.findByTestId('alertDetailTakeActionCloseAlertButton');
await renderResult.findByTestId('alertDetailTakeActionWhitelistButton');
});
});
describe('when the user navigates to the overview tab', () => {
let renderResult: reactTestingLibrary.RenderResult;
beforeEach(async () => {
renderResult = render();
const overviewTab = await renderResult.findByTestId('overviewMetadata');
if (overviewTab) {
fireEvent.click(overviewTab);
}
});
it('should render all accordion panels', async () => {
await renderResult.findAllByTestId('alertDetailsAlertAccordion');
await renderResult.findAllByTestId('alertDetailsHostAccordion');
await renderResult.findAllByTestId('alertDetailsFileAccordion');
await renderResult.findAllByTestId('alertDetailsHashAccordion');
await renderResult.findAllByTestId('alertDetailsSourceProcessAccordion');
await renderResult.findAllByTestId('alertDetailsSourceProcessTokenAccordion');
});
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export const FileAccordion = memo(({ alertData }: { alertData: Immutable<AlertDa
}
)}
paddingSize="l"
data-test-subj="alertDetailsFileAccordion"
>
<EuiDescriptionList type="column" listItems={columns} />
</EuiAccordion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export const GeneralAccordion = memo(({ alertData }: { alertData: Immutable<Aler
)}
paddingSize="l"
initialIsOpen={true}
data-test-subj="alertDetailsAlertAccordion"
>
<EuiDescriptionList type="column" listItems={columns} />
</EuiAccordion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const HashAccordion = memo(({ alertData }: { alertData: Immutable<AlertDa
}
)}
paddingSize="l"
data-test-subj="alertDetailsHashAccordion"
>
<EuiDescriptionList type="column" listItems={columns} />
</EuiAccordion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const HostAccordion = memo(({ alertData }: { alertData: Immutable<AlertDa
}
)}
paddingSize="l"
data-test-subj="alertDetailsHostAccordion"
>
<EuiDescriptionList type="column" listItems={columns} />
</EuiAccordion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export const SourceProcessAccordion = memo(({ alertData }: { alertData: Immutabl
}
)}
paddingSize="l"
data-test-subj="alertDetailsSourceProcessAccordion"
>
<EuiDescriptionList type="column" listItems={columns} />
</EuiAccordion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const SourceProcessTokenAccordion = memo(
}
)}
paddingSize="l"
data-test-subj="alertDetailsSourceProcessTokenAccordion"
>
<EuiDescriptionList type="column" listItems={columns} />
</EuiAccordion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,20 @@
import React, { memo, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiSpacer, EuiTitle, EuiText, EuiHealth, EuiTabbedContent } from '@elastic/eui';
import {
EuiSpacer,
EuiTitle,
EuiText,
EuiHealth,
EuiTabbedContent,
EuiTabbedContentTab,
} from '@elastic/eui';
import { useAlertListSelector } from '../../hooks/use_alerts_selector';
import * as selectors from '../../../../store/alerts/selectors';
import { MetadataPanel } from './metadata_panel';
import { FormattedDate } from '../../formatted_date';
import { AlertDetailResolver } from '../../resolver';
import { TakeActionDropdown } from './take_action_dropdown';

export const AlertDetailsOverview = memo(() => {
const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData);
Expand All @@ -22,10 +30,11 @@ export const AlertDetailsOverview = memo(() => {
selectors.selectedAlertIsLegacyEndpointEvent
);

const tabs = useMemo(() => {
const tabs: EuiTabbedContentTab[] = useMemo(() => {
return [
{
id: 'overviewMetadata',
'data-test-subj': 'overviewMetadata',
name: i18n.translate(
'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.overview',
{
Expand Down Expand Up @@ -87,6 +96,8 @@ export const AlertDetailsOverview = memo(() => {
</EuiText>
<EuiText>Alert Status: Open</EuiText>
<EuiSpacer />
<TakeActionDropdown />
<EuiSpacer />
</section>
<EuiTabbedContent tabs={tabs} initialSelectedTab={tabs[0]} />
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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, useState, useCallback } from 'react';
import { EuiPopover, EuiFormRow, EuiButton, EuiButtonEmpty } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';

const TakeActionButton = memo(({ onClick }: { onClick: () => void }) => (
<EuiButton
iconType="arrowDown"
iconSide="right"
data-test-subj="alertDetailTakeActionDropdownButton"
onClick={onClick}
>
<FormattedMessage
id="xpack.endpoint.application.endpoint.alertDetails.takeAction.title"
defaultMessage="Take Action"
/>
</EuiButton>
));

export const TakeActionDropdown = memo(() => {
const [isDropdownOpen, setIsDropdownOpen] = useState(false);

const onClick = useCallback(() => {
setIsDropdownOpen(!isDropdownOpen);
}, [isDropdownOpen]);

const closePopover = useCallback(() => {
setIsDropdownOpen(false);
}, []);

return (
<EuiPopover
button={<TakeActionButton onClick={onClick} />}
isOpen={isDropdownOpen}
anchorPosition="downRight"
closePopover={closePopover}
data-test-subj="alertListTakeActionDropdownContent"
>
<EuiFormRow>
<EuiButtonEmpty
data-test-subj="alertDetailTakeActionCloseAlertButton"
color="text"
iconType="folderCheck"
>
<FormattedMessage
id="xpack.endpoint.application.endpoint.alertDetails.takeAction.close"
defaultMessage="Close Alert"
/>
</EuiButtonEmpty>
</EuiFormRow>

<EuiFormRow>
<EuiButtonEmpty
data-test-subj="alertDetailTakeActionWhitelistButton"
color="text"
iconType="listAdd"
>
<FormattedMessage
id="xpack.endpoint.application.endpoint.alertDetails.takeAction.whitelist"
defaultMessage="Whitelist..."
/>
</EuiButtonEmpty>
</EuiFormRow>
</EuiPopover>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';
import * as reactTestingLibrary from '@testing-library/react';
import { Provider } from 'react-redux';
import { I18nProvider } from '@kbn/i18n/react';
import { AlertIndex } from './index';
import { IIndexPattern } from 'src/plugins/data/public';
import { appStoreFactory } from '../../store';
import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public';
import { fireEvent, act } from '@testing-library/react';
import { RouteCapture } from '../route_capture';
import { createMemoryHistory, MemoryHistory } from 'history';
import { Router } from 'react-router-dom';
import { MemoryHistory } from 'history';
import { AppAction } from '../../types';
import { mockAlertResultList } from '../../store/alerts/mock_alert_result_list';
import { DepsStartMock, depsStartMock } from '../../mocks';
import { DepsStartMock } from '../../mocks';
import { alertPageTestRender } from './test_helpers/render_alert_page';

describe('when on the alerting page', () => {
let render: () => reactTestingLibrary.RenderResult;
Expand All @@ -27,42 +21,8 @@ describe('when on the alerting page', () => {
let depsStart: DepsStartMock;

beforeEach(async () => {
/**
* Create a 'history' instance that is only in-memory and causes no side effects to the testing environment.
*/
history = createMemoryHistory<never>();
/**
* Create a store, with the middleware disabled. We don't want side effects being created by our code in this test.
*/
store = appStoreFactory();

depsStart = depsStartMock();
depsStart.data.ui.SearchBar.mockImplementation(() => <div />);

/**
* Render the test component, use this after setting up anything in `beforeEach`.
*/
render = () => {
/**
* Provide the store via `Provider`, and i18n APIs via `I18nProvider`.
* Use react-router via `Router`, passing our in-memory `history` instance.
* Use `RouteCapture` to emit url-change actions when the URL is changed.
* Finally, render the `AlertIndex` component which we are testing.
*/
return reactTestingLibrary.render(
<Provider store={store}>
<KibanaContextProvider services={{ data: depsStart.data }}>
<I18nProvider>
<Router history={history}>
<RouteCapture>
<AlertIndex />
</RouteCapture>
</Router>
</I18nProvider>
</KibanaContextProvider>
</Provider>
);
};
// Creates the render elements for the tests to use
({ render, history, store, depsStart } = alertPageTestRender);
});
it('should show a data grid', async () => {
await render().findByTestId('alertListGrid');
Expand All @@ -80,7 +40,7 @@ describe('when on the alerting page', () => {
reactTestingLibrary.act(() => {
const action: AppAction = {
type: 'serverReturnedAlertsData',
payload: mockAlertResultList(),
payload: mockAlertResultList({ total: 11 }),
};
store.dispatch(action);
});
Expand All @@ -93,16 +53,17 @@ describe('when on the alerting page', () => {
* There should be a 'row' which is the header, and
* row which is the alert item.
*/
expect(rows).toHaveLength(2);
expect(rows).toHaveLength(11);
});
describe('when the user has clicked the alert type in the grid', () => {
let renderResult: reactTestingLibrary.RenderResult;
beforeEach(async () => {
renderResult = render();
const alertLinks = await renderResult.findAllByTestId('alertTypeCellLink');
/**
* This is the cell with the alert type, it has a link.
*/
fireEvent.click(await renderResult.findByTestId('alertTypeCellLink'));
fireEvent.click(alertLinks[0]);
});
it('should show the flyout', async () => {
await renderResult.findByTestId('alertDetailFlyout');
Expand Down
Loading