mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[RAM] Fix rule definition not showing actions of the same connector type (#142533)
* Fix rule definition actions is incomplete * Fix translation and tests * Address comments and add back tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
45dd05037d
commit
d206d8382a
11 changed files with 153 additions and 128 deletions
|
@ -32638,8 +32638,6 @@
|
|||
"xpack.triggersActionsUI.sections.rulesList.rulesListSnoozePanel.snoozeFailed": "Impossible de modifier les paramètres de répétition de la règle",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListSnoozePanel.snoozeSuccess": "Répétition de la règle activée",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListSnoozePanel.unsnoozeSuccess": "Répétition de la règle désactivée",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.actionsTex": "Actions",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.actionsWarningTooltip": "Impossible de charger l'un des connecteurs associés à cette règle. Modifiez la règle pour sélectionner un nouveau connecteur.",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.apiKeyOwnerTitle": "Propriétaire de la clé d'API",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.deleteAriaLabel": "Supprimer",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.deleteButtonTooltip": "Supprimer",
|
||||
|
|
|
@ -32612,8 +32612,6 @@
|
|||
"xpack.triggersActionsUI.sections.rulesList.rulesListSnoozePanel.snoozeFailed": "ルールスヌーズ設定を変更できません",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListSnoozePanel.snoozeSuccess": "ルールが正常にスヌーズされました",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListSnoozePanel.unsnoozeSuccess": "ルールが正常にスヌーズ解除されました",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.actionsTex": "アクション",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.actionsWarningTooltip": "このルールに関連付けられたコネクターの1つを読み込めません。ルールを編集して、新しいコネクターを選択します。",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.apiKeyOwnerTitle": "APIキー所有者",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.deleteAriaLabel": "削除",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.deleteButtonTooltip": "削除",
|
||||
|
|
|
@ -32649,8 +32649,6 @@
|
|||
"xpack.triggersActionsUI.sections.rulesList.rulesListSnoozePanel.snoozeFailed": "无法更改规则暂停设置",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListSnoozePanel.snoozeSuccess": "已成功暂停规则",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListSnoozePanel.unsnoozeSuccess": "已成功取消暂停规则",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.actionsTex": "操作",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.actionsWarningTooltip": "无法加载与此规则关联的连接器之一。请编辑该规则以选择新连接器。",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.apiKeyOwnerTitle": "API 密钥所有者",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.deleteAriaLabel": "删除",
|
||||
"xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.deleteButtonTooltip": "删除",
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { intersectionBy } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ActionConnector, loadAllActions } from '../..';
|
||||
import { useKibana } from '../../common/lib/kibana';
|
||||
|
@ -50,11 +49,10 @@ export function useFetchRuleActionConnectors({ ruleActions }: FetchRuleActionCon
|
|||
const allActions = await loadAllActions({
|
||||
http,
|
||||
});
|
||||
const actions = intersectionBy(allActions, ruleActions, 'actionTypeId');
|
||||
setActionConnector((oldState: FetchActionConnectors) => ({
|
||||
...oldState,
|
||||
isLoadingActionConnectors: false,
|
||||
actionConnectors: actions,
|
||||
actionConnectors: allActions,
|
||||
}));
|
||||
} catch (error) {
|
||||
const errorMsg = ACTIONS_LOAD_ERROR(
|
||||
|
|
|
@ -80,4 +80,91 @@ describe('Rule Actions', () => {
|
|||
expect(wrapper.find('[data-euiicon-type="index"]').length).toBe(0);
|
||||
expect(wrapper.find('[data-euiicon-type="email"]').length).toBe(0);
|
||||
});
|
||||
|
||||
it('renders multiple rule action connectors of the same type and connector', async () => {
|
||||
const ruleActions = [
|
||||
{
|
||||
id: '1',
|
||||
group: 'metrics.inventory_threshold.fired',
|
||||
actionTypeId: '.server-log',
|
||||
params: {},
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
group: 'metrics.inventory_threshold.fired',
|
||||
actionTypeId: '.server-log',
|
||||
params: {},
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
group: 'metrics.inventory_threshold.fired',
|
||||
actionTypeId: '.server-log',
|
||||
params: {},
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
group: 'metrics.inventory_threshold.fired',
|
||||
actionTypeId: '.slack',
|
||||
params: {},
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
group: 'metrics.inventory_threshold.fired',
|
||||
actionTypeId: '.slack',
|
||||
params: {},
|
||||
},
|
||||
];
|
||||
|
||||
mockedUseFetchRuleActionConnectorsHook.mockReturnValue({
|
||||
isLoadingActionConnectors: false,
|
||||
actionConnectors: [
|
||||
{
|
||||
id: '1',
|
||||
name: 'logs1',
|
||||
config: {},
|
||||
actionTypeId: '.server-log',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'logs2',
|
||||
config: {},
|
||||
actionTypeId: '.server-log',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: 'Slack1',
|
||||
actionTypeId: '.slack',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: 'Slack1',
|
||||
actionTypeId: '.slack',
|
||||
},
|
||||
] as Array<ActionConnector<Record<string, unknown>>>,
|
||||
errorActionConnectors: undefined,
|
||||
reloadRuleActionConnectors: jest.fn(),
|
||||
});
|
||||
|
||||
actionTypeRegistry.list.mockReturnValue([
|
||||
{ id: '.server-log', iconClass: 'logsApp' },
|
||||
{ id: '.slack', iconClass: 'logoSlack' },
|
||||
{ id: '.email', iconClass: 'email' },
|
||||
{ id: '.index', iconClass: 'indexOpen' },
|
||||
] as ActionTypeModel[]);
|
||||
|
||||
const wrapper = mount(
|
||||
<RuleActions ruleActions={ruleActions} actionTypeRegistry={actionTypeRegistry} />
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await nextTick();
|
||||
wrapper.update();
|
||||
});
|
||||
|
||||
expect(wrapper.find('[data-test-subj="actionConnectorName-0-logs1"]').exists).toBeTruthy();
|
||||
expect(wrapper.find('[data-test-subj="actionConnectorName-1-logs1"]').exists).toBeTruthy();
|
||||
expect(wrapper.find('[data-test-subj="actionConnectorName-2-logs2"]').exists).toBeTruthy();
|
||||
expect(wrapper.find('[data-test-subj="actionConnectorName-3-slack1"]').exists).toBeTruthy();
|
||||
expect(wrapper.find('[data-test-subj="actionConnectorName-4-slack2"]').exists).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -27,7 +27,11 @@ export function RuleActions({ ruleActions, actionTypeRegistry }: RuleActionsProp
|
|||
ruleActions,
|
||||
});
|
||||
|
||||
if (!actionConnectors || actionConnectors.length <= 0)
|
||||
const hasConnectors = actionConnectors && actionConnectors.length > 0;
|
||||
|
||||
const hasActions = ruleActions && ruleActions.length > 0;
|
||||
|
||||
if (!hasConnectors || !hasActions) {
|
||||
return (
|
||||
<EuiFlexItem>
|
||||
<EuiText size="s">
|
||||
|
@ -37,31 +41,44 @@ export function RuleActions({ ruleActions, actionTypeRegistry }: RuleActionsProp
|
|||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
}
|
||||
|
||||
function getActionIconClass(actionGroupId?: string): IconType | undefined {
|
||||
const getActionIconClass = (actionGroupId?: string): IconType | undefined => {
|
||||
const actionGroup = actionTypeRegistry.list().find((group) => group.id === actionGroupId);
|
||||
return typeof actionGroup?.iconClass === 'string'
|
||||
? actionGroup?.iconClass
|
||||
: suspendedComponentWithProps(actionGroup?.iconClass as React.ComponentType);
|
||||
}
|
||||
};
|
||||
|
||||
const getActionName = (actionTypeId?: string) => {
|
||||
const actionConnector = actionConnectors.find((connector) => connector.id === actionTypeId);
|
||||
return actionConnector?.name;
|
||||
};
|
||||
|
||||
if (isLoadingActionConnectors) return <EuiLoadingSpinner size="s" />;
|
||||
return (
|
||||
<EuiFlexGroup direction="column" gutterSize="none">
|
||||
{actionConnectors.map(({ actionTypeId, name }) => (
|
||||
<EuiFlexItem key={actionTypeId}>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s" component="span">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon size="m" type={getActionIconClass(actionTypeId) ?? 'apps'} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText data-test-subj={`actionConnectorName-${name}`} size="s">
|
||||
{name}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
{ruleActions.map(({ actionTypeId, id }, index) => {
|
||||
const actionName = getActionName(id);
|
||||
return (
|
||||
<EuiFlexItem key={index}>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s" component="span">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon size="m" type={getActionIconClass(actionTypeId) ?? 'apps'} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiText
|
||||
data-test-subj={`actionConnectorName-${index}-${actionName || actionTypeId}`}
|
||||
size="s"
|
||||
>
|
||||
{actionName}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
</EuiFlexItem>
|
||||
);
|
||||
})}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -164,6 +164,21 @@ export const RuleDefinition: React.FunctionComponent<RuleDefinitionProps> = ({
|
|||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiFlexGroup>
|
||||
<ItemTitleRuleSummary>
|
||||
{i18n.translate('xpack.triggersActionsUI.ruleDetails.runsEvery', {
|
||||
defaultMessage: 'Runs every',
|
||||
})}
|
||||
</ItemTitleRuleSummary>
|
||||
|
||||
<ItemValueRuleSummary
|
||||
data-test-subj="ruleSummaryRuleInterval"
|
||||
itemValue={formatDuration(rule.schedule.interval)}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<ItemTitleRuleSummary>
|
||||
{i18n.translate('xpack.triggersActionsUI.ruleDetails.conditionsTitle', {
|
||||
|
@ -188,20 +203,6 @@ export const RuleDefinition: React.FunctionComponent<RuleDefinitionProps> = ({
|
|||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup>
|
||||
<ItemTitleRuleSummary>
|
||||
{i18n.translate('xpack.triggersActionsUI.ruleDetails.runsEvery', {
|
||||
defaultMessage: 'Runs every',
|
||||
})}
|
||||
</ItemTitleRuleSummary>
|
||||
|
||||
<ItemValueRuleSummary
|
||||
data-test-subj="ruleSummaryRuleInterval"
|
||||
itemValue={formatDuration(rule.schedule.interval)}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
||||
|
@ -213,8 +214,8 @@ export const RuleDefinition: React.FunctionComponent<RuleDefinitionProps> = ({
|
|||
</ItemTitleRuleSummary>
|
||||
<ItemValueRuleSummary itemValue={String(getNotifyText())} />
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup alignItems="baseline">
|
||||
<ItemTitleRuleSummary>
|
||||
{i18n.translate('xpack.triggersActionsUI.ruleDetails.actions', {
|
||||
|
|
|
@ -12,7 +12,7 @@ import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers';
|
|||
import { act } from '@testing-library/react';
|
||||
import { RuleDetails } from './rule_details';
|
||||
import { Rule, ActionType, RuleTypeModel, RuleType } from '../../../../types';
|
||||
import { EuiBadge, EuiFlexItem, EuiButtonEmpty, EuiPageHeaderProps } from '@elastic/eui';
|
||||
import { EuiBadge, EuiButtonEmpty, EuiPageHeaderProps } from '@elastic/eui';
|
||||
import {
|
||||
ActionGroup,
|
||||
RuleExecutionStatusErrorReasons,
|
||||
|
@ -211,19 +211,17 @@ describe('rule_details', () => {
|
|||
},
|
||||
];
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<RuleDetails
|
||||
rule={rule}
|
||||
ruleType={ruleType}
|
||||
actionTypes={actionTypes}
|
||||
{...mockRuleApis}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
mountWithIntl(
|
||||
<RuleDetails
|
||||
rule={rule}
|
||||
ruleType={ruleType}
|
||||
actionTypes={actionTypes}
|
||||
{...mockRuleApis}
|
||||
/>
|
||||
).containsMatchingElement(
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge color="hollow">{actionTypes[0].name}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
)
|
||||
wrapper.find('[data-test-subj="actionConnectorName-0-Server log"]').exists
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@ -275,19 +273,10 @@ describe('rule_details', () => {
|
|||
);
|
||||
|
||||
expect(
|
||||
details.containsMatchingElement(
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge color="hollow">{actionTypes[0].name}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
)
|
||||
details.find('[data-test-subj="actionConnectorName-0-Server log"]').exists
|
||||
).toBeTruthy();
|
||||
|
||||
expect(
|
||||
details.containsMatchingElement(
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge color="hollow">{actionTypes[1].name}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
)
|
||||
details.find('[data-test-subj="actionConnectorName-0-Send email"]').exists
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -579,16 +568,12 @@ describe('rule_details', () => {
|
|||
await nextTick();
|
||||
wrapper.update();
|
||||
});
|
||||
const brokenConnectorIndicator = wrapper
|
||||
.find('[data-test-subj="actionWithBrokenConnector"]')
|
||||
.first();
|
||||
const brokenConnectorWarningBanner = wrapper
|
||||
.find('[data-test-subj="actionWithBrokenConnectorWarningBanner"]')
|
||||
.first();
|
||||
const brokenConnectorWarningBannerAction = wrapper
|
||||
.find('[data-test-subj="actionWithBrokenConnectorWarningBannerEdit"]')
|
||||
.first();
|
||||
expect(brokenConnectorIndicator.exists()).toBeTruthy();
|
||||
expect(brokenConnectorWarningBanner.exists()).toBeTruthy();
|
||||
expect(brokenConnectorWarningBannerAction.exists()).toBeTruthy();
|
||||
});
|
||||
|
@ -627,16 +612,12 @@ describe('rule_details', () => {
|
|||
await nextTick();
|
||||
wrapper.update();
|
||||
});
|
||||
const brokenConnectorIndicator = wrapper
|
||||
.find('[data-test-subj="actionWithBrokenConnector"]')
|
||||
.first();
|
||||
const brokenConnectorWarningBanner = wrapper
|
||||
.find('[data-test-subj="actionWithBrokenConnectorWarningBanner"]')
|
||||
.first();
|
||||
const brokenConnectorWarningBannerAction = wrapper
|
||||
.find('[data-test-subj="actionWithBrokenConnectorWarningBannerEdit"]')
|
||||
.first();
|
||||
expect(brokenConnectorIndicator.exists()).toBeTruthy();
|
||||
expect(brokenConnectorWarningBanner.exists()).toBeTruthy();
|
||||
expect(brokenConnectorWarningBannerAction.exists()).toBeFalsy();
|
||||
});
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useState, useEffect, useReducer } from 'react';
|
||||
import { keyBy } from 'lodash';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
EuiPageHeader,
|
||||
|
@ -20,7 +19,6 @@ import {
|
|||
EuiSpacer,
|
||||
EuiButtonEmpty,
|
||||
EuiButton,
|
||||
EuiIconTip,
|
||||
EuiIcon,
|
||||
EuiLink,
|
||||
} from '@elastic/eui';
|
||||
|
@ -150,7 +148,6 @@ export const RuleDetails: React.FunctionComponent<RuleDetailsProps> = ({
|
|||
// if the rule has actions, can the user save the rule's action params
|
||||
(canExecuteActions || (!canExecuteActions && rule.actions.length === 0));
|
||||
|
||||
const actionTypesByTypeId = keyBy(actionTypes, 'id');
|
||||
const hasEditButton =
|
||||
// can the user save the rule
|
||||
canSaveRule &&
|
||||
|
@ -159,8 +156,6 @@ export const RuleDetails: React.FunctionComponent<RuleDetailsProps> = ({
|
|||
? !ruleTypeRegistry.get(rule.ruleTypeId).requiresAppContext
|
||||
: false);
|
||||
|
||||
const ruleActions = rule.actions;
|
||||
const uniqueActions = Array.from(new Set(ruleActions.map((item: any) => item.actionTypeId)));
|
||||
const [editFlyoutVisible, setEditFlyoutVisibility] = useState<boolean>(false);
|
||||
const onRunRule = async (id: string) => {
|
||||
await runRule(http, toasts, id);
|
||||
|
@ -352,46 +347,6 @@ export const RuleDetails: React.FunctionComponent<RuleDetailsProps> = ({
|
|||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItem grow={false}>
|
||||
{uniqueActions && uniqueActions.length ? (
|
||||
<EuiFlexGroup responsive={false} gutterSize="xs">
|
||||
<EuiFlexItem>
|
||||
<EuiText size="s">
|
||||
<FormattedMessage
|
||||
id="xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.actionsTex"
|
||||
defaultMessage="Actions"
|
||||
/>{' '}
|
||||
{hasActionsWithBrokenConnector && (
|
||||
<EuiIconTip
|
||||
data-test-subj="actionWithBrokenConnector"
|
||||
type="alert"
|
||||
color="danger"
|
||||
content={i18n.translate(
|
||||
'xpack.triggersActionsUI.sections.rulesList.rulesListTable.columns.actionsWarningTooltip',
|
||||
{
|
||||
defaultMessage:
|
||||
'Unable to load one of the connectors associated with this rule. Edit the rule to select a new connector.',
|
||||
}
|
||||
)}
|
||||
position="right"
|
||||
/>
|
||||
)}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
{uniqueActions.map((action, index) => (
|
||||
<EuiFlexItem key={index} grow={false}>
|
||||
<EuiBadge color="hollow" data-test-subj="actionTypeLabel">
|
||||
{actionTypesByTypeId[action].name ?? action}
|
||||
</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : null}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
rightSideItems={[
|
||||
|
|
|
@ -168,9 +168,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
|
||||
const owner = await pageObjects.ruleDetailsUI.getAPIKeyOwner();
|
||||
expect(owner).to.be('elastic');
|
||||
|
||||
const { connectorType } = await pageObjects.ruleDetailsUI.getActionsLabels();
|
||||
expect(connectorType).to.be(`Slack`);
|
||||
});
|
||||
|
||||
it('renders toast when schedule is less than configured minimum', async () => {
|
||||
|
|
|
@ -24,11 +24,6 @@ export function RuleDetailsPageProvider({ getService }: FtrProviderContext) {
|
|||
async getAPIKeyOwner() {
|
||||
return await testSubjects.getVisibleText('apiKeyOwnerLabel');
|
||||
},
|
||||
async getActionsLabels() {
|
||||
return {
|
||||
connectorType: await testSubjects.getVisibleText('actionTypeLabel'),
|
||||
};
|
||||
},
|
||||
async getAlertsList() {
|
||||
const table = await find.byCssSelector(
|
||||
'.euiBasicTable[data-test-subj="alertsList"]:not(.euiBasicTable-loading)'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue