diff --git a/x-pack/plugins/observability/public/hooks/use_get_filtered_rule_types.ts b/x-pack/plugins/observability/public/hooks/use_get_filtered_rule_types.ts new file mode 100644 index 0000000000000..d72118182ed65 --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/use_get_filtered_rule_types.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMemo } from 'react'; +import { usePluginContext } from './use_plugin_context'; + +export function useGetFilteredRuleTypes() { + const { observabilityRuleTypeRegistry } = usePluginContext(); + + return useMemo(() => observabilityRuleTypeRegistry.list(), [observabilityRuleTypeRegistry]); +} diff --git a/x-pack/plugins/observability/public/observability_public_plugins_start.mock.ts b/x-pack/plugins/observability/public/observability_public_plugins_start.mock.tsx similarity index 50% rename from x-pack/plugins/observability/public/observability_public_plugins_start.mock.ts rename to x-pack/plugins/observability/public/observability_public_plugins_start.mock.tsx index 31aa38cb2fd49..6ccca21b37907 100644 --- a/x-pack/plugins/observability/public/observability_public_plugins_start.mock.ts +++ b/x-pack/plugins/observability/public/observability_public_plugins_start.mock.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import React from 'react'; import { mockCasesContract } from '@kbn/cases-plugin/public/mocks'; const embeddableStartMock = { @@ -26,13 +26,39 @@ const embeddableStartMock = { const triggersActionsUiStartMock = { createStart() { return { - getAddAlertFlyout: jest.fn(), - getAlertsSearchBar: jest.fn(), - getRuleStatusDropdown: jest.fn(), - getRuleTagBadge: jest.fn(), - getRuleStatusFilter: jest.fn(), - getRuleTagFilter: jest.fn(), - getRulesList: jest.fn(), + getAddAlertFlyout: jest.fn(() => ( +
mocked component
+ )), + getAlertsSearchBar: jest.fn(() => ( +
mocked component
+ )), + getAlertsStateTable: jest.fn(() => ( +
mocked component
+ )), + getEditAlertFlyout: jest.fn(() => ( +
mocked component
+ )), + getRuleAlertsSummary: jest.fn(() => ( +
mocked component
+ )), + getRuleDefinition: jest.fn(() => ( +
mocked component
+ )), + getRuleEventLogList: jest.fn(() => ( +
mocked component
+ )), + getRuleStatusDropdown: jest.fn(() => ( +
mocked component
+ )), + getRuleStatusPanel: jest.fn(() => ( +
mocked component
+ )), + getRuleTagBadge: jest.fn(() =>
mocked component
), + getRuleStatusFilter: jest.fn(() => ( +
mocked component
+ )), + getRuleTagFilter: jest.fn(() =>
mocked component
), + getRulesList: jest.fn(() =>
mocked component
), ruleTypeRegistry: { has: jest.fn(), register: jest.fn(), diff --git a/x-pack/plugins/observability/public/pages/rules/index.test.tsx b/x-pack/plugins/observability/public/pages/rules/index.test.tsx index 5c1a66246cd53..fafa723c1aa1b 100644 --- a/x-pack/plugins/observability/public/pages/rules/index.test.tsx +++ b/x-pack/plugins/observability/public/pages/rules/index.test.tsx @@ -6,11 +6,9 @@ */ import React from 'react'; -import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; -import { act } from 'react-dom/test-utils'; -import { ReactWrapper } from 'enzyme'; +import { render } from '@testing-library/react'; import { CoreStart } from '@kbn/core/public'; -import { ConfigSchema, ObservabilityPublicPluginsStart } from '../../plugin'; +import { ObservabilityPublicPluginsStart } from '../../plugin'; import { RulesPage } from '.'; import { kibanaStartMock } from '../../utils/kibana_react.mock'; import * as pluginContext from '../../hooks/use_plugin_context'; @@ -34,20 +32,18 @@ jest.mock('@kbn/triggers-actions-ui-plugin/public', () => ({ useLoadRuleTypes: jest.fn(), })); -const config = { - unsafe: { - alertDetails: { - apm: { enabled: false }, - logs: { enabled: false }, - metrics: { enabled: false }, - uptime: { enabled: false }, - }, - }, -} as ConfigSchema; - jest.spyOn(pluginContext, 'usePluginContext').mockImplementation(() => ({ appMountParameters: {} as AppMountParameters, - config, + config: { + unsafe: { + alertDetails: { + apm: { enabled: false }, + logs: { enabled: false }, + metrics: { enabled: false }, + uptime: { enabled: false }, + }, + }, + }, observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), ObservabilityPageTemplate: KibanaPageTemplate, kibanaFeatures: [], @@ -58,9 +54,8 @@ jest.spyOn(pluginContext, 'usePluginContext').mockImplementation(() => ({ const { useLoadRuleTypes } = jest.requireMock('@kbn/triggers-actions-ui-plugin/public'); describe('RulesPage with all capabilities', () => { - let wrapper: ReactWrapper; async function setup() { - const mockedRuleTypeIndex = new Map( + const ruleTypeIndex = new Map( Object.entries({ '1': { enabledInLicense: true, @@ -79,6 +74,7 @@ describe('RulesPage with all capabilities', () => { }, }) ); + const ruleTypes = [ { id: 'test_rule_type', @@ -96,51 +92,34 @@ describe('RulesPage with all capabilities', () => { ruleTaskTimeout: '1m', }, ]; + useLoadRuleTypes.mockReturnValue({ ruleTypes, - ruleTypeIndex: mockedRuleTypeIndex, - }); - wrapper = mountWithIntl(); - await act(async () => { - await nextTick(); - wrapper.update(); + ruleTypeIndex, }); + + return render(); } - it('renders table of rules', async () => { - await setup(); - const getRulesList = mockUseKibanaReturnValue.services.triggersActionsUi.getRulesList; - expect(getRulesList).toHaveBeenCalled(); - expect(getRulesList).toHaveBeenCalledWith( - expect.objectContaining({ - showActionFilter: false, - showCreateRuleButton: false, - ruleDetailsRoute: 'alerts/rules/:ruleId', - filteredRuleTypes: ['ruleType1', 'ruleType2'], - rulesListKey: 'observability_rulesListColumns', - visibleColumns: [ - 'ruleName', - 'ruleExecutionStatusLastDate', - 'ruleSnoozeNotify', - 'ruleExecutionStatus', - 'ruleExecutionState', - ], - }) - ); + it('should render a page template', async () => { + const wrapper = await setup(); + expect(wrapper.getByTestId('rulesPage')).toBeInTheDocument(); }); - it('renders a create rule button that is not disabled', async () => { - await setup(); - expect(wrapper.find('[data-test-subj="createRuleButton"]').hostNodes().prop('disabled')).toBe( - false - ); + it('should render a RuleList ', async () => { + const wrapper = await setup(); + expect(wrapper.getByTestId('rules-list')).toBeInTheDocument(); + }); + + it('renders a create rule button which is not disabled', async () => { + const wrapper = await setup(); + expect(wrapper.getByTestId('createRuleButton')).not.toBeDisabled(); }); }); describe('RulesPage with show only capability', () => { - let wrapper: ReactWrapper; async function setup() { - const mockedRuleTypeIndex = new Map( + const ruleTypeIndex = new Map( Object.entries({ '1': { enabledInLicense: true, @@ -159,6 +138,7 @@ describe('RulesPage with show only capability', () => { }, }) ); + const ruleTypes = [ { id: 'test_rule_type', @@ -176,16 +156,13 @@ describe('RulesPage with show only capability', () => { ruleTaskTimeout: '1m', }, ]; - useLoadRuleTypes.mockReturnValue({ ruleTypes, ruleTypeIndex: mockedRuleTypeIndex }); + useLoadRuleTypes.mockReturnValue({ ruleTypes, ruleTypeIndex }); - wrapper = mountWithIntl(); + return render(); } - it('renders a create rule button that is disabled', async () => { - await setup(); - - expect(wrapper.find('[data-test-subj="createRuleButton"]').hostNodes().prop('disabled')).toBe( - true - ); + it('renders a create rule button which is not disabled', async () => { + const wrapper = await setup(); + expect(wrapper.getByTestId('createRuleButton')).toBeDisabled(); }); }); diff --git a/x-pack/plugins/observability/public/pages/rules/index.tsx b/x-pack/plugins/observability/public/pages/rules/index.tsx index 7051aa68d944f..166e62b06d261 100644 --- a/x-pack/plugins/observability/public/pages/rules/index.tsx +++ b/x-pack/plugins/observability/public/pages/rules/index.tsx @@ -5,19 +5,19 @@ * 2.0. */ -import React, { useState, useMemo } from 'react'; +import React, { useState } from 'react'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { useLoadRuleTypes } from '@kbn/triggers-actions-ui-plugin/public'; -import type { RulesListVisibleColumns } from '@kbn/triggers-actions-ui-plugin/public'; import { ALERTS_FEATURE_ID } from '@kbn/alerting-plugin/common'; +import type { RulesListVisibleColumns } from '@kbn/triggers-actions-ui-plugin/public'; -import { usePluginContext } from '../../hooks/use_plugin_context'; import { Provider, rulesPageStateContainer, useRulesPageStateContainer } from './state_container'; - -import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; import { useKibana } from '../../utils/kibana_react'; +import { usePluginContext } from '../../hooks/use_plugin_context'; +import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; +import { useGetFilteredRuleTypes } from '../../hooks/use_get_filtered_rule_types'; import { RULES_PAGE_TITLE, RULES_BREADCRUMB_TEXT } from './translations'; const RULES_LIST_COLUMNS_KEY = 'observability_rulesListColumns'; @@ -30,22 +30,23 @@ const RULES_LIST_COLUMNS: RulesListVisibleColumns[] = [ ]; function RulesPage() { - const { ObservabilityPageTemplate, observabilityRuleTypeRegistry } = usePluginContext(); - const { http, docLinks, triggersActionsUi } = useKibana().services; - - const documentationLink = docLinks.links.observability.createAlerts; - - const filteredRuleTypes = useMemo( - () => observabilityRuleTypeRegistry.list(), - [observabilityRuleTypeRegistry] - ); + const { ObservabilityPageTemplate } = usePluginContext(); + const { + http, + docLinks, + triggersActionsUi: { getAddAlertFlyout: AddAlertFlyout, getRulesList: RuleList }, + } = useKibana().services; const { status, setStatus, lastResponse, setLastResponse } = useRulesPageStateContainer(); - const [createRuleFlyoutVisibility, setCreateRuleFlyoutVisibility] = useState(false); - const [refresh, setRefresh] = useState(new Date()); + + const filteredRuleTypes = useGetFilteredRuleTypes(); const { ruleTypes } = useLoadRuleTypes({ filteredRuleTypes, }); + + const [addRuleFlyoutVisibility, setAddRuleFlyoutVisibility] = useState(false); + const [refresh, setRefresh] = useState(new Date()); + const authorizedRuleTypes = [...ruleTypes.values()]; const authorizedToCreateAnyRules = authorizedRuleTypes.some( @@ -64,54 +65,6 @@ function RulesPage() { }, ]); - const CreateRuleFlyout = useMemo( - () => - triggersActionsUi.getAddAlertFlyout({ - consumer: ALERTS_FEATURE_ID, - onClose: () => { - setCreateRuleFlyoutVisibility(false); - }, - onSave: () => { - setRefresh(new Date()); - return Promise.resolve(); - }, - filteredRuleTypes, - }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [filteredRuleTypes] - ); - - const getRulesTable = useMemo(() => { - return ( - <> - - - {triggersActionsUi.getRulesList({ - filteredRuleTypes, - showActionFilter: false, - showCreateRuleButton: false, - ruleDetailsRoute: 'alerts/rules/:ruleId', - statusFilter: status, - onStatusFilterChange: setStatus, - lastResponseFilter: lastResponse, - onLastResponseFilterChange: setLastResponse, - refresh, - rulesListKey: RULES_LIST_COLUMNS_KEY, - visibleColumns: RULES_LIST_COLUMNS, - })} - - - - ); - }, [ - triggersActionsUi, - filteredRuleTypes, - status, - setStatus, - lastResponse, - setLastResponse, - refresh, - ]); return ( setCreateRuleFlyoutVisibility(true)} + onClick={() => setAddRuleFlyoutVisibility(true)} > , , ], }} + data-test-subj="rulesPage" > - {getRulesTable} - {createRuleFlyoutVisibility && CreateRuleFlyout} + + + + + + + {addRuleFlyoutVisibility && ( + { + setAddRuleFlyoutVisibility(false); + }} + onSave={() => { + setRefresh(new Date()); + return Promise.resolve(); + }} + /> + )} ); }