[ML] Adds Jest tests for the Filter List Settings components (#21782)

This commit is contained in:
Pete Harverson 2018-08-08 15:22:02 +01:00 committed by GitHub
parent f08f92a673
commit cdc4ab6f67
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 3090 additions and 22 deletions

View file

@ -0,0 +1,232 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AddItemPopover calls addItems with multiple items on clicking Add button 1`] = `
<div>
<EuiPopover
anchorPosition="downCenter"
button={
<EuiButton
color="primary"
fill={false}
iconSide="right"
iconType="arrowDown"
onClick={[Function]}
size="s"
type="button"
>
Add item
</EuiButton>
}
closePopover={[Function]}
id="add_item_popover"
isOpen={false}
ownFocus={true}
panelClassName="ml-add-filter-item-popover"
panelPaddingSize="m"
>
<EuiForm>
<EuiFormRow
describedByIds={Array []}
fullWidth={false}
hasEmptyLabelSpace={false}
label="Items"
>
<EuiTextArea
fullWidth={false}
onChange={[Function]}
resize="vertical"
value=""
/>
</EuiFormRow>
</EuiForm>
<EuiText
grow={true}
size="xs"
>
Enter one item per line
</EuiText>
<EuiSpacer
size="s"
/>
<EuiFlexGroup
alignItems="stretch"
component="div"
direction="row"
gutterSize="l"
justifyContent="flexEnd"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButton
color="primary"
disabled={true}
fill={false}
iconSide="left"
onClick={[Function]}
type="button"
>
Add
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPopover>
</div>
`;
exports[`AddItemPopover opens the popover onButtonClick 1`] = `
<div>
<EuiPopover
anchorPosition="downCenter"
button={
<EuiButton
color="primary"
fill={false}
iconSide="right"
iconType="arrowDown"
onClick={[Function]}
size="s"
type="button"
>
Add item
</EuiButton>
}
closePopover={[Function]}
id="add_item_popover"
isOpen={true}
ownFocus={true}
panelClassName="ml-add-filter-item-popover"
panelPaddingSize="m"
>
<EuiForm>
<EuiFormRow
describedByIds={Array []}
fullWidth={false}
hasEmptyLabelSpace={false}
label="Items"
>
<EuiTextArea
fullWidth={false}
onChange={[Function]}
resize="vertical"
value=""
/>
</EuiFormRow>
</EuiForm>
<EuiText
grow={true}
size="xs"
>
Enter one item per line
</EuiText>
<EuiSpacer
size="s"
/>
<EuiFlexGroup
alignItems="stretch"
component="div"
direction="row"
gutterSize="l"
justifyContent="flexEnd"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButton
color="primary"
disabled={true}
fill={false}
iconSide="left"
onClick={[Function]}
type="button"
>
Add
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPopover>
</div>
`;
exports[`AddItemPopover renders the popover 1`] = `
<div>
<EuiPopover
anchorPosition="downCenter"
button={
<EuiButton
color="primary"
fill={false}
iconSide="right"
iconType="arrowDown"
onClick={[Function]}
size="s"
type="button"
>
Add item
</EuiButton>
}
closePopover={[Function]}
id="add_item_popover"
isOpen={false}
ownFocus={true}
panelClassName="ml-add-filter-item-popover"
panelPaddingSize="m"
>
<EuiForm>
<EuiFormRow
describedByIds={Array []}
fullWidth={false}
hasEmptyLabelSpace={false}
label="Items"
>
<EuiTextArea
fullWidth={false}
onChange={[Function]}
resize="vertical"
value=""
/>
</EuiFormRow>
</EuiForm>
<EuiText
grow={true}
size="xs"
>
Enter one item per line
</EuiText>
<EuiSpacer
size="s"
/>
<EuiFlexGroup
alignItems="stretch"
component="div"
direction="row"
gutterSize="l"
justifyContent="flexEnd"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButton
color="primary"
disabled={true}
fill={false}
iconSide="left"
onClick={[Function]}
type="button"
>
Add
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPopover>
</div>
`;

View file

@ -0,0 +1,64 @@
/*
* 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 { shallow } from 'enzyme';
import React from 'react';
import { AddItemPopover } from './add_item_popover';
function prepareTest(addItemsFn) {
const props = {
addItems: addItemsFn
};
const wrapper = shallow(
<AddItemPopover {...props} />
);
return wrapper;
}
describe('AddItemPopover', () => {
test('renders the popover', () => {
const addItems = jest.fn(() => {});
const wrapper = prepareTest(addItems);
expect(wrapper).toMatchSnapshot();
});
test('opens the popover onButtonClick', () => {
const addItems = jest.fn(() => {});
const wrapper = prepareTest(addItems);
const instance = wrapper.instance();
instance.onButtonClick();
wrapper.update();
expect(wrapper).toMatchSnapshot();
});
test('calls addItems with one item on clicking Add button', () => {
const addItems = jest.fn(() => {});
const wrapper = prepareTest(addItems);
wrapper.find('EuiTextArea').simulate('change', { target: { value: 'google.com' } });
const instance = wrapper.instance();
instance.onAddButtonClick();
wrapper.update();
expect(addItems).toHaveBeenCalledWith(['google.com']);
});
test('calls addItems with multiple items on clicking Add button', () => {
const addItems = jest.fn(() => {});
const wrapper = prepareTest(addItems);
wrapper.find('EuiTextArea').simulate('change', { target: { value: 'google.com\nelastic.co' } });
const instance = wrapper.instance();
instance.onAddButtonClick();
wrapper.update();
expect(addItems).toHaveBeenCalledWith(['google.com', 'elastic.co']);
expect(wrapper).toMatchSnapshot();
});
});

View file

@ -0,0 +1,104 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DeleteFilterListModal false canDeleteFilter privilege renders as disabled delete button 1`] = `
<div>
<EuiButton
color="danger"
fill={false}
iconSide="left"
iconType="trash"
isDisabled={true}
key="delete_filter_list"
onClick={[Function]}
type="button"
>
Delete
</EuiButton>
</div>
`;
exports[`DeleteFilterListModal renders as delete button after opening and closing modal 1`] = `
<div>
<EuiButton
color="danger"
fill={false}
iconSide="left"
iconType="trash"
isDisabled={false}
key="delete_filter_list"
onClick={[Function]}
type="button"
>
Delete
</EuiButton>
</div>
`;
exports[`DeleteFilterListModal renders as disabled delete button when no lists selected 1`] = `
<div>
<EuiButton
color="danger"
fill={false}
iconSide="left"
iconType="trash"
isDisabled={true}
key="delete_filter_list"
onClick={[Function]}
type="button"
>
Delete
</EuiButton>
</div>
`;
exports[`DeleteFilterListModal renders as enabled delete button when a list is selected 1`] = `
<div>
<EuiButton
color="danger"
fill={false}
iconSide="left"
iconType="trash"
isDisabled={false}
key="delete_filter_list"
onClick={[Function]}
type="button"
>
Delete
</EuiButton>
</div>
`;
exports[`DeleteFilterListModal renders modal after clicking delete button 1`] = `
<div>
<EuiButton
color="danger"
fill={false}
iconSide="left"
iconType="trash"
isDisabled={false}
key="delete_filter_list"
onClick={[Function]}
type="button"
>
Delete
</EuiButton>
<EuiOverlayMask>
<EuiConfirmModal
buttonColor="danger"
cancelButtonText="Cancel"
className="eui-textBreakWord"
confirmButtonText="Delete"
defaultFocusedButton="confirm"
onCancel={[Function]}
onConfirm={[Function]}
title="Delete 2 filter lists"
>
<p>
Are you sure you want to delete
these filter lists
?
</p>
</EuiConfirmModal>
</EuiOverlayMask>
</div>
`;

View file

@ -16,7 +16,7 @@ import {
EUI_MODAL_CONFIRM_BUTTON,
} from '@elastic/eui';
import { checkPermission } from 'plugins/ml/privilege/check_privilege';
import { checkPermission } from '../../../../privilege/check_privilege';
import { deleteFilterLists } from './delete_filter_lists';
/*
@ -87,7 +87,7 @@ export class DeleteFilterListModal extends Component {
iconType="trash"
color="danger"
onClick={this.showModal}
isDisabled={(selectedFilterLists.length === 0 || this.canDeleteFilter === false)}
isDisabled={(selectedFilterLists === undefined || selectedFilterLists.length === 0 || this.canDeleteFilter === false)}
>
Delete
</EuiButton>
@ -98,7 +98,7 @@ export class DeleteFilterListModal extends Component {
}
}
DeleteFilterListModal.propTypes = {
selectedFilterLists: PropTypes.array.isRequired,
selectedFilterLists: PropTypes.array,
};

View file

@ -0,0 +1,92 @@
/*
* 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.
*/
// Create a mock for the canDeleteFilter privilege check.
// The mock is hoisted to the top, so need to prefix the mock function
// with 'mock' so it can be used lazily.
const mockCheckPermission = jest.fn(() => true);
jest.mock('../../../../privilege/check_privilege', () => ({
checkPermission: (privilege) => mockCheckPermission(privilege)
}));
jest.mock('../../../../services/ml_api_service', () => 'ml');
import { shallow } from 'enzyme';
import React from 'react';
import { DeleteFilterListModal } from './delete_filter_list_modal';
const testSelectedLists = [
{ filter_id: 'web_domains' },
{ filter_id: 'test_instances' }
];
const testProps = {
selectedFilterLists: testSelectedLists
};
describe('DeleteFilterListModal', () => {
test('renders as disabled delete button when no lists selected', () => {
const component = shallow(
<DeleteFilterListModal />
);
expect(component).toMatchSnapshot();
});
test('renders as enabled delete button when a list is selected', () => {
const component = shallow(
<DeleteFilterListModal {...testProps} />
);
expect(component).toMatchSnapshot();
});
test('renders modal after clicking delete button', () => {
const wrapper = shallow(
<DeleteFilterListModal {...testProps} />
);
wrapper.find('EuiButton').simulate('click');
wrapper.update();
expect(wrapper).toMatchSnapshot();
});
test('renders as delete button after opening and closing modal', () => {
const wrapper = shallow(
<DeleteFilterListModal {...testProps} />
);
wrapper.find('EuiButton').simulate('click');
const instance = wrapper.instance();
instance.closeModal();
wrapper.update();
expect(wrapper).toMatchSnapshot();
});
});
describe('DeleteFilterListModal false canDeleteFilter privilege', () => {
beforeEach(() => {
jest.resetModules();
});
test('renders as disabled delete button', () => {
mockCheckPermission.mockImplementationOnce(() => {
return false;
});
const component = shallow(
<DeleteFilterListModal {...testProps} />
);
expect(component).toMatchSnapshot();
});
});

View file

@ -5,7 +5,7 @@
*/
import { toastNotifications } from 'ui/notify';
import { ml } from 'plugins/ml/services/ml_api_service';
import { ml } from '../../../../services/ml_api_service';
export async function deleteFilterLists(filterListsToDelete) {

View file

@ -0,0 +1,147 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`FilterListUsagePopover opens the popover onButtonClick 1`] = `
<div>
<EuiPopover
anchorPosition="downCenter"
button={
<EuiButtonIcon
aria-label="Edit description"
color="primary"
iconType="pencil"
onClick={[Function]}
size="s"
type="button"
/>
}
closePopover={[Function]}
id="filter_list_description_popover"
isOpen={true}
ownFocus={true}
panelPaddingSize="m"
>
<div
style={
Object {
"width": "300px",
}
}
>
<EuiForm>
<EuiFormRow
describedByIds={Array []}
fullWidth={false}
hasEmptyLabelSpace={false}
label="Filter list description"
>
<EuiFieldText
compressed={false}
fullWidth={false}
isLoading={false}
name="filter_list_description"
onChange={[Function]}
value="A list of known safe domains"
/>
</EuiFormRow>
</EuiForm>
</div>
</EuiPopover>
</div>
`;
exports[`FilterListUsagePopover renders the popover with a description 1`] = `
<div>
<EuiPopover
anchorPosition="downCenter"
button={
<EuiButtonIcon
aria-label="Edit description"
color="primary"
iconType="pencil"
onClick={[Function]}
size="s"
type="button"
/>
}
closePopover={[Function]}
id="filter_list_description_popover"
isOpen={false}
ownFocus={true}
panelPaddingSize="m"
>
<div
style={
Object {
"width": "300px",
}
}
>
<EuiForm>
<EuiFormRow
describedByIds={Array []}
fullWidth={false}
hasEmptyLabelSpace={false}
label="Filter list description"
>
<EuiFieldText
compressed={false}
fullWidth={false}
isLoading={false}
name="filter_list_description"
onChange={[Function]}
value="A list of known safe domains"
/>
</EuiFormRow>
</EuiForm>
</div>
</EuiPopover>
</div>
`;
exports[`FilterListUsagePopover renders the popover with no description 1`] = `
<div>
<EuiPopover
anchorPosition="downCenter"
button={
<EuiButtonIcon
aria-label="Edit description"
color="primary"
iconType="pencil"
onClick={[Function]}
size="s"
type="button"
/>
}
closePopover={[Function]}
id="filter_list_description_popover"
isOpen={false}
ownFocus={true}
panelPaddingSize="m"
>
<div
style={
Object {
"width": "300px",
}
}
>
<EuiForm>
<EuiFormRow
describedByIds={Array []}
fullWidth={false}
hasEmptyLabelSpace={false}
label="Filter list description"
>
<EuiFieldText
compressed={false}
fullWidth={false}
isLoading={false}
name="filter_list_description"
onChange={[Function]}
/>
</EuiFormRow>
</EuiForm>
</div>
</EuiPopover>
</div>
`;

View file

@ -0,0 +1,69 @@
/*
* 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 { shallow } from 'enzyme';
import React from 'react';
import { EditDescriptionPopover } from './edit_description_popover';
function prepareTest(updateDescriptionFn) {
const props = {
description: 'A list of known safe domains',
updateDescription: updateDescriptionFn
};
const wrapper = shallow(
<EditDescriptionPopover {...props} />
);
return wrapper;
}
describe('FilterListUsagePopover', () => {
test('renders the popover with no description', () => {
const updateDescription = jest.fn(() => {});
const props = {
updateDescription
};
const component = shallow(
<EditDescriptionPopover {...props} />
);
expect(component).toMatchSnapshot();
});
test('renders the popover with a description', () => {
const updateDescription = jest.fn(() => {});
const wrapper = prepareTest(updateDescription);
expect(wrapper).toMatchSnapshot();
});
test('opens the popover onButtonClick', () => {
const updateDescription = jest.fn(() => {});
const wrapper = prepareTest(updateDescription);
const instance = wrapper.instance();
instance.onButtonClick();
wrapper.update();
expect(wrapper).toMatchSnapshot();
});
test('calls updateDescription on closing', () => {
const updateDescription = jest.fn(() => {});
const wrapper = prepareTest(updateDescription);
const instance = wrapper.instance();
instance.onButtonClick();
instance.closePopover();
wrapper.update();
expect(updateDescription).toHaveBeenCalled();
});
});

View file

@ -0,0 +1,120 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`FilterListUsagePopover opens the popover onButtonClick 1`] = `
<div>
<EuiPopover
anchorPosition="downCenter"
button={
<EuiButtonEmpty
color="primary"
iconSide="left"
onClick={[Function]}
size="s"
type="button"
>
3 detectors
</EuiButtonEmpty>
}
closePopover={[Function]}
id="detector_filter_list_usage"
isOpen={true}
ownFocus={true}
panelClassName="ml-filter-list-usage-popover"
panelPaddingSize="m"
>
<ul>
<li
key="mean responsetime"
>
mean responsetime
</li>
<li
key="max responsetime"
>
max responsetime
</li>
<li
key="count"
>
count
</li>
</ul>
</EuiPopover>
</div>
`;
exports[`FilterListUsagePopover renders the popover for 1 job 1`] = `
<div>
<EuiPopover
anchorPosition="downCenter"
button={
<EuiButtonEmpty
color="primary"
iconSide="left"
onClick={[Function]}
size="s"
type="button"
>
1 job
</EuiButtonEmpty>
}
closePopover={[Function]}
id="job_filter_list_usage"
isOpen={false}
ownFocus={true}
panelClassName="ml-filter-list-usage-popover"
panelPaddingSize="m"
>
<ul>
<li
key="farequote"
>
farequote
</li>
</ul>
</EuiPopover>
</div>
`;
exports[`FilterListUsagePopover renders the popover for 2 detectors 1`] = `
<div>
<EuiPopover
anchorPosition="downCenter"
button={
<EuiButtonEmpty
color="primary"
iconSide="left"
onClick={[Function]}
size="s"
type="button"
>
3 detectors
</EuiButtonEmpty>
}
closePopover={[Function]}
id="detector_filter_list_usage"
isOpen={false}
ownFocus={true}
panelClassName="ml-filter-list-usage-popover"
panelPaddingSize="m"
>
<ul>
<li
key="mean responsetime"
>
mean responsetime
</li>
<li
key="max responsetime"
>
max responsetime
</li>
<li
key="count"
>
count
</li>
</ul>
</EuiPopover>
</div>
`;

View file

@ -0,0 +1,56 @@
/*
* 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 { shallow } from 'enzyme';
import React from 'react';
import { FilterListUsagePopover } from './filter_list_usage_popover';
function prepareDetectorsTest() {
const props = {
entityType: 'detector',
entityValues: ['mean responsetime', 'max responsetime', 'count']
};
const wrapper = shallow(
<FilterListUsagePopover {...props} />
);
return { wrapper };
}
describe('FilterListUsagePopover', () => {
test('renders the popover for 1 job', () => {
const props = {
entityType: 'job',
entityValues: ['farequote']
};
const component = shallow(
<FilterListUsagePopover {...props} />
);
expect(component).toMatchSnapshot();
});
test('renders the popover for 2 detectors', () => {
const test = prepareDetectorsTest();
expect(test.wrapper).toMatchSnapshot();
});
test('opens the popover onButtonClick', () => {
const test = prepareDetectorsTest();
const instance = test.wrapper.instance();
instance.onButtonClick();
test.wrapper.update();
expect(test.wrapper).toMatchSnapshot();
});
});

View file

@ -0,0 +1,820 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`EditFilterList adds new items to filter list 1`] = `
<EuiPage
className="ml-edit-filter-lists"
restrictWidth={false}
>
<EuiPageContent
className="ml-edit-filter-lists-content"
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
>
<EditFilterListHeader
description="List of known safe domains"
isNewFilterIdInvalid={false}
newFilterId=""
totalItemCount={6}
updateDescription={[Function]}
updateNewFilterId={[Function]}
usedBy={
Object {
"detectors": Array [
"high info content",
],
"jobs": Array [
"dns_exfiltration",
],
}
}
/>
<EditFilterListToolbar
addItems={[Function]}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={0}
/>
<EuiSpacer
size="xl"
/>
<ItemsGrid
activePage={0}
items={
Array [
"amazon.com",
"elastic.co",
"google.co.uk",
"google.com",
"spotify.com",
"youtube.com",
]
}
itemsPerPage={50}
itemsPerPageOptions={
Array [
50,
100,
500,
1000,
]
}
numberColumns={4}
selectedItems={Array []}
setActivePage={[Function]}
setItemSelected={[Function]}
setItemsPerPage={[Function]}
totalItemCount={6}
/>
<EuiFlexGroup
alignItems="stretch"
component="div"
direction="row"
gutterSize="l"
justifyContent="flexEnd"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButtonEmpty
color="primary"
iconSide="left"
onClick={[Function]}
type="button"
>
Cancel
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButton
color="primary"
disabled={false}
fill={true}
iconSide="left"
onClick={[Function]}
type="button"
>
Save
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContent>
</EuiPage>
`;
exports[`EditFilterList renders after selecting an item and deleting it 1`] = `
<EuiPage
className="ml-edit-filter-lists"
restrictWidth={false}
>
<EuiPageContent
className="ml-edit-filter-lists-content"
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
>
<EditFilterListHeader
description="List of known safe domains"
isNewFilterIdInvalid={false}
newFilterId=""
totalItemCount={4}
updateDescription={[Function]}
updateNewFilterId={[Function]}
usedBy={
Object {
"detectors": Array [
"high info content",
],
"jobs": Array [
"dns_exfiltration",
],
}
}
/>
<EditFilterListToolbar
addItems={[Function]}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={1}
/>
<EuiSpacer
size="xl"
/>
<ItemsGrid
activePage={0}
items={
Array [
"google.com",
"google.co.uk",
"elastic.co",
"youtube.com",
]
}
itemsPerPage={50}
itemsPerPageOptions={
Array [
50,
100,
500,
1000,
]
}
numberColumns={4}
selectedItems={
Array [
"google.co.uk",
]
}
setActivePage={[Function]}
setItemSelected={[Function]}
setItemsPerPage={[Function]}
totalItemCount={4}
/>
<EuiFlexGroup
alignItems="stretch"
component="div"
direction="row"
gutterSize="l"
justifyContent="flexEnd"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButtonEmpty
color="primary"
iconSide="left"
onClick={[Function]}
type="button"
>
Cancel
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButton
color="primary"
disabled={false}
fill={true}
iconSide="left"
onClick={[Function]}
type="button"
>
Save
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContent>
</EuiPage>
`;
exports[`EditFilterList renders after selecting an item and deleting it 2`] = `
<EuiPage
className="ml-edit-filter-lists"
restrictWidth={false}
>
<EuiPageContent
className="ml-edit-filter-lists-content"
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
>
<EditFilterListHeader
description="List of known safe domains"
isNewFilterIdInvalid={false}
newFilterId=""
totalItemCount={3}
updateDescription={[Function]}
updateNewFilterId={[Function]}
usedBy={
Object {
"detectors": Array [
"high info content",
],
"jobs": Array [
"dns_exfiltration",
],
}
}
/>
<EditFilterListToolbar
addItems={[Function]}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={0}
/>
<EuiSpacer
size="xl"
/>
<ItemsGrid
activePage={0}
items={
Array [
"google.com",
"elastic.co",
"youtube.com",
]
}
itemsPerPage={50}
itemsPerPageOptions={
Array [
50,
100,
500,
1000,
]
}
numberColumns={4}
selectedItems={Array []}
setActivePage={[Function]}
setItemSelected={[Function]}
setItemsPerPage={[Function]}
totalItemCount={3}
/>
<EuiFlexGroup
alignItems="stretch"
component="div"
direction="row"
gutterSize="l"
justifyContent="flexEnd"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButtonEmpty
color="primary"
iconSide="left"
onClick={[Function]}
type="button"
>
Cancel
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButton
color="primary"
disabled={false}
fill={true}
iconSide="left"
onClick={[Function]}
type="button"
>
Save
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContent>
</EuiPage>
`;
exports[`EditFilterList renders the edit page for a new filter list and updates ID 1`] = `
<EuiPage
className="ml-edit-filter-lists"
restrictWidth={false}
>
<EuiPageContent
className="ml-edit-filter-lists-content"
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
>
<EditFilterListHeader
description=""
isNewFilterIdInvalid={true}
newFilterId=""
totalItemCount={0}
updateDescription={[Function]}
updateNewFilterId={[Function]}
/>
<EditFilterListToolbar
addItems={[Function]}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={0}
/>
<EuiSpacer
size="xl"
/>
<ItemsGrid
activePage={0}
items={Array []}
itemsPerPage={50}
itemsPerPageOptions={
Array [
50,
100,
500,
1000,
]
}
numberColumns={4}
selectedItems={Array []}
setActivePage={[Function]}
setItemSelected={[Function]}
setItemsPerPage={[Function]}
totalItemCount={0}
/>
<EuiFlexGroup
alignItems="stretch"
component="div"
direction="row"
gutterSize="l"
justifyContent="flexEnd"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButtonEmpty
color="primary"
iconSide="left"
onClick={[Function]}
type="button"
>
Cancel
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButton
color="primary"
disabled={true}
fill={true}
iconSide="left"
onClick={[Function]}
type="button"
>
Save
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContent>
</EuiPage>
`;
exports[`EditFilterList renders the edit page for a new filter list and updates ID 2`] = `
<EuiPage
className="ml-edit-filter-lists"
restrictWidth={false}
>
<EuiPageContent
className="ml-edit-filter-lists-content"
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
>
<EditFilterListHeader
description=""
isNewFilterIdInvalid={false}
newFilterId="new_filter_list"
totalItemCount={0}
updateDescription={[Function]}
updateNewFilterId={[Function]}
/>
<EditFilterListToolbar
addItems={[Function]}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={0}
/>
<EuiSpacer
size="xl"
/>
<ItemsGrid
activePage={0}
items={Array []}
itemsPerPage={50}
itemsPerPageOptions={
Array [
50,
100,
500,
1000,
]
}
numberColumns={4}
selectedItems={Array []}
setActivePage={[Function]}
setItemSelected={[Function]}
setItemsPerPage={[Function]}
totalItemCount={0}
/>
<EuiFlexGroup
alignItems="stretch"
component="div"
direction="row"
gutterSize="l"
justifyContent="flexEnd"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButtonEmpty
color="primary"
iconSide="left"
onClick={[Function]}
type="button"
>
Cancel
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButton
color="primary"
disabled={false}
fill={true}
iconSide="left"
onClick={[Function]}
type="button"
>
Save
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContent>
</EuiPage>
`;
exports[`EditFilterList renders the edit page for an existing filter list and updates description 1`] = `
<EuiPage
className="ml-edit-filter-lists"
restrictWidth={false}
>
<EuiPageContent
className="ml-edit-filter-lists-content"
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
>
<EditFilterListHeader
description="List of known safe domains"
isNewFilterIdInvalid={false}
newFilterId=""
totalItemCount={4}
updateDescription={[Function]}
updateNewFilterId={[Function]}
usedBy={
Object {
"detectors": Array [
"high info content",
],
"jobs": Array [
"dns_exfiltration",
],
}
}
/>
<EditFilterListToolbar
addItems={[Function]}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={0}
/>
<EuiSpacer
size="xl"
/>
<ItemsGrid
activePage={0}
items={
Array [
"google.com",
"google.co.uk",
"elastic.co",
"youtube.com",
]
}
itemsPerPage={50}
itemsPerPageOptions={
Array [
50,
100,
500,
1000,
]
}
numberColumns={4}
selectedItems={Array []}
setActivePage={[Function]}
setItemSelected={[Function]}
setItemsPerPage={[Function]}
totalItemCount={4}
/>
<EuiFlexGroup
alignItems="stretch"
component="div"
direction="row"
gutterSize="l"
justifyContent="flexEnd"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButtonEmpty
color="primary"
iconSide="left"
onClick={[Function]}
type="button"
>
Cancel
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButton
color="primary"
disabled={false}
fill={true}
iconSide="left"
onClick={[Function]}
type="button"
>
Save
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContent>
</EuiPage>
`;
exports[`EditFilterList renders the edit page for an existing filter list and updates description 2`] = `
<EuiPage
className="ml-edit-filter-lists"
restrictWidth={false}
>
<EuiPageContent
className="ml-edit-filter-lists-content"
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
>
<EditFilterListHeader
description="Known safe web domains"
isNewFilterIdInvalid={false}
newFilterId=""
totalItemCount={4}
updateDescription={[Function]}
updateNewFilterId={[Function]}
usedBy={
Object {
"detectors": Array [
"high info content",
],
"jobs": Array [
"dns_exfiltration",
],
}
}
/>
<EditFilterListToolbar
addItems={[Function]}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={0}
/>
<EuiSpacer
size="xl"
/>
<ItemsGrid
activePage={0}
items={
Array [
"google.com",
"google.co.uk",
"elastic.co",
"youtube.com",
]
}
itemsPerPage={50}
itemsPerPageOptions={
Array [
50,
100,
500,
1000,
]
}
numberColumns={4}
selectedItems={Array []}
setActivePage={[Function]}
setItemSelected={[Function]}
setItemsPerPage={[Function]}
totalItemCount={4}
/>
<EuiFlexGroup
alignItems="stretch"
component="div"
direction="row"
gutterSize="l"
justifyContent="flexEnd"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButtonEmpty
color="primary"
iconSide="left"
onClick={[Function]}
type="button"
>
Cancel
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButton
color="primary"
disabled={false}
fill={true}
iconSide="left"
onClick={[Function]}
type="button"
>
Save
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContent>
</EuiPage>
`;
exports[`EditFilterList updates the items per page 1`] = `
<EuiPage
className="ml-edit-filter-lists"
restrictWidth={false}
>
<EuiPageContent
className="ml-edit-filter-lists-content"
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
>
<EditFilterListHeader
description="List of known safe domains"
isNewFilterIdInvalid={false}
newFilterId=""
totalItemCount={4}
updateDescription={[Function]}
updateNewFilterId={[Function]}
usedBy={
Object {
"detectors": Array [
"high info content",
],
"jobs": Array [
"dns_exfiltration",
],
}
}
/>
<EditFilterListToolbar
addItems={[Function]}
deleteSelectedItems={[Function]}
onSearchChange={[Function]}
selectedItemCount={0}
/>
<EuiSpacer
size="xl"
/>
<ItemsGrid
activePage={0}
items={
Array [
"google.com",
"google.co.uk",
"elastic.co",
"youtube.com",
]
}
itemsPerPage={500}
itemsPerPageOptions={
Array [
50,
100,
500,
1000,
]
}
numberColumns={4}
selectedItems={Array []}
setActivePage={[Function]}
setItemSelected={[Function]}
setItemsPerPage={[Function]}
totalItemCount={4}
/>
<EuiFlexGroup
alignItems="stretch"
component="div"
direction="row"
gutterSize="l"
justifyContent="flexEnd"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButtonEmpty
color="primary"
iconSide="left"
onClick={[Function]}
type="button"
>
Cancel
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButton
color="primary"
disabled={false}
fill={true}
iconSide="left"
onClick={[Function]}
type="button"
>
Save
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageContent>
</EuiPage>
`;

View file

@ -0,0 +1,472 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`EditFilterListHeader renders the header when creating a new filter list with ID, description and items set 1`] = `
<React.Fragment>
<EuiFlexGroup
alignItems="baseline"
component="div"
direction="row"
gutterSize="l"
justifyContent="spaceBetween"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiFlexGroup
alignItems="baseline"
component="div"
direction="row"
gutterSize="m"
justifyContent="flexStart"
responsive={false}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiTitle
size="m"
>
<h1>
Create new filter list
</h1>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={true}
>
<EuiTextColor
color="subdued"
component="span"
>
<p>
15
items
in total
</p>
</EuiTextColor>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer
size="m"
/>
<EuiFormRow
describedByIds={Array []}
fullWidth={false}
hasEmptyLabelSpace={false}
helpText="Use lowercase alphanumerics (a-z and 0-9), hyphens or underscores; must start and end with an alphanumeric character"
isInvalid={false}
label="Filter list ID"
>
<EuiFieldText
compressed={false}
fullWidth={false}
isInvalid={false}
isLoading={false}
name="new_filter_id"
onChange={[Function]}
value="test_filter_list"
/>
</EuiFormRow>
<EuiFlexGroup
alignItems="baseline"
component="div"
direction="row"
gutterSize="s"
justifyContent="flexStart"
responsive={false}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiText
grow={true}
>
<p>
A test filter list
</p>
</EuiText>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={false}
>
<EditDescriptionPopover
description="A test filter list"
updateDescription={[MockFunction]}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer
size="s"
/>
</React.Fragment>
`;
exports[`EditFilterListHeader renders the header when creating a new filter list with the ID not set 1`] = `
<React.Fragment>
<EuiFlexGroup
alignItems="baseline"
component="div"
direction="row"
gutterSize="l"
justifyContent="spaceBetween"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiFlexGroup
alignItems="baseline"
component="div"
direction="row"
gutterSize="m"
justifyContent="flexStart"
responsive={false}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiTitle
size="m"
>
<h1>
Create new filter list
</h1>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={true}
>
<EuiTextColor
color="subdued"
component="span"
>
<p>
0
items
in total
</p>
</EuiTextColor>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer
size="m"
/>
<EuiFormRow
describedByIds={Array []}
error={
Array [
"Use lowercase alphanumerics (a-z and 0-9), hyphens or underscores; must start and end with an alphanumeric character",
]
}
fullWidth={false}
hasEmptyLabelSpace={false}
isInvalid={true}
label="Filter list ID"
>
<EuiFieldText
compressed={false}
fullWidth={false}
isInvalid={true}
isLoading={false}
name="new_filter_id"
onChange={[Function]}
value=""
/>
</EuiFormRow>
<EuiFlexGroup
alignItems="baseline"
component="div"
direction="row"
gutterSize="s"
justifyContent="flexStart"
responsive={false}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiText
grow={true}
>
<EuiTextColor
color="subdued"
component="span"
>
Add a description
</EuiTextColor>
</EuiText>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={false}
>
<EditDescriptionPopover
updateDescription={[MockFunction]}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer
size="s"
/>
</React.Fragment>
`;
exports[`EditFilterListHeader renders the header when editing an existing unused filter list with no description or items 1`] = `
<React.Fragment>
<EuiFlexGroup
alignItems="baseline"
component="div"
direction="row"
gutterSize="l"
justifyContent="spaceBetween"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiFlexGroup
alignItems="baseline"
component="div"
direction="row"
gutterSize="m"
justifyContent="flexStart"
responsive={false}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiTitle
size="m"
>
<h1>
Filter list test_filter_list
</h1>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={true}
>
<EuiTextColor
color="subdued"
component="span"
>
<p>
0
items
in total
</p>
</EuiTextColor>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer
size="m"
/>
<EuiFlexGroup
alignItems="baseline"
component="div"
direction="row"
gutterSize="s"
justifyContent="flexStart"
responsive={false}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiText
grow={true}
>
<EuiTextColor
color="subdued"
component="span"
>
Add a description
</EuiTextColor>
</EuiText>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={false}
>
<EditDescriptionPopover
updateDescription={[MockFunction]}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer
size="s"
/>
<React.Fragment>
<EuiText
grow={true}
>
<p>
This filter list is not used by any jobs.
</p>
</EuiText>
<EuiSpacer
size="s"
/>
</React.Fragment>
</React.Fragment>
`;
exports[`EditFilterListHeader renders the header when editing an existing used filter list with description and items set 1`] = `
<React.Fragment>
<EuiFlexGroup
alignItems="baseline"
component="div"
direction="row"
gutterSize="l"
justifyContent="spaceBetween"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiFlexGroup
alignItems="baseline"
component="div"
direction="row"
gutterSize="m"
justifyContent="flexStart"
responsive={false}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiTitle
size="m"
>
<h1>
Filter list test_filter_list
</h1>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={true}
>
<EuiTextColor
color="subdued"
component="span"
>
<p>
15
items
in total
</p>
</EuiTextColor>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer
size="m"
/>
<EuiFlexGroup
alignItems="baseline"
component="div"
direction="row"
gutterSize="s"
justifyContent="flexStart"
responsive={false}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiText
grow={true}
>
<p>
A test filter list
</p>
</EuiText>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={false}
>
<EditDescriptionPopover
description="A test filter list"
updateDescription={[MockFunction]}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer
size="s"
/>
<React.Fragment>
<div
className="ml-filter-list-usage"
>
<EuiText
grow={true}
>
This filter list is used in
</EuiText>
<FilterListUsagePopover
entityType="detector"
entityValues={
Array [
"mean CPUUtilization",
]
}
/>
<EuiText
grow={true}
>
across
</EuiText>
<FilterListUsagePopover
entityType="job"
entityValues={
Array [
"cloudwatch",
]
}
/>
</div>
<EuiSpacer
size="s"
/>
</React.Fragment>
</React.Fragment>
`;

View file

@ -0,0 +1,113 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`EditFilterListToolbar renders the toolbar with no items selected 1`] = `
<React.Fragment>
<EuiFlexGroup
alignItems="stretch"
component="div"
direction="row"
gutterSize="l"
justifyContent="flexEnd"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<AddItemPopover
addItems={[MockFunction]}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup
alignItems="center"
component="div"
direction="row"
gutterSize="xl"
justifyContent="flexStart"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButton
color="danger"
disabled={true}
fill={false}
iconSide="left"
onClick={[MockFunction]}
type="button"
>
Delete item
</EuiButton>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={true}
>
<EuiSearchBar
onChange={[MockFunction]}
/>
</EuiFlexItem>
</EuiFlexGroup>
</React.Fragment>
`;
exports[`EditFilterListToolbar renders the toolbar with one item selected 1`] = `
<React.Fragment>
<EuiFlexGroup
alignItems="stretch"
component="div"
direction="row"
gutterSize="l"
justifyContent="flexEnd"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<AddItemPopover
addItems={[MockFunction]}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup
alignItems="center"
component="div"
direction="row"
gutterSize="xl"
justifyContent="flexStart"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButton
color="danger"
disabled={false}
fill={false}
iconSide="left"
onClick={[MockFunction]}
type="button"
>
Delete item
</EuiButton>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={true}
>
<EuiSearchBar
onChange={[MockFunction]}
/>
</EuiFlexItem>
</EuiFlexGroup>
</React.Fragment>
`;

View file

@ -30,17 +30,21 @@ import { toastNotifications } from 'ui/notify';
import { EditFilterListHeader } from './header';
import { EditFilterListToolbar } from './toolbar';
import { ItemsGrid } from 'plugins/ml/components/items_grid';
import { ItemsGrid } from '../../../components/items_grid';
import {
isValidFilterListId,
saveFilterList
} from './utils';
import { ml } from 'plugins/ml/services/ml_api_service';
import { ml } from '../../../services/ml_api_service';
const DEFAULT_ITEMS_PER_PAGE = 50;
// Returns the list of items that match the query entered in the EuiSearchBar.
function getMatchingFilterItems(searchBarQuery, items) {
if (items === undefined) {
return [];
}
if (searchBarQuery === undefined) {
return [...items];
}
@ -116,7 +120,7 @@ export class EditFilterList extends Component {
return {
description: loadedFilter.description,
items: [...loadedFilter.items],
items: (loadedFilter.items !== undefined) ? [...loadedFilter.items] : [],
matchingItems,
selectedItems: [],
loadedFilter,

View file

@ -0,0 +1,113 @@
/*
* 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.
*/
// Define the required mocks used for loading, saving and validating the filter list.
jest.mock('./utils', () => ({
isValidFilterListId: () => true,
saveFilterList: jest.fn(),
}));
// Mock the call for loading the list of filters.
// The mock is hoisted to the top, so need to prefix the filter variable
// with 'mock' so it can be used lazily.
const mockTestFilter = {
filter_id: 'safe_domains',
description: 'List of known safe domains',
items: ['google.com', 'google.co.uk', 'elastic.co', 'youtube.com'],
used_by: {
detectors: ['high info content'],
jobs: ['dns_exfiltration']
},
};
jest.mock('../../../services/ml_api_service', () => ({
ml: {
filters: {
filters: () => {
return Promise.resolve(mockTestFilter);
}
}
}
}));
import { shallow } from 'enzyme';
import React from 'react';
import { EditFilterList } from './edit_filter_list';
function prepareEditTest() {
const wrapper = shallow(
<EditFilterList />
);
// Cannot find a way to generate the snapshot after the Promise in the mock ml.filters
// has resolved.
// So set the loaded filter state directly to ensure the snapshot is generated against
// the test filter and not the default empty state.
const instance = wrapper.instance();
instance.setLoadedFilterState(mockTestFilter);
wrapper.update();
return wrapper;
}
describe('EditFilterList', () => {
test('renders the edit page for a new filter list and updates ID', () => {
const wrapper = shallow(
<EditFilterList />
);
expect(wrapper).toMatchSnapshot();
const instance = wrapper.instance();
instance.updateNewFilterId('new_filter_list');
wrapper.update();
expect(wrapper).toMatchSnapshot();
});
test('renders the edit page for an existing filter list and updates description', () => {
const wrapper = prepareEditTest();
expect(wrapper).toMatchSnapshot();
const instance = wrapper.instance();
instance.updateDescription('Known safe web domains');
wrapper.update();
expect(wrapper).toMatchSnapshot();
});
test('updates the items per page', () => {
const wrapper = prepareEditTest();
const instance = wrapper.instance();
instance.setItemsPerPage(500);
wrapper.update();
expect(wrapper).toMatchSnapshot();
});
test('renders after selecting an item and deleting it', () => {
const wrapper = prepareEditTest();
const instance = wrapper.instance();
instance.setItemSelected(mockTestFilter.items[1], true);
wrapper.update();
expect(wrapper).toMatchSnapshot();
instance.deleteSelectedItems();
wrapper.update();
expect(wrapper).toMatchSnapshot();
});
test('adds new items to filter list', () => {
const wrapper = prepareEditTest();
const instance = wrapper.instance();
instance.addItems(['amazon.com', 'spotify.com']);
wrapper.update();
expect(wrapper).toMatchSnapshot();
});
});

