mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[ResponseOps][Rules] Use rule form instead of rule flyout in observability solution (#206774)
## Summary Resolves https://github.com/elastic/kibana/issues/195574 This PR updates observability solution to use new rule form to `create` and `edit` rules same as `stack management > rules` page. It removes usage of rule flyout form o11y solution. Also updated functional tests. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### How to test - Create a rule in o11y, verify it works as expected - Edit rule in o11y via different options (from rule details page, rule list table, alert details page etc.) verify it works as expected - Verify the same in serverless o11y project ### Release Note Use rule form to create or edit rules in observability. --------- Co-authored-by: Maryam Saeidi <maryam.saeidi@elastic.co>
This commit is contained in:
parent
1ca4d967d9
commit
dd6376d3be
12 changed files with 469 additions and 61 deletions
|
@ -44,6 +44,7 @@ export interface CreateRuleFormProps {
|
||||||
shouldUseRuleProducer?: boolean;
|
shouldUseRuleProducer?: boolean;
|
||||||
canShowConsumerSelection?: boolean;
|
canShowConsumerSelection?: boolean;
|
||||||
showMustacheAutocompleteSwitch?: boolean;
|
showMustacheAutocompleteSwitch?: boolean;
|
||||||
|
isServerless?: boolean;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
onSubmit?: (ruleId: string) => void;
|
onSubmit?: (ruleId: string) => void;
|
||||||
}
|
}
|
||||||
|
@ -60,6 +61,7 @@ export const CreateRuleForm = (props: CreateRuleFormProps) => {
|
||||||
shouldUseRuleProducer = false,
|
shouldUseRuleProducer = false,
|
||||||
canShowConsumerSelection = true,
|
canShowConsumerSelection = true,
|
||||||
showMustacheAutocompleteSwitch = false,
|
showMustacheAutocompleteSwitch = false,
|
||||||
|
isServerless = false,
|
||||||
onCancel,
|
onCancel,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
} = props;
|
} = props;
|
||||||
|
@ -195,6 +197,7 @@ export const CreateRuleForm = (props: CreateRuleFormProps) => {
|
||||||
validConsumers,
|
validConsumers,
|
||||||
ruleType,
|
ruleType,
|
||||||
ruleTypes,
|
ruleTypes,
|
||||||
|
isServerless,
|
||||||
}),
|
}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { EuiEmptyPrompt, EuiText } from '@elastic/eui';
|
import { EuiEmptyPrompt, EuiText } from '@elastic/eui';
|
||||||
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
|
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
|
||||||
|
import { type RuleCreationValidConsumer } from '@kbn/rule-data-utils';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { CreateRuleForm } from './create_rule_form';
|
import { CreateRuleForm } from './create_rule_form';
|
||||||
import { EditRuleForm } from './edit_rule_form';
|
import { EditRuleForm } from './edit_rule_form';
|
||||||
|
@ -27,10 +28,20 @@ export interface RuleFormProps {
|
||||||
plugins: RuleFormPlugins;
|
plugins: RuleFormPlugins;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
onSubmit?: (ruleId: string) => void;
|
onSubmit?: (ruleId: string) => void;
|
||||||
|
validConsumers?: RuleCreationValidConsumer[];
|
||||||
|
multiConsumerSelection?: RuleCreationValidConsumer | null;
|
||||||
|
isServerless?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RuleForm = (props: RuleFormProps) => {
|
export const RuleForm = (props: RuleFormProps) => {
|
||||||
const { plugins: _plugins, onCancel, onSubmit } = props;
|
const {
|
||||||
|
plugins: _plugins,
|
||||||
|
onCancel,
|
||||||
|
onSubmit,
|
||||||
|
validConsumers,
|
||||||
|
multiConsumerSelection,
|
||||||
|
isServerless,
|
||||||
|
} = props;
|
||||||
const { id, ruleTypeId } = useParams<{
|
const { id, ruleTypeId } = useParams<{
|
||||||
id?: string;
|
id?: string;
|
||||||
ruleTypeId?: string;
|
ruleTypeId?: string;
|
||||||
|
@ -80,6 +91,9 @@ export const RuleForm = (props: RuleFormProps) => {
|
||||||
plugins={plugins}
|
plugins={plugins}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
|
validConsumers={validConsumers}
|
||||||
|
multiConsumerSelection={multiConsumerSelection}
|
||||||
|
isServerless={isServerless}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -112,6 +126,9 @@ export const RuleForm = (props: RuleFormProps) => {
|
||||||
actionTypeRegistry,
|
actionTypeRegistry,
|
||||||
id,
|
id,
|
||||||
ruleTypeId,
|
ruleTypeId,
|
||||||
|
validConsumers,
|
||||||
|
multiConsumerSelection,
|
||||||
|
isServerless,
|
||||||
onCancel,
|
onCancel,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -0,0 +1,247 @@
|
||||||
|
/*
|
||||||
|
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||||
|
* Public License v 1"; you may not use this file except in compliance with, at
|
||||||
|
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||||
|
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { RuleTypeWithDescription } from '@kbn/alerts-ui-shared';
|
||||||
|
import { getInitialMultiConsumer } from './get_initial_multi_consumer';
|
||||||
|
|
||||||
|
describe('getInitialMultiConsumer', () => {
|
||||||
|
const ruleType = {
|
||||||
|
id: '.es-query',
|
||||||
|
name: 'Test',
|
||||||
|
actionGroups: [
|
||||||
|
{
|
||||||
|
id: 'testActionGroup',
|
||||||
|
name: 'Test Action Group',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'recovered',
|
||||||
|
name: 'Recovered',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
defaultActionGroupId: 'testActionGroup',
|
||||||
|
minimumLicenseRequired: 'basic',
|
||||||
|
recoveryActionGroup: {
|
||||||
|
id: 'recovered',
|
||||||
|
name: 'Recovered',
|
||||||
|
},
|
||||||
|
producer: 'logs',
|
||||||
|
authorizedConsumers: {
|
||||||
|
alerting: { read: true, all: true },
|
||||||
|
test: { read: true, all: true },
|
||||||
|
stackAlerts: { read: true, all: true },
|
||||||
|
logs: { read: true, all: true },
|
||||||
|
},
|
||||||
|
actionVariables: {
|
||||||
|
params: [],
|
||||||
|
state: [],
|
||||||
|
},
|
||||||
|
enabledInLicense: true,
|
||||||
|
category: 'test',
|
||||||
|
} as RuleTypeWithDescription;
|
||||||
|
|
||||||
|
const ruleTypes = [
|
||||||
|
{
|
||||||
|
id: '.es-query',
|
||||||
|
name: 'Test',
|
||||||
|
actionGroups: [
|
||||||
|
{
|
||||||
|
id: 'testActionGroup',
|
||||||
|
name: 'Test Action Group',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'recovered',
|
||||||
|
name: 'Recovered',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
defaultActionGroupId: 'testActionGroup',
|
||||||
|
minimumLicenseRequired: 'basic',
|
||||||
|
recoveryActionGroup: {
|
||||||
|
id: 'recovered',
|
||||||
|
},
|
||||||
|
producer: 'logs',
|
||||||
|
authorizedConsumers: {
|
||||||
|
alerting: { read: true, all: true },
|
||||||
|
test: { read: true, all: true },
|
||||||
|
stackAlerts: { read: true, all: true },
|
||||||
|
logs: { read: true, all: true },
|
||||||
|
},
|
||||||
|
actionVariables: {
|
||||||
|
params: [],
|
||||||
|
state: [],
|
||||||
|
},
|
||||||
|
enabledInLicense: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabledInLicense: true,
|
||||||
|
recoveryActionGroup: {
|
||||||
|
id: 'recovered',
|
||||||
|
name: 'Recovered',
|
||||||
|
},
|
||||||
|
actionGroups: [],
|
||||||
|
defaultActionGroupId: 'threshold met',
|
||||||
|
minimumLicenseRequired: 'basic',
|
||||||
|
authorizedConsumers: {
|
||||||
|
stackAlerts: {
|
||||||
|
read: true,
|
||||||
|
all: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actionVariables: {
|
||||||
|
params: [],
|
||||||
|
state: [],
|
||||||
|
},
|
||||||
|
id: '.index-threshold',
|
||||||
|
name: 'Index threshold',
|
||||||
|
category: 'management',
|
||||||
|
producer: 'stackAlerts',
|
||||||
|
},
|
||||||
|
] as RuleTypeWithDescription[];
|
||||||
|
|
||||||
|
test('should return null when rule type id does not match', () => {
|
||||||
|
const res = getInitialMultiConsumer({
|
||||||
|
multiConsumerSelection: null,
|
||||||
|
validConsumers: ['logs', 'observability'],
|
||||||
|
ruleType: {
|
||||||
|
...ruleType,
|
||||||
|
id: 'test',
|
||||||
|
},
|
||||||
|
ruleTypes,
|
||||||
|
isServerless: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return null when no valid consumers', () => {
|
||||||
|
const res = getInitialMultiConsumer({
|
||||||
|
multiConsumerSelection: null,
|
||||||
|
validConsumers: [],
|
||||||
|
ruleType,
|
||||||
|
ruleTypes,
|
||||||
|
isServerless: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return same valid consumer when only one valid consumer', () => {
|
||||||
|
const res = getInitialMultiConsumer({
|
||||||
|
multiConsumerSelection: null,
|
||||||
|
validConsumers: ['alerts'],
|
||||||
|
ruleType,
|
||||||
|
ruleTypes,
|
||||||
|
isServerless: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res).toBe('alerts');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not return observability consumer for non serverless', () => {
|
||||||
|
const res = getInitialMultiConsumer({
|
||||||
|
multiConsumerSelection: null,
|
||||||
|
validConsumers: ['logs', 'infrastructure', 'observability'],
|
||||||
|
ruleType,
|
||||||
|
ruleTypes,
|
||||||
|
isServerless: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res).toBe('logs');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return observability consumer for serverless', () => {
|
||||||
|
const res = getInitialMultiConsumer({
|
||||||
|
multiConsumerSelection: null,
|
||||||
|
validConsumers: ['logs', 'infrastructure', 'observability'],
|
||||||
|
ruleType,
|
||||||
|
ruleTypes,
|
||||||
|
isServerless: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res).toBe('observability');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return null when there is no authorized consumers', () => {
|
||||||
|
const res = getInitialMultiConsumer({
|
||||||
|
multiConsumerSelection: null,
|
||||||
|
validConsumers: ['alerts', 'infrastructure'],
|
||||||
|
ruleType: {
|
||||||
|
...ruleType,
|
||||||
|
authorizedConsumers: {},
|
||||||
|
},
|
||||||
|
ruleTypes,
|
||||||
|
isServerless: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return null when multiConsumerSelection is null', () => {
|
||||||
|
const res = getInitialMultiConsumer({
|
||||||
|
multiConsumerSelection: null,
|
||||||
|
validConsumers: ['stackAlerts', 'logs'],
|
||||||
|
ruleType: {
|
||||||
|
...ruleType,
|
||||||
|
authorizedConsumers: {
|
||||||
|
stackAlerts: { read: true, all: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ruleTypes,
|
||||||
|
isServerless: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return valid multi consumer correctly', () => {
|
||||||
|
const res = getInitialMultiConsumer({
|
||||||
|
multiConsumerSelection: 'logs',
|
||||||
|
validConsumers: ['stackAlerts', 'logs'],
|
||||||
|
ruleType: {
|
||||||
|
...ruleType,
|
||||||
|
authorizedConsumers: {
|
||||||
|
stackAlerts: { read: true, all: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ruleTypes,
|
||||||
|
isServerless: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res).toBe('logs');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return stackAlerts correctly', () => {
|
||||||
|
const res = getInitialMultiConsumer({
|
||||||
|
multiConsumerSelection: 'alerts',
|
||||||
|
validConsumers: ['stackAlerts', 'logs'],
|
||||||
|
ruleType: {
|
||||||
|
...ruleType,
|
||||||
|
authorizedConsumers: {},
|
||||||
|
},
|
||||||
|
ruleTypes,
|
||||||
|
isServerless: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res).toBe('stackAlerts');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return null valid consumer correctly', () => {
|
||||||
|
const res = getInitialMultiConsumer({
|
||||||
|
multiConsumerSelection: 'alerts',
|
||||||
|
validConsumers: ['infrastructure', 'logs'],
|
||||||
|
ruleType: {
|
||||||
|
...ruleType,
|
||||||
|
authorizedConsumers: {},
|
||||||
|
},
|
||||||
|
ruleTypes: [],
|
||||||
|
isServerless: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res).toBe(null);
|
||||||
|
});
|
||||||
|
});
|
|
@ -35,11 +35,13 @@ export const getInitialMultiConsumer = ({
|
||||||
validConsumers,
|
validConsumers,
|
||||||
ruleType,
|
ruleType,
|
||||||
ruleTypes,
|
ruleTypes,
|
||||||
|
isServerless,
|
||||||
}: {
|
}: {
|
||||||
multiConsumerSelection?: RuleCreationValidConsumer | null;
|
multiConsumerSelection?: RuleCreationValidConsumer | null;
|
||||||
validConsumers: RuleCreationValidConsumer[];
|
validConsumers: RuleCreationValidConsumer[];
|
||||||
ruleType: RuleTypeWithDescription;
|
ruleType: RuleTypeWithDescription;
|
||||||
ruleTypes: RuleTypeWithDescription[];
|
ruleTypes: RuleTypeWithDescription[];
|
||||||
|
isServerless?: boolean;
|
||||||
}): RuleCreationValidConsumer | null => {
|
}): RuleCreationValidConsumer | null => {
|
||||||
// If rule type doesn't support multi-consumer or no valid consumers exists,
|
// If rule type doesn't support multi-consumer or no valid consumers exists,
|
||||||
// return nothing
|
// return nothing
|
||||||
|
@ -52,8 +54,8 @@ export const getInitialMultiConsumer = ({
|
||||||
return validConsumers[0];
|
return validConsumers[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// If o11y is in the valid consumers, just use that
|
// If o11y is in the valid consumers and it is serverless, just use that
|
||||||
if (validConsumers.includes(AlertConsumers.OBSERVABILITY)) {
|
if (isServerless && validConsumers.includes(AlertConsumers.OBSERVABILITY)) {
|
||||||
return AlertConsumers.OBSERVABILITY;
|
return AlertConsumers.OBSERVABILITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ export const EXPLORATORY_VIEW_PATH = '/exploratory-view' as const; // has been m
|
||||||
export const RULES_PATH = '/alerts/rules' as const;
|
export const RULES_PATH = '/alerts/rules' as const;
|
||||||
export const RULES_LOGS_PATH = '/alerts/rules/logs' as const;
|
export const RULES_LOGS_PATH = '/alerts/rules/logs' as const;
|
||||||
export const RULE_DETAIL_PATH = '/alerts/rules/:ruleId' as const;
|
export const RULE_DETAIL_PATH = '/alerts/rules/:ruleId' as const;
|
||||||
|
export const CREATE_RULE_PATH = '/alerts/rules/create/:ruleTypeId' as const;
|
||||||
export const CASES_PATH = '/cases' as const;
|
export const CASES_PATH = '/cases' as const;
|
||||||
export const ANNOTATIONS_PATH = '/annotations' as const;
|
export const ANNOTATIONS_PATH = '/annotations' as const;
|
||||||
export const SETTINGS_PATH = '/slos/settings' as const;
|
export const SETTINGS_PATH = '/slos/settings' as const;
|
||||||
|
@ -37,6 +38,8 @@ export const paths = {
|
||||||
rules: `${OBSERVABILITY_BASE_PATH}${RULES_PATH}`,
|
rules: `${OBSERVABILITY_BASE_PATH}${RULES_PATH}`,
|
||||||
ruleDetails: (ruleId: string) =>
|
ruleDetails: (ruleId: string) =>
|
||||||
`${OBSERVABILITY_BASE_PATH}${RULES_PATH}/${encodeURIComponent(ruleId)}`,
|
`${OBSERVABILITY_BASE_PATH}${RULES_PATH}/${encodeURIComponent(ruleId)}`,
|
||||||
|
createRule: (ruleTypeId: string) =>
|
||||||
|
`${OBSERVABILITY_BASE_PATH}${RULES_PATH}/create/${encodeURIComponent(ruleTypeId)}`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* 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 { i18n } from '@kbn/i18n';
|
||||||
|
import React from 'react';
|
||||||
|
import { RuleForm } from '@kbn/response-ops-rule-form';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import { AlertConsumers } from '@kbn/rule-data-utils';
|
||||||
|
import { useBreadcrumbs } from '@kbn/observability-shared-plugin/public';
|
||||||
|
import { HeaderMenu } from '../overview/components/header_menu/header_menu';
|
||||||
|
import { useKibana } from '../../utils/kibana_react';
|
||||||
|
import { paths } from '../../../common/locators/paths';
|
||||||
|
import { observabilityRuleCreationValidConsumers } from '../../../common/constants';
|
||||||
|
import { usePluginContext } from '../../hooks/use_plugin_context';
|
||||||
|
|
||||||
|
export function RulePage() {
|
||||||
|
const {
|
||||||
|
http,
|
||||||
|
docLinks,
|
||||||
|
observabilityAIAssistant,
|
||||||
|
application,
|
||||||
|
notifications,
|
||||||
|
charts,
|
||||||
|
settings,
|
||||||
|
data,
|
||||||
|
dataViews,
|
||||||
|
unifiedSearch,
|
||||||
|
serverless,
|
||||||
|
actionTypeRegistry,
|
||||||
|
ruleTypeRegistry,
|
||||||
|
chrome,
|
||||||
|
...startServices
|
||||||
|
} = useKibana().services;
|
||||||
|
const { ObservabilityPageTemplate } = usePluginContext();
|
||||||
|
const location = useLocation<{ returnApp?: string; returnPath?: string }>();
|
||||||
|
const { returnApp, returnPath } = location.state || {};
|
||||||
|
|
||||||
|
useBreadcrumbs(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', {
|
||||||
|
defaultMessage: 'Alerts',
|
||||||
|
}),
|
||||||
|
href: http.basePath.prepend(paths.observability.alerts),
|
||||||
|
deepLinkId: 'observability-overview:alerts',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
href: http.basePath.prepend(paths.observability.rules),
|
||||||
|
text: i18n.translate('xpack.observability.breadcrumbs.rulesLinkText', {
|
||||||
|
defaultMessage: 'Rules',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: i18n.translate('xpack.observability.breadcrumbs.createLinkText', {
|
||||||
|
defaultMessage: 'Create',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{ serverless }
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ObservabilityPageTemplate data-test-subj="rulePage">
|
||||||
|
<HeaderMenu />
|
||||||
|
<RuleForm
|
||||||
|
plugins={{
|
||||||
|
http,
|
||||||
|
application,
|
||||||
|
notifications,
|
||||||
|
charts,
|
||||||
|
settings,
|
||||||
|
data,
|
||||||
|
dataViews,
|
||||||
|
unifiedSearch,
|
||||||
|
docLinks,
|
||||||
|
ruleTypeRegistry,
|
||||||
|
actionTypeRegistry,
|
||||||
|
...startServices,
|
||||||
|
}}
|
||||||
|
validConsumers={observabilityRuleCreationValidConsumers}
|
||||||
|
multiConsumerSelection={AlertConsumers.LOGS}
|
||||||
|
isServerless={!!serverless}
|
||||||
|
onCancel={() => {
|
||||||
|
if (returnApp && returnPath) {
|
||||||
|
application.navigateToApp(returnApp, { path: returnPath });
|
||||||
|
} else {
|
||||||
|
return application.navigateToUrl(http.basePath.prepend(paths.observability.rules));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onSubmit={(ruleId) => {
|
||||||
|
return application.navigateToUrl(
|
||||||
|
http.basePath.prepend(paths.observability.ruleDetails(ruleId))
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ObservabilityPageTemplate>
|
||||||
|
);
|
||||||
|
}
|
|
@ -5,14 +5,16 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { fireEvent, render, waitFor } from '@testing-library/react';
|
||||||
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
import { ALERTING_FEATURE_ID } from '@kbn/alerting-plugin/common';
|
import { ALERTING_FEATURE_ID } from '@kbn/alerting-plugin/common';
|
||||||
import { AppMountParameters, CoreStart } from '@kbn/core/public';
|
import { AppMountParameters, CoreStart } from '@kbn/core/public';
|
||||||
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
|
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
|
||||||
import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock';
|
import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock';
|
||||||
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
|
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
|
||||||
import { render } from '@testing-library/react';
|
import { RuleTypeModalProps } from '@kbn/response-ops-rule-form/src/rule_type_modal/components/rule_type_modal';
|
||||||
import React from 'react';
|
|
||||||
import { useLocation } from 'react-router-dom';
|
|
||||||
import * as pluginContext from '../../hooks/use_plugin_context';
|
import * as pluginContext from '../../hooks/use_plugin_context';
|
||||||
import { ObservabilityPublicPluginsStart } from '../../plugin';
|
import { ObservabilityPublicPluginsStart } from '../../plugin';
|
||||||
import { createObservabilityRuleTypeRegistryMock } from '../../rules/observability_rule_type_registry_mock';
|
import { createObservabilityRuleTypeRegistryMock } from '../../rules/observability_rule_type_registry_mock';
|
||||||
|
@ -21,6 +23,12 @@ import { RulesPage } from './rules';
|
||||||
|
|
||||||
const mockUseKibanaReturnValue = kibanaStartMock.startContract();
|
const mockUseKibanaReturnValue = kibanaStartMock.startContract();
|
||||||
const mockObservabilityAIAssistant = observabilityAIAssistantPluginMock.createStartContract();
|
const mockObservabilityAIAssistant = observabilityAIAssistantPluginMock.createStartContract();
|
||||||
|
const mockApplication = {
|
||||||
|
navigateToApp: jest.fn(),
|
||||||
|
navigateToUrl: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
jest.mock('react-router-dom', () => ({
|
jest.mock('react-router-dom', () => ({
|
||||||
...jest.requireActual('react-router-dom'),
|
...jest.requireActual('react-router-dom'),
|
||||||
|
@ -34,6 +42,11 @@ jest.mock('../../utils/kibana_react', () => ({
|
||||||
services: {
|
services: {
|
||||||
...mockUseKibanaReturnValue.services,
|
...mockUseKibanaReturnValue.services,
|
||||||
observabilityAIAssistant: mockObservabilityAIAssistant,
|
observabilityAIAssistant: mockObservabilityAIAssistant,
|
||||||
|
application: {
|
||||||
|
...mockUseKibanaReturnValue.services.application,
|
||||||
|
navigateToApp: mockApplication.navigateToApp,
|
||||||
|
navigateToUrl: mockApplication.navigateToUrl,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
@ -48,6 +61,15 @@ jest.mock('@kbn/triggers-actions-ui-plugin/public', () => ({
|
||||||
useLoadRuleTypesQuery: jest.fn(),
|
useLoadRuleTypesQuery: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('@kbn/response-ops-rule-form/src/rule_type_modal', () => ({
|
||||||
|
RuleTypeModal: ({ onSelectRuleType }: RuleTypeModalProps) => (
|
||||||
|
<div data-test-subj="ruleTypeModal">
|
||||||
|
RuleTypeModal
|
||||||
|
<button onClick={() => onSelectRuleType('1')}>Rule type 1</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
const useLocationMock = useLocation as jest.Mock;
|
const useLocationMock = useLocation as jest.Mock;
|
||||||
|
|
||||||
jest.spyOn(pluginContext, 'usePluginContext').mockImplementation(() => ({
|
jest.spyOn(pluginContext, 'usePluginContext').mockImplementation(() => ({
|
||||||
|
@ -130,13 +152,17 @@ describe('RulesPage with all capabilities', () => {
|
||||||
|
|
||||||
useLoadRuleTypesQuery.mockReturnValue({
|
useLoadRuleTypesQuery.mockReturnValue({
|
||||||
ruleTypesState: {
|
ruleTypesState: {
|
||||||
|
isLoading: false,
|
||||||
|
isInitialLoading: false,
|
||||||
data: ruleTypeIndex,
|
data: ruleTypeIndex,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
<IntlProvider locale="en">
|
<IntlProvider locale="en">
|
||||||
<RulesPage />
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<RulesPage />
|
||||||
|
</QueryClientProvider>
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -155,6 +181,21 @@ describe('RulesPage with all capabilities', () => {
|
||||||
const wrapper = await setup();
|
const wrapper = await setup();
|
||||||
expect(wrapper.getByTestId('createRuleButton')).not.toBeDisabled();
|
expect(wrapper.getByTestId('createRuleButton')).not.toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('navigates to create rule form correctly', async () => {
|
||||||
|
const wrapper = await setup();
|
||||||
|
expect(wrapper.getByTestId('createRuleButton')).toBeInTheDocument();
|
||||||
|
|
||||||
|
fireEvent.click(wrapper.getByTestId('createRuleButton'));
|
||||||
|
expect(await wrapper.findByTestId('ruleTypeModal')).toBeInTheDocument();
|
||||||
|
|
||||||
|
fireEvent.click(await wrapper.findByText('Rule type 1'));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockApplication.navigateToUrl).toHaveBeenCalledWith(
|
||||||
|
'/app/observability/alerts/rules/create/1'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('RulesPage with show only capability', () => {
|
describe('RulesPage with show only capability', () => {
|
||||||
|
|
|
@ -11,12 +11,10 @@ import { ALERTING_FEATURE_ID } from '@kbn/alerting-plugin/common';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { FormattedMessage } from '@kbn/i18n-react';
|
import { FormattedMessage } from '@kbn/i18n-react';
|
||||||
import { useBreadcrumbs } from '@kbn/observability-shared-plugin/public';
|
import { useBreadcrumbs } from '@kbn/observability-shared-plugin/public';
|
||||||
import { AlertConsumers } from '@kbn/rule-data-utils';
|
|
||||||
import { useLoadRuleTypesQuery } from '@kbn/triggers-actions-ui-plugin/public';
|
import { useLoadRuleTypesQuery } from '@kbn/triggers-actions-ui-plugin/public';
|
||||||
import React, { lazy, useEffect, useState } from 'react';
|
import React, { lazy, useEffect, useState } from 'react';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { observabilityRuleCreationValidConsumers } from '../../../common/constants';
|
import { RULES_LOGS_PATH, RULES_PATH, paths } from '../../../common/locators/paths';
|
||||||
import { RULES_LOGS_PATH, RULES_PATH } from '../../../common/locators/paths';
|
|
||||||
import { useGetFilteredRuleTypes } from '../../hooks/use_get_filtered_rule_types';
|
import { useGetFilteredRuleTypes } from '../../hooks/use_get_filtered_rule_types';
|
||||||
import { usePluginContext } from '../../hooks/use_plugin_context';
|
import { usePluginContext } from '../../hooks/use_plugin_context';
|
||||||
import { useKibana } from '../../utils/kibana_react';
|
import { useKibana } from '../../utils/kibana_react';
|
||||||
|
@ -37,18 +35,13 @@ export function RulesPage({ activeTab = RULES_TAB_NAME }: RulesPageProps) {
|
||||||
docLinks,
|
docLinks,
|
||||||
notifications: { toasts },
|
notifications: { toasts },
|
||||||
observabilityAIAssistant,
|
observabilityAIAssistant,
|
||||||
triggersActionsUi: {
|
application,
|
||||||
ruleTypeRegistry,
|
triggersActionsUi: { ruleTypeRegistry, getRulesSettingsLink: RulesSettingsLink },
|
||||||
getAddRuleFlyout: AddRuleFlyout,
|
|
||||||
getRulesSettingsLink: RulesSettingsLink,
|
|
||||||
},
|
|
||||||
serverless,
|
serverless,
|
||||||
} = useKibana().services;
|
} = useKibana().services;
|
||||||
const { ObservabilityPageTemplate } = usePluginContext();
|
const { ObservabilityPageTemplate } = usePluginContext();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [ruleTypeModalVisibility, setRuleTypeModalVisibility] = useState<boolean>(false);
|
const [ruleTypeModalVisibility, setRuleTypeModalVisibility] = useState<boolean>(false);
|
||||||
const [ruleTypeIdToCreate, setRuleTypeIdToCreate] = useState<string | undefined>(undefined);
|
|
||||||
const [addRuleFlyoutVisibility, setAddRuleFlyoutVisibility] = useState(false);
|
|
||||||
const [stateRefresh, setRefresh] = useState(new Date());
|
const [stateRefresh, setRefresh] = useState(new Date());
|
||||||
|
|
||||||
useBreadcrumbs(
|
useBreadcrumbs(
|
||||||
|
@ -188,9 +181,10 @@ export function RulesPage({ activeTab = RULES_TAB_NAME }: RulesPageProps) {
|
||||||
<RuleTypeModal
|
<RuleTypeModal
|
||||||
onClose={() => setRuleTypeModalVisibility(false)}
|
onClose={() => setRuleTypeModalVisibility(false)}
|
||||||
onSelectRuleType={(ruleTypeId) => {
|
onSelectRuleType={(ruleTypeId) => {
|
||||||
setRuleTypeIdToCreate(ruleTypeId);
|
|
||||||
setRuleTypeModalVisibility(false);
|
setRuleTypeModalVisibility(false);
|
||||||
setAddRuleFlyoutVisibility(true);
|
return application.navigateToUrl(
|
||||||
|
http.basePath.prepend(paths.observability.createRule(ruleTypeId))
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
http={http}
|
http={http}
|
||||||
toasts={toasts}
|
toasts={toasts}
|
||||||
|
@ -198,26 +192,6 @@ export function RulesPage({ activeTab = RULES_TAB_NAME }: RulesPageProps) {
|
||||||
filteredRuleTypes={filteredRuleTypes}
|
filteredRuleTypes={filteredRuleTypes}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{addRuleFlyoutVisibility && (
|
|
||||||
<AddRuleFlyout
|
|
||||||
ruleTypeId={ruleTypeIdToCreate}
|
|
||||||
canChangeTrigger={false}
|
|
||||||
consumer={ALERTING_FEATURE_ID}
|
|
||||||
filteredRuleTypes={filteredRuleTypes}
|
|
||||||
validConsumers={observabilityRuleCreationValidConsumers}
|
|
||||||
initialSelectedConsumer={AlertConsumers.LOGS}
|
|
||||||
onClose={() => {
|
|
||||||
setAddRuleFlyoutVisibility(false);
|
|
||||||
}}
|
|
||||||
onSave={() => {
|
|
||||||
setRefresh(new Date());
|
|
||||||
return Promise.resolve();
|
|
||||||
}}
|
|
||||||
hideGrouping
|
|
||||||
useRuleProducer
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ObservabilityPageTemplate>
|
</ObservabilityPageTemplate>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { LandingPage } from '../pages/landing/landing';
|
||||||
import { OverviewPage } from '../pages/overview/overview';
|
import { OverviewPage } from '../pages/overview/overview';
|
||||||
import { RulesPage } from '../pages/rules/rules';
|
import { RulesPage } from '../pages/rules/rules';
|
||||||
import { RuleDetailsPage } from '../pages/rule_details/rule_details';
|
import { RuleDetailsPage } from '../pages/rule_details/rule_details';
|
||||||
|
import { RulePage } from '../pages/rules/rule';
|
||||||
import {
|
import {
|
||||||
ALERTS_PATH,
|
ALERTS_PATH,
|
||||||
ALERT_DETAIL_PATH,
|
ALERT_DETAIL_PATH,
|
||||||
|
@ -34,6 +35,7 @@ import {
|
||||||
OLD_SLOS_OUTDATED_DEFINITIONS_PATH,
|
OLD_SLOS_OUTDATED_DEFINITIONS_PATH,
|
||||||
OLD_SLO_DETAIL_PATH,
|
OLD_SLO_DETAIL_PATH,
|
||||||
OLD_SLO_EDIT_PATH,
|
OLD_SLO_EDIT_PATH,
|
||||||
|
CREATE_RULE_PATH,
|
||||||
} from '../../common/locators/paths';
|
} from '../../common/locators/paths';
|
||||||
import { HasDataContextProvider } from '../context/has_data_context/has_data_context';
|
import { HasDataContextProvider } from '../context/has_data_context/has_data_context';
|
||||||
|
|
||||||
|
@ -133,6 +135,13 @@ export const routes = {
|
||||||
params: {},
|
params: {},
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
|
[CREATE_RULE_PATH]: {
|
||||||
|
handler: () => {
|
||||||
|
return <RulePage />;
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
exact: true,
|
||||||
|
},
|
||||||
[ALERT_DETAIL_PATH]: {
|
[ALERT_DETAIL_PATH]: {
|
||||||
handler: () => {
|
handler: () => {
|
||||||
return <AlertDetails />;
|
return <AlertDetails />;
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Key } from 'selenium-webdriver';
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
import { FtrProviderContext } from '../../../../ftr_provider_context';
|
import { FtrProviderContext } from '../../../../ftr_provider_context';
|
||||||
|
|
||||||
export default ({ getService }: FtrProviderContext) => {
|
export default ({ getService, getPageObjects }: FtrProviderContext) => {
|
||||||
const esArchiver = getService('esArchiver');
|
const esArchiver = getService('esArchiver');
|
||||||
const testSubjects = getService('testSubjects');
|
const testSubjects = getService('testSubjects');
|
||||||
const kibanaServer = getService('kibanaServer');
|
const kibanaServer = getService('kibanaServer');
|
||||||
|
@ -17,9 +17,10 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
const find = getService('find');
|
const find = getService('find');
|
||||||
const logger = getService('log');
|
const logger = getService('log');
|
||||||
const retry = getService('retry');
|
const retry = getService('retry');
|
||||||
|
const toasts = getService('toasts');
|
||||||
|
const PageObjects = getPageObjects(['header']);
|
||||||
|
|
||||||
// FLAKY: https://github.com/elastic/kibana/issues/196766
|
describe('Custom threshold rule', function () {
|
||||||
describe.skip('Custom threshold rule', function () {
|
|
||||||
this.tags('includeFirefox');
|
this.tags('includeFirefox');
|
||||||
|
|
||||||
const observability = getService('observability');
|
const observability = getService('observability');
|
||||||
|
@ -58,13 +59,16 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
|
|
||||||
it('shows the custom threshold rule in the observability section', async () => {
|
it('shows the custom threshold rule in the observability section', async () => {
|
||||||
await observability.alerts.rulesPage.clickCreateRuleButton();
|
await observability.alerts.rulesPage.clickCreateRuleButton();
|
||||||
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||||
await observability.alerts.rulesPage.clickOnObservabilityCategory();
|
await observability.alerts.rulesPage.clickOnObservabilityCategory();
|
||||||
await observability.alerts.rulesPage.clickOnCustomThresholdRule();
|
await observability.alerts.rulesPage.clickOnCustomThresholdRule();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can add name and tags', async () => {
|
it('can add name and tags', async () => {
|
||||||
await testSubjects.setValue('ruleNameInput', 'test custom threshold rule');
|
await testSubjects.setValue('ruleDetailsNameInput', 'test custom threshold rule', {
|
||||||
await testSubjects.setValue('comboBoxSearchInput', 'tag1');
|
clearWithKeyboard: true,
|
||||||
|
});
|
||||||
|
await testSubjects.setValue('ruleDetailsTagsInput', 'tag1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can add data view', async () => {
|
it('can add data view', async () => {
|
||||||
|
@ -204,9 +208,11 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can save the rule', async () => {
|
it('can save the rule', async () => {
|
||||||
await testSubjects.click('saveRuleButton');
|
await testSubjects.click('rulePageFooterSaveButton');
|
||||||
await testSubjects.click('confirmModalConfirmButton');
|
await testSubjects.click('confirmModalConfirmButton');
|
||||||
await find.byCssSelector('button[title="test custom threshold rule"]');
|
|
||||||
|
const title = await toasts.getTitleAndDismiss();
|
||||||
|
expect(title).toEqual(`Created rule "test custom threshold rule"`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('saved the rule correctly', async () => {
|
it('saved the rule correctly', async () => {
|
||||||
|
@ -220,6 +226,7 @@ export default ({ getService }: FtrProviderContext) => {
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
name: 'test custom threshold rule',
|
name: 'test custom threshold rule',
|
||||||
tags: ['tag1'],
|
tags: ['tag1'],
|
||||||
|
consumer: 'logs',
|
||||||
params: expect.objectContaining({
|
params: expect.objectContaining({
|
||||||
alertOnGroupDisappear: false,
|
alertOnGroupDisappear: false,
|
||||||
alertOnNoData: false,
|
alertOnNoData: false,
|
||||||
|
|
|
@ -51,7 +51,7 @@ export default ({ getService, getPageObject }: FtrProviderContext) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does render the correct error message', async () => {
|
it('does render the correct error message', async () => {
|
||||||
await testSubjects.setValue('ruleNameInput', 'test custom threshold rule');
|
await testSubjects.setValue('ruleDetailsNameInput', 'test custom threshold rule');
|
||||||
|
|
||||||
await testSubjects.click('customEquation');
|
await testSubjects.click('customEquation');
|
||||||
const customEquationField = await find.byCssSelector(
|
const customEquationField = await find.byCssSelector(
|
||||||
|
|
|
@ -64,11 +64,11 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => {
|
||||||
const selectAndFillInEsQueryRule = async (ruleName: string) => {
|
const selectAndFillInEsQueryRule = async (ruleName: string) => {
|
||||||
await testSubjects.click(`.es-query-SelectOption`);
|
await testSubjects.click(`.es-query-SelectOption`);
|
||||||
await retry.waitFor(
|
await retry.waitFor(
|
||||||
'Create Rule flyout is visible',
|
'Create Rule form is visible',
|
||||||
async () => await testSubjects.exists('addRuleFlyoutTitle')
|
async () => await testSubjects.exists('createRuleForm')
|
||||||
);
|
);
|
||||||
|
|
||||||
await testSubjects.setValue('ruleNameInput', ruleName);
|
await testSubjects.setValue('ruleDetailsNameInput', ruleName);
|
||||||
await testSubjects.click('queryFormType_esQuery');
|
await testSubjects.click('queryFormType_esQuery');
|
||||||
await testSubjects.click('selectIndexExpression');
|
await testSubjects.click('selectIndexExpression');
|
||||||
const indexComboBox = await find.byCssSelector('#indexSelectSearchBox');
|
const indexComboBox = await find.byCssSelector('#indexSelectSearchBox');
|
||||||
|
@ -90,7 +90,7 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => {
|
||||||
|
|
||||||
const observability = getService('observability');
|
const observability = getService('observability');
|
||||||
|
|
||||||
const navigateAndOpenCreateRuleFlyout = async () => {
|
const navigateAndOpenRuleTypeModal = async () => {
|
||||||
await observability.alerts.common.navigateToRulesPage();
|
await observability.alerts.common.navigateToRulesPage();
|
||||||
await retry.waitFor(
|
await retry.waitFor(
|
||||||
'Create Rule button is visible',
|
'Create Rule button is visible',
|
||||||
|
@ -128,11 +128,11 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => {
|
||||||
|
|
||||||
describe('Create rule button', () => {
|
describe('Create rule button', () => {
|
||||||
it('Show Rule Type Modal when Create Rule button is clicked', async () => {
|
it('Show Rule Type Modal when Create Rule button is clicked', async () => {
|
||||||
await navigateAndOpenCreateRuleFlyout();
|
await navigateAndOpenRuleTypeModal();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Create rules flyout', () => {
|
describe('Create rules form', () => {
|
||||||
const ruleName = 'esQueryRule';
|
const ruleName = 'esQueryRule';
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
@ -151,13 +151,15 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => {
|
||||||
infrastructure: ['all'],
|
infrastructure: ['all'],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
await navigateAndOpenCreateRuleFlyout();
|
await navigateAndOpenRuleTypeModal();
|
||||||
await selectAndFillInEsQueryRule(ruleName);
|
await selectAndFillInEsQueryRule(ruleName);
|
||||||
|
|
||||||
await testSubjects.click('saveRuleButton');
|
await testSubjects.click('rulePageFooterSaveButton');
|
||||||
|
|
||||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||||
|
|
||||||
|
await observability.alerts.common.navigateToRulesPage();
|
||||||
|
|
||||||
const tableRows = await find.allByCssSelector('.euiTableRow');
|
const tableRows = await find.allByCssSelector('.euiTableRow');
|
||||||
const rows = await getRulesList(tableRows);
|
const rows = await getRulesList(tableRows);
|
||||||
expect(rows.length).to.be(1);
|
expect(rows.length).to.be(1);
|
||||||
|
@ -174,13 +176,14 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => {
|
||||||
logs: ['all'],
|
logs: ['all'],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
await navigateAndOpenCreateRuleFlyout();
|
await navigateAndOpenRuleTypeModal();
|
||||||
await selectAndFillInEsQueryRule(ruleName);
|
await selectAndFillInEsQueryRule(ruleName);
|
||||||
|
|
||||||
await testSubjects.click('saveRuleButton');
|
await testSubjects.click('rulePageFooterSaveButton');
|
||||||
|
|
||||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||||
|
|
||||||
|
await observability.alerts.common.navigateToRulesPage();
|
||||||
const tableRows = await find.allByCssSelector('.euiTableRow');
|
const tableRows = await find.allByCssSelector('.euiTableRow');
|
||||||
const rows = await getRulesList(tableRows);
|
const rows = await getRulesList(tableRows);
|
||||||
expect(rows.length).to.be(1);
|
expect(rows.length).to.be(1);
|
||||||
|
@ -196,17 +199,17 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
await navigateAndOpenCreateRuleFlyout();
|
await navigateAndOpenRuleTypeModal();
|
||||||
await selectAndFillInEsQueryRule(ruleName);
|
await selectAndFillInEsQueryRule(ruleName);
|
||||||
|
|
||||||
await retry.waitFor('consumer select modal is visible', async () => {
|
await retry.waitFor('consumer select modal is visible', async () => {
|
||||||
return await testSubjects.exists('ruleFormConsumerSelect');
|
return await testSubjects.exists('ruleConsumerSelection');
|
||||||
});
|
});
|
||||||
|
|
||||||
const consumerSelect = await testSubjects.find('ruleFormConsumerSelect');
|
const consumerSelect = await testSubjects.find('ruleConsumerSelection');
|
||||||
await consumerSelect.click();
|
await consumerSelect.click();
|
||||||
const consumerOptionsList = await testSubjects.find(
|
const consumerOptionsList = await testSubjects.find(
|
||||||
'comboBoxOptionsList ruleFormConsumerSelect-optionsList'
|
'comboBoxOptionsList ruleConsumerSelectionInput-optionsList'
|
||||||
);
|
);
|
||||||
const consumerOptions = await consumerOptionsList.findAllByClassName(
|
const consumerOptions = await consumerOptionsList.findAllByClassName(
|
||||||
'euiComboBoxOption__content'
|
'euiComboBoxOption__content'
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue