From cdc4ab6f67e585d35061c80712edc0241e7e02e0 Mon Sep 17 00:00:00 2001 From: Pete Harverson Date: Wed, 8 Aug 2018 15:22:02 +0100 Subject: [PATCH] [ML] Adds Jest tests for the Filter List Settings components (#21782) --- .../add_item_popover.test.js.snap | 232 +++++ .../add_item_popover/add_item_popover.test.js | 64 ++ .../delete_filter_list_modal.test.js.snap | 104 +++ .../delete_filter_list_modal.js | 6 +- .../delete_filter_list_modal.test.js | 92 ++ .../delete_filter_lists.js | 2 +- .../edit_description_popover.test.js.snap | 147 ++++ .../edit_description_popover.test.js | 69 ++ .../filter_list_usage_popover.test.js.snap | 120 +++ .../filter_list_usage_popover.test.js | 56 ++ .../edit_filter_list.test.js.snap | 820 ++++++++++++++++++ .../edit/__snapshots__/header.test.js.snap | 472 ++++++++++ .../edit/__snapshots__/toolbar.test.js.snap | 113 +++ .../filter_lists/edit/edit_filter_list.js | 10 +- .../edit/edit_filter_list.test.js | 113 +++ .../settings/filter_lists/edit/header.test.js | 92 ++ .../settings/filter_lists/edit/toolbar.js | 2 +- .../filter_lists/edit/toolbar.test.js | 53 ++ .../__snapshots__/filter_lists.test.js.snap | 39 + .../list/__snapshots__/header.test.js.snap | 114 +++ .../list/__snapshots__/table.test.js.snap | 198 +++++ .../filter_lists/list/filter_lists.js | 30 +- .../filter_lists/list/filter_lists.test.js | 53 ++ .../settings/filter_lists/list/header.test.js | 35 + .../settings/filter_lists/list/table.js | 2 +- .../settings/filter_lists/list/table.test.js | 74 ++ 26 files changed, 3090 insertions(+), 22 deletions(-) create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/components/add_item_popover/add_item_popover.test.js create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/__snapshots__/delete_filter_list_modal.test.js.snap create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.test.js create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/components/edit_description_popover/__snapshots__/edit_description_popover.test.js.snap create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/components/edit_description_popover/edit_description_popover.test.js create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/components/filter_list_usage_popover/__snapshots__/filter_list_usage_popover.test.js.snap create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/components/filter_list_usage_popover/filter_list_usage_popover.test.js create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/edit/__snapshots__/edit_filter_list.test.js.snap create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/edit/__snapshots__/header.test.js.snap create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/edit/__snapshots__/toolbar.test.js.snap create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/edit/edit_filter_list.test.js create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/edit/header.test.js create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/edit/toolbar.test.js create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/list/__snapshots__/filter_lists.test.js.snap create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/list/__snapshots__/header.test.js.snap create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/list/__snapshots__/table.test.js.snap create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/list/filter_lists.test.js create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/list/header.test.js create mode 100644 x-pack/plugins/ml/public/settings/filter_lists/list/table.test.js diff --git a/x-pack/plugins/ml/public/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap b/x-pack/plugins/ml/public/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap new file mode 100644 index 000000000000..fc98b2e69df7 --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap @@ -0,0 +1,232 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AddItemPopover calls addItems with multiple items on clicking Add button 1`] = ` +
+ + Add item + + } + closePopover={[Function]} + id="add_item_popover" + isOpen={false} + ownFocus={true} + panelClassName="ml-add-filter-item-popover" + panelPaddingSize="m" + > + + + + + + + Enter one item per line + + + + + + Add + + + + +
+`; + +exports[`AddItemPopover opens the popover onButtonClick 1`] = ` +
+ + Add item + + } + closePopover={[Function]} + id="add_item_popover" + isOpen={true} + ownFocus={true} + panelClassName="ml-add-filter-item-popover" + panelPaddingSize="m" + > + + + + + + + Enter one item per line + + + + + + Add + + + + +
+`; + +exports[`AddItemPopover renders the popover 1`] = ` +
+ + Add item + + } + closePopover={[Function]} + id="add_item_popover" + isOpen={false} + ownFocus={true} + panelClassName="ml-add-filter-item-popover" + panelPaddingSize="m" + > + + + + + + + Enter one item per line + + + + + + Add + + + + +
+`; diff --git a/x-pack/plugins/ml/public/settings/filter_lists/components/add_item_popover/add_item_popover.test.js b/x-pack/plugins/ml/public/settings/filter_lists/components/add_item_popover/add_item_popover.test.js new file mode 100644 index 000000000000..e4ce7e7104ae --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/components/add_item_popover/add_item_popover.test.js @@ -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( + + ); + + 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(); + }); + +}); diff --git a/x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/__snapshots__/delete_filter_list_modal.test.js.snap b/x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/__snapshots__/delete_filter_list_modal.test.js.snap new file mode 100644 index 000000000000..d2cdca52de78 --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/__snapshots__/delete_filter_list_modal.test.js.snap @@ -0,0 +1,104 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DeleteFilterListModal false canDeleteFilter privilege renders as disabled delete button 1`] = ` +
+ + Delete + +
+`; + +exports[`DeleteFilterListModal renders as delete button after opening and closing modal 1`] = ` +
+ + Delete + +
+`; + +exports[`DeleteFilterListModal renders as disabled delete button when no lists selected 1`] = ` +
+ + Delete + +
+`; + +exports[`DeleteFilterListModal renders as enabled delete button when a list is selected 1`] = ` +
+ + Delete + +
+`; + +exports[`DeleteFilterListModal renders modal after clicking delete button 1`] = ` +
+ + Delete + + + +

+ Are you sure you want to delete + these filter lists + ? +

+
+
+
+`; diff --git a/x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.js b/x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.js index 43b5be11a4ca..f147b4891283 100644 --- a/x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.js +++ b/x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.js @@ -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 @@ -98,7 +98,7 @@ export class DeleteFilterListModal extends Component { } } DeleteFilterListModal.propTypes = { - selectedFilterLists: PropTypes.array.isRequired, + selectedFilterLists: PropTypes.array, }; diff --git a/x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.test.js b/x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.test.js new file mode 100644 index 000000000000..87078e1592e5 --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.test.js @@ -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( + + ); + + expect(component).toMatchSnapshot(); + }); + + test('renders as enabled delete button when a list is selected', () => { + const component = shallow( + + ); + + expect(component).toMatchSnapshot(); + }); + + test('renders modal after clicking delete button', () => { + const wrapper = shallow( + + ); + wrapper.find('EuiButton').simulate('click'); + wrapper.update(); + expect(wrapper).toMatchSnapshot(); + }); + + + test('renders as delete button after opening and closing modal', () => { + const wrapper = shallow( + + ); + 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( + + ); + + expect(component).toMatchSnapshot(); + }); + +}); diff --git a/x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/delete_filter_lists.js b/x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/delete_filter_lists.js index b07bf002328e..42f65247483c 100644 --- a/x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/delete_filter_lists.js +++ b/x-pack/plugins/ml/public/settings/filter_lists/components/delete_filter_list_modal/delete_filter_lists.js @@ -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) { diff --git a/x-pack/plugins/ml/public/settings/filter_lists/components/edit_description_popover/__snapshots__/edit_description_popover.test.js.snap b/x-pack/plugins/ml/public/settings/filter_lists/components/edit_description_popover/__snapshots__/edit_description_popover.test.js.snap new file mode 100644 index 000000000000..e59216401033 --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/components/edit_description_popover/__snapshots__/edit_description_popover.test.js.snap @@ -0,0 +1,147 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FilterListUsagePopover opens the popover onButtonClick 1`] = ` +
+ + } + closePopover={[Function]} + id="filter_list_description_popover" + isOpen={true} + ownFocus={true} + panelPaddingSize="m" + > +
+ + + + + +
+
+
+`; + +exports[`FilterListUsagePopover renders the popover with a description 1`] = ` +
+ + } + closePopover={[Function]} + id="filter_list_description_popover" + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+ + + + + +
+
+
+`; + +exports[`FilterListUsagePopover renders the popover with no description 1`] = ` +
+ + } + closePopover={[Function]} + id="filter_list_description_popover" + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + > +
+ + + + + +
+
+
+`; diff --git a/x-pack/plugins/ml/public/settings/filter_lists/components/edit_description_popover/edit_description_popover.test.js b/x-pack/plugins/ml/public/settings/filter_lists/components/edit_description_popover/edit_description_popover.test.js new file mode 100644 index 000000000000..2d6a4372771b --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/components/edit_description_popover/edit_description_popover.test.js @@ -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( + + ); + + return wrapper; +} + +describe('FilterListUsagePopover', () => { + + test('renders the popover with no description', () => { + const updateDescription = jest.fn(() => {}); + + const props = { + updateDescription + }; + + const component = shallow( + + ); + + 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(); + }); + +}); diff --git a/x-pack/plugins/ml/public/settings/filter_lists/components/filter_list_usage_popover/__snapshots__/filter_list_usage_popover.test.js.snap b/x-pack/plugins/ml/public/settings/filter_lists/components/filter_list_usage_popover/__snapshots__/filter_list_usage_popover.test.js.snap new file mode 100644 index 000000000000..d66c33bf095e --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/components/filter_list_usage_popover/__snapshots__/filter_list_usage_popover.test.js.snap @@ -0,0 +1,120 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FilterListUsagePopover opens the popover onButtonClick 1`] = ` +
+ + 3 detectors + + } + closePopover={[Function]} + id="detector_filter_list_usage" + isOpen={true} + ownFocus={true} + panelClassName="ml-filter-list-usage-popover" + panelPaddingSize="m" + > +
    +
  • + mean responsetime +
  • +
  • + max responsetime +
  • +
  • + count +
  • +
+
+
+`; + +exports[`FilterListUsagePopover renders the popover for 1 job 1`] = ` +
+ + 1 job + + } + closePopover={[Function]} + id="job_filter_list_usage" + isOpen={false} + ownFocus={true} + panelClassName="ml-filter-list-usage-popover" + panelPaddingSize="m" + > +
    +
  • + farequote +
  • +
+
+
+`; + +exports[`FilterListUsagePopover renders the popover for 2 detectors 1`] = ` +
+ + 3 detectors + + } + closePopover={[Function]} + id="detector_filter_list_usage" + isOpen={false} + ownFocus={true} + panelClassName="ml-filter-list-usage-popover" + panelPaddingSize="m" + > +
    +
  • + mean responsetime +
  • +
  • + max responsetime +
  • +
  • + count +
  • +
+
+
+`; diff --git a/x-pack/plugins/ml/public/settings/filter_lists/components/filter_list_usage_popover/filter_list_usage_popover.test.js b/x-pack/plugins/ml/public/settings/filter_lists/components/filter_list_usage_popover/filter_list_usage_popover.test.js new file mode 100644 index 000000000000..83f219b001b0 --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/components/filter_list_usage_popover/filter_list_usage_popover.test.js @@ -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( + + ); + + return { wrapper }; +} + +describe('FilterListUsagePopover', () => { + + test('renders the popover for 1 job', () => { + const props = { + entityType: 'job', + entityValues: ['farequote'] + }; + + const component = shallow( + + ); + + 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(); + }); + +}); diff --git a/x-pack/plugins/ml/public/settings/filter_lists/edit/__snapshots__/edit_filter_list.test.js.snap b/x-pack/plugins/ml/public/settings/filter_lists/edit/__snapshots__/edit_filter_list.test.js.snap new file mode 100644 index 000000000000..44dc4ddddbd9 --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/edit/__snapshots__/edit_filter_list.test.js.snap @@ -0,0 +1,820 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EditFilterList adds new items to filter list 1`] = ` + + + + + + + + + + Cancel + + + + + Save + + + + + +`; + +exports[`EditFilterList renders after selecting an item and deleting it 1`] = ` + + + + + + + + + + Cancel + + + + + Save + + + + + +`; + +exports[`EditFilterList renders after selecting an item and deleting it 2`] = ` + + + + + + + + + + Cancel + + + + + Save + + + + + +`; + +exports[`EditFilterList renders the edit page for a new filter list and updates ID 1`] = ` + + + + + + + + + + Cancel + + + + + Save + + + + + +`; + +exports[`EditFilterList renders the edit page for a new filter list and updates ID 2`] = ` + + + + + + + + + + Cancel + + + + + Save + + + + + +`; + +exports[`EditFilterList renders the edit page for an existing filter list and updates description 1`] = ` + + + + + + + + + + Cancel + + + + + Save + + + + + +`; + +exports[`EditFilterList renders the edit page for an existing filter list and updates description 2`] = ` + + + + + + + + + + Cancel + + + + + Save + + + + + +`; + +exports[`EditFilterList updates the items per page 1`] = ` + + + + + + + + + + Cancel + + + + + Save + + + + + +`; diff --git a/x-pack/plugins/ml/public/settings/filter_lists/edit/__snapshots__/header.test.js.snap b/x-pack/plugins/ml/public/settings/filter_lists/edit/__snapshots__/header.test.js.snap new file mode 100644 index 000000000000..9ca5b210256b --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/edit/__snapshots__/header.test.js.snap @@ -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`] = ` + + + + + + +

+ Create new filter list +

+
+
+ + +

+ 15 + + items + in total +

+
+
+
+
+
+ + + + + + + +

+ A test filter list +

+
+
+ + + +
+ +
+`; + +exports[`EditFilterListHeader renders the header when creating a new filter list with the ID not set 1`] = ` + + + + + + +

+ Create new filter list +

+
+
+ + +

+ 0 + + items + in total +

+
+
+
+
+
+ + + + + + + + + Add a description + + + + + + + + +
+`; + +exports[`EditFilterListHeader renders the header when editing an existing unused filter list with no description or items 1`] = ` + + + + + + +

+ Filter list test_filter_list +

+
+
+ + +

+ 0 + + items + in total +

+
+
+
+
+
+ + + + + + Add a description + + + + + + + + + + +

+ This filter list is not used by any jobs. +

+
+ +
+
+`; + +exports[`EditFilterListHeader renders the header when editing an existing used filter list with description and items set 1`] = ` + + + + + + +

+ Filter list test_filter_list +

+
+
+ + +

+ 15 + + items + in total +

+
+
+
+
+
+ + + + +

+ A test filter list +

+
+
+ + + +
+ + +
+ + This filter list is used in + + + + across + + +
+ +
+
+`; diff --git a/x-pack/plugins/ml/public/settings/filter_lists/edit/__snapshots__/toolbar.test.js.snap b/x-pack/plugins/ml/public/settings/filter_lists/edit/__snapshots__/toolbar.test.js.snap new file mode 100644 index 000000000000..80b743fe73eb --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/edit/__snapshots__/toolbar.test.js.snap @@ -0,0 +1,113 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EditFilterListToolbar renders the toolbar with no items selected 1`] = ` + + + + + + + + + + Delete item + + + + + + + +`; + +exports[`EditFilterListToolbar renders the toolbar with one item selected 1`] = ` + + + + + + + + + + Delete item + + + + + + + +`; diff --git a/x-pack/plugins/ml/public/settings/filter_lists/edit/edit_filter_list.js b/x-pack/plugins/ml/public/settings/filter_lists/edit/edit_filter_list.js index 8a7bd87f003c..f63556160400 100644 --- a/x-pack/plugins/ml/public/settings/filter_lists/edit/edit_filter_list.js +++ b/x-pack/plugins/ml/public/settings/filter_lists/edit/edit_filter_list.js @@ -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, diff --git a/x-pack/plugins/ml/public/settings/filter_lists/edit/edit_filter_list.test.js b/x-pack/plugins/ml/public/settings/filter_lists/edit/edit_filter_list.test.js new file mode 100644 index 000000000000..1386f8a4b1da --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/edit/edit_filter_list.test.js @@ -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( + + ); + + // 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( + + ); + 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(); + }); + +}); diff --git a/x-pack/plugins/ml/public/settings/filter_lists/edit/header.test.js b/x-pack/plugins/ml/public/settings/filter_lists/edit/header.test.js new file mode 100644 index 000000000000..f3313d6f8d06 --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/edit/header.test.js @@ -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( + + ); + + 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( + + ); + + 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( + + ); + + 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( + + ); + + expect(component).toMatchSnapshot(); + + }); + + +}); diff --git a/x-pack/plugins/ml/public/settings/filter_lists/edit/toolbar.js b/x-pack/plugins/ml/public/settings/filter_lists/edit/toolbar.js index 16cdd2654a7b..80b9a269f574 100644 --- a/x-pack/plugins/ml/public/settings/filter_lists/edit/toolbar.js +++ b/x-pack/plugins/ml/public/settings/filter_lists/edit/toolbar.js @@ -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'; diff --git a/x-pack/plugins/ml/public/settings/filter_lists/edit/toolbar.test.js b/x-pack/plugins/ml/public/settings/filter_lists/edit/toolbar.test.js new file mode 100644 index 000000000000..50e4faa45069 --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/edit/toolbar.test.js @@ -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( + + ); + + expect(component).toMatchSnapshot(); + + }); + + test('renders the toolbar with one item selected', () => { + const props = { + ...requiredProps, + selectedItemCount: 1, + }; + + const component = shallow( + + ); + + expect(component).toMatchSnapshot(); + + }); + +}); diff --git a/x-pack/plugins/ml/public/settings/filter_lists/list/__snapshots__/filter_lists.test.js.snap b/x-pack/plugins/ml/public/settings/filter_lists/list/__snapshots__/filter_lists.test.js.snap new file mode 100644 index 000000000000..18e5fe8b1a2b --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/list/__snapshots__/filter_lists.test.js.snap @@ -0,0 +1,39 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Filter Lists renders a list of filters 1`] = ` + + + + + + +`; diff --git a/x-pack/plugins/ml/public/settings/filter_lists/list/__snapshots__/header.test.js.snap b/x-pack/plugins/ml/public/settings/filter_lists/list/__snapshots__/header.test.js.snap new file mode 100644 index 000000000000..34da004d8013 --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/list/__snapshots__/header.test.js.snap @@ -0,0 +1,114 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Filter Lists Header renders header 1`] = ` + + + + + + +