View file

@ -0,0 +1,92 @@
/*
* 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 { shallow } from 'enzyme';
import React from 'react';
import { EditFilterListHeader } from './header';
describe('EditFilterListHeader', () => {
const updateNewFilterId = jest.fn(() => {});
const updateDescription = jest.fn(() => {});
const requiredProps = {
updateNewFilterId,
updateDescription,
};
test('renders the header when creating a new filter list with the ID not set', () => {
const props = {
...requiredProps,
newFilterId: '',
isNewFilterIdInvalid: true,
totalItemCount: 0,
};
const component = shallow(
<EditFilterListHeader {...props} />
);
expect(component).toMatchSnapshot();
});
test('renders the header when creating a new filter list with ID, description and items set', () => {
const props = {
...requiredProps,
newFilterId: 'test_filter_list',
isNewFilterIdInvalid: false,
description: 'A test filter list',
totalItemCount: 15,
};
const component = shallow(
<EditFilterListHeader {...props} />
);
expect(component).toMatchSnapshot();
});
test('renders the header when editing an existing unused filter list with no description or items', () => {
const props = {
...requiredProps,
filterId: 'test_filter_list',
totalItemCount: 0,
};
const component = shallow(
<EditFilterListHeader {...props} />
);
expect(component).toMatchSnapshot();
});
test('renders the header when editing an existing used filter list with description and items set', () => {
const props = {
...requiredProps,
filterId: 'test_filter_list',
description: 'A test filter list',
totalItemCount: 15,
usedBy: {
jobs: ['cloudwatch'],
detectors: ['mean CPUUtilization']
}
};
const component = shallow(
<EditFilterListHeader {...props} />
);
expect(component).toMatchSnapshot();
});
});

View file

@ -7,7 +7,7 @@
/*
* React component for the toolbar section of the edit filter list page,
* holding a search bar,, and buttons for adding and deleting items from the list.
* holding a search bar, and buttons for adding and deleting items from the list.
*/
import PropTypes from 'prop-types';

View file

@ -0,0 +1,53 @@
/*
* 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 { shallow } from 'enzyme';
import React from 'react';
import { EditFilterListToolbar } from './toolbar';
describe('EditFilterListToolbar', () => {
const onSearchChange = jest.fn(() => {});
const addItems = jest.fn(() => {});
const deleteSelectedItems = jest.fn(() => {});
const requiredProps = {
onSearchChange,
addItems,
deleteSelectedItems,
};
test('renders the toolbar with no items selected', () => {
const props = {
...requiredProps,
selectedItemCount: 0,
};
const component = shallow(
<EditFilterListToolbar {...props} />
);
expect(component).toMatchSnapshot();
});
test('renders the toolbar with one item selected', () => {
const props = {
...requiredProps,
selectedItemCount: 1,
};
const component = shallow(
<EditFilterListToolbar {...props} />
);
expect(component).toMatchSnapshot();
});
});

View file

@ -0,0 +1,39 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Filter Lists renders a list of filters 1`] = `
<EuiPage
className="ml-list-filter-lists"
restrictWidth={false}
>
<EuiPageContent
className="ml-list-filter-lists-content"
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
>
<FilterListsHeader
refreshFilterLists={[Function]}
totalCount={1}
/>
<FilterListsTable
filterLists={
Array [
Object {
"description": "List of known safe domains",
"filter_id": "safe_domains",
"item_count": 500,
"used_by": Object {
"jobs": Array [
"dns_exfiltration",
],
},
},
]
}
refreshFilterLists={[Function]}
selectedFilterLists={Array []}
setSelectedFilterLists={[Function]}
/>
</EuiPageContent>
</EuiPage>
`;

View file

@ -0,0 +1,114 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Filter Lists Header renders header 1`] = `
<React.Fragment>
<EuiFlexGroup
alignItems="baseline"
component="div"
direction="row"
gutterSize="l"
justifyContent="spaceBetween"
responsive={true}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiFlexGroup
alignItems="baseline"
component="div"
direction="row"
gutterSize="m"
justifyContent="flexStart"
responsive={false}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiTitle
size="m"
>
<h1>
Filter Lists
</h1>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={true}
>
<EuiTextColor
color="subdued"
component="span"
>
<p>
3
in total
</p>
</EuiTextColor>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem
component="div"
grow={false}
>
<EuiFlexGroup
alignItems="baseline"
component="div"
direction="row"
gutterSize="m"
justifyContent="flexStart"
responsive={false}
wrap={false}
>
<EuiFlexItem
component="div"
grow={false}
>
<EuiButtonEmpty
color="primary"
iconSide="left"
iconType="refresh"
onClick={[Function]}
size="s"
type="button"
>
Refresh
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer
size="m"
/>
<EuiText
grow={true}
>
<p>
<EuiTextColor
color="subdued"
component="span"
>
Filter lists contain values that you can use to include or exclude events from the machine learning analysis. You can use the same filter list in multiple jobs.
<br />
<EuiLink
color="primary"
href="https://www.elastic.co/guide/en/elastic-stack-overview/my-metadata-branch/ml-rules.html"
target="_blank"
type="button"
>
Learn more
</EuiLink>
</EuiTextColor>
</p>
</EuiText>
<EuiSpacer
size="m"
/>
</React.Fragment>
`;

View file

@ -0,0 +1,198 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Filter Lists Table renders with filter lists and selection supplied 1`] = `
<React.Fragment>
<React.Fragment>
<EuiInMemoryTable
className="ml-filter-lists-table"
columns={
Array [
Object {
"field": "filter_id",
"name": "ID",
"render": [Function],
"sortable": true,
},
Object {
"field": "description",
"name": "Description",
"sortable": true,
},
Object {
"field": "item_count",
"name": "Item count",
"sortable": true,
},
Object {
"field": "used_by",
"name": "In use",
"render": [Function],
"sortable": true,
},
]
}
isSelectable={true}
itemId="filter_id"
items={
Array [
Object {
"description": "List of known safe domains",
"filter_id": "safe_domains",
"item_count": 500,
"used_by": Object {
"jobs": Array [
"dns_exfiltration",
],
},
},
Object {
"description": "US East AWS instances",
"filter_id": "us_east_instances",
"item_count": 20,
"used_by": Object {
"jobs": Array [],
},
},
]
}
pagination={true}
responsive={true}
search={
Object {
"box": Object {
"incremental": true,
},
"filters": Array [],
"toolsRight": Array [
<NewFilterButton />,
<DeleteFilterListModal
refreshFilterLists={[MockFunction]}
selectedFilterLists={
Array [
Object {
"description": "List of known safe domains",
"filter_id": "safe_domains",
"item_count": 500,
"used_by": Object {
"jobs": Array [
"dns_exfiltration",
],
},
},
]
}
/>,
],
}
}
selection={
Object {
"onSelectionChange": [Function],
"selectable": [Function],
"selectableMessage": [Function],
}
}
sorting={
Object {
"sort": Object {
"direction": "asc",
"field": "filter_id",
},
}
}
/>
</React.Fragment>
</React.Fragment>
`;
exports[`Filter Lists Table renders with filter lists supplied 1`] = `
<React.Fragment>
<React.Fragment>
<EuiInMemoryTable
className="ml-filter-lists-table"
columns={
Array [
Object {
"field": "filter_id",
"name": "ID",
"render": [Function],
"sortable": true,
},
Object {
"field": "description",
"name": "Description",
"sortable": true,
},
Object {
"field": "item_count",
"name": "Item count",
"sortable": true,
},
Object {
"field": "used_by",
"name": "In use",
"render": [Function],
"sortable": true,
},
]
}
isSelectable={true}
itemId="filter_id"
items={
Array [
Object {
"description": "List of known safe domains",
"filter_id": "safe_domains",
"item_count": 500,
"used_by": Object {
"jobs": Array [
"dns_exfiltration",
],
},
},
Object {
"description": "US East AWS instances",
"filter_id": "us_east_instances",
"item_count": 20,
"used_by": Object {
"jobs": Array [],
},
},
]
}
pagination={true}
responsive={true}
search={
Object {
"box": Object {
"incremental": true,
},
"filters": Array [],
"toolsRight": Array [
<NewFilterButton />,
<DeleteFilterListModal
refreshFilterLists={[MockFunction]}
selectedFilterLists={undefined}
/>,
],
}
}
selection={
Object {
"onSelectionChange": [Function],
"selectable": [Function],
"selectableMessage": [Function],
}
}
sorting={
Object {
"sort": Object {
"direction": "asc",
"field": "filter_id",
},
}
}
/>
</React.Fragment>
</React.Fragment>
`;

View file

@ -22,7 +22,7 @@ import { toastNotifications } from 'ui/notify';
import { FilterListsHeader } from './header';
import { FilterListsTable } from './table';
import { ml } from 'plugins/ml/services/ml_api_service';
import { ml } from '../../../services/ml_api_service';
export class FilterLists extends Component {
@ -39,6 +39,21 @@ export class FilterLists extends Component {
this.refreshFilterLists();
}
setFilterLists = (filterLists) => {
// Check selected filter lists still exist.
this.setState((prevState) => {
const loadedFilterIds = filterLists.map(filterList => filterList.filter_id);
const selectedFilterLists = prevState.selectedFilterLists.filter((filterList) => {
return (loadedFilterIds.indexOf(filterList.filter_id) !== -1);
});
return {
filterLists,
selectedFilterLists
};
});
}
setSelectedFilterLists = (selectedFilterLists) => {
this.setState({ selectedFilterLists });
}
@ -47,18 +62,7 @@ export class FilterLists extends Component {
// Load the list of filters.
ml.filters.filtersStats()
.then((filterLists) => {
// Check selected filter lists still exist.
this.setState((prevState) => {
const loadedFilterIds = filterLists.map(filterList => filterList.filter_id);
const selectedFilterLists = prevState.selectedFilterLists.filter((filterList) => {
return (loadedFilterIds.indexOf(filterList.filter_id) !== -1);
});
return {
filterLists,
selectedFilterLists
};
});
this.setFilterLists(filterLists);
})
.catch((resp) => {
console.log('Error loading list of filters:', resp);

View file

@ -0,0 +1,53 @@
/*
* 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.
*/
jest.mock('../../../privilege/check_privilege', () => ({
checkPermission: () => true
}));
// Mock the call for loading the list of filters.
// The mock is hoisted to the top, so need to prefix the filter variable
// with 'mock' so it can be used lazily.
const mockTestFilter = {
filter_id: 'safe_domains',
description: 'List of known safe domains',
item_count: 500,
used_by: { jobs: ['dns_exfiltration'] },
};
jest.mock('../../../services/ml_api_service', () => ({
ml: {
filters: {
filtersStats: () => {
return Promise.resolve([mockTestFilter]);
}
}
}
}));
import { shallow } from 'enzyme';
import React from 'react';
import { FilterLists } from './filter_lists';
describe('Filter Lists', () => {
test('renders a list of filters', () => {
const wrapper = shallow(
<FilterLists />
);
// Cannot find a way to generate the snapshot after the Promise in the mock ml.filters
// has resolved.
// So set the filter lists directly to ensure the snapshot is generated against
// the test list and not the default empty state.
wrapper.instance().setFilterLists([mockTestFilter]);
wrapper.update();
expect(wrapper).toMatchSnapshot();
});
});