+ Filter Lists +

+
+
+ + +

+ 3 + in total +

+
+
+
+
+ + + + + Refresh + + + + +
+ + +

+ + 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. +
+ + Learn more + +
+

+
+ +
+`; diff --git a/x-pack/plugins/ml/public/settings/filter_lists/list/__snapshots__/table.test.js.snap b/x-pack/plugins/ml/public/settings/filter_lists/list/__snapshots__/table.test.js.snap new file mode 100644 index 000000000000..cc3079080ecd --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/list/__snapshots__/table.test.js.snap @@ -0,0 +1,198 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Filter Lists Table renders with filter lists and selection supplied 1`] = ` + + + , + , + ], + } + } + selection={ + Object { + "onSelectionChange": [Function], + "selectable": [Function], + "selectableMessage": [Function], + } + } + sorting={ + Object { + "sort": Object { + "direction": "asc", + "field": "filter_id", + }, + } + } + /> + + +`; + +exports[`Filter Lists Table renders with filter lists supplied 1`] = ` + + + , + , + ], + } + } + selection={ + Object { + "onSelectionChange": [Function], + "selectable": [Function], + "selectableMessage": [Function], + } + } + sorting={ + Object { + "sort": Object { + "direction": "asc", + "field": "filter_id", + }, + } + } + /> + + +`; diff --git a/x-pack/plugins/ml/public/settings/filter_lists/list/filter_lists.js b/x-pack/plugins/ml/public/settings/filter_lists/list/filter_lists.js index 2c2047da36ab..e469ba58eb52 100644 --- a/x-pack/plugins/ml/public/settings/filter_lists/list/filter_lists.js +++ b/x-pack/plugins/ml/public/settings/filter_lists/list/filter_lists.js @@ -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); diff --git a/x-pack/plugins/ml/public/settings/filter_lists/list/filter_lists.test.js b/x-pack/plugins/ml/public/settings/filter_lists/list/filter_lists.test.js new file mode 100644 index 000000000000..7299525eb5b8 --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/list/filter_lists.test.js @@ -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( + + ); + + // 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(); + }); + +}); diff --git a/x-pack/plugins/ml/public/settings/filter_lists/list/header.test.js b/x-pack/plugins/ml/public/settings/filter_lists/list/header.test.js new file mode 100644 index 000000000000..45e7c6c0eebc --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/list/header.test.js @@ -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( + + ); + + expect(component).toMatchSnapshot(); + + }); + +}); diff --git a/x-pack/plugins/ml/public/settings/filter_lists/list/table.js b/x-pack/plugins/ml/public/settings/filter_lists/list/table.js index d3c170883213..c348ffe4d0e8 100644 --- a/x-pack/plugins/ml/public/settings/filter_lists/list/table.js +++ b/x-pack/plugins/ml/public/settings/filter_lists/list/table.js @@ -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'; diff --git a/x-pack/plugins/ml/public/settings/filter_lists/list/table.test.js b/x-pack/plugins/ml/public/settings/filter_lists/list/table.test.js new file mode 100644 index 000000000000..e1f31be9d70e --- /dev/null +++ b/x-pack/plugins/ml/public/settings/filter_lists/list/table.test.js @@ -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( + + ); + + expect(component).toMatchSnapshot(); + + }); + + test('renders with filter lists and selection supplied', () => { + const props = { + ...requiredProps, + filterLists: testFilterLists, + selectedFilterLists: [testFilterLists[0]], + }; + + const component = shallow( + + ); + + expect(component).toMatchSnapshot(); + + }); + +});