View file

@ -0,0 +1,35 @@
/*
* 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 { shallow } from 'enzyme';
import React from 'react';
import { FilterListsHeader } from './header';
describe('Filter Lists Header', () => {
const refreshFilterLists = jest.fn(() => {});
const requiredProps = {
totalCount: 3,
refreshFilterLists,
};
test('renders header', () => {
const props = {
...requiredProps,
};
const component = shallow(
<FilterListsHeader {...props} />
);
expect(component).toMatchSnapshot();
});
});

View file

@ -24,7 +24,7 @@ import {
} from '@elastic/eui';
import chrome from 'ui/chrome';
import { checkPermission } from 'plugins/ml/privilege/check_privilege';
import { checkPermission } from '../../../privilege/check_privilege';
import { DeleteFilterListModal } from '../components/delete_filter_list_modal';

View file

@ -0,0 +1,74 @@
/*
* 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.
*/
// Create a mock for the privilege check used within the table to
// enable/disable the 'New Filter' button.
jest.mock('../../../privilege/check_privilege', () => ({
checkPermission: () => true
}));
jest.mock('../../../services/ml_api_service', () => 'ml');
import { shallow } from 'enzyme';
import React from 'react';
import { FilterListsTable } from './table';
describe('Filter Lists Table', () => {
const setSelectedFilterLists = jest.fn(() => {});
const refreshFilterLists = jest.fn(() => {});
const requiredProps = {
setSelectedFilterLists,
refreshFilterLists,
};
const testFilterLists = [
{
filter_id: 'safe_domains',
description: 'List of known safe domains',
item_count: 500,
used_by: { jobs: ['dns_exfiltration'] },
},
{
filter_id: 'us_east_instances',
description: 'US East AWS instances',
item_count: 20,
used_by: { jobs: [] },
},
];
test('renders with filter lists supplied', () => {
const props = {
...requiredProps,
filterLists: testFilterLists
};
const component = shallow(
<FilterListsTable {...props} />
);
expect(component).toMatchSnapshot();
});
test('renders with filter lists and selection supplied', () => {
const props = {
...requiredProps,
filterLists: testFilterLists,
selectedFilterLists: [testFilterLists[0]],
};
const component = shallow(
<FilterListsTable {...props} />
);
expect(component).toMatchSnapshot();
});
});