[Observability rules] Add baseline alert detail pages (#180256)

Closes https://github.com/elastic/kibana/issues/179338

Added baseline alert details page for:
- Inventory rule
- Metric threshold rule
- Error count threshold rule
- Failed transaction rate threshold rule
- APM Anomaly rule
- Elasticsearch query rule
- Anomaly detection rule

Example of Elasticsearch query alert details page:

<img width="1483" alt="Screenshot 2024-04-08 at 23 35 16"
src="acd1c9ba-9711-4bfe-97e3-71aa56a2c57c">
This commit is contained in:
Bena Kansara 2024-04-15 13:31:34 +02:00 committed by GitHub
parent 3670d5eafc
commit 382be7ea3f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 181 additions and 159 deletions

View file

@ -6,7 +6,14 @@
*/
import React, { memo, useEffect, useMemo, useState } from 'react';
import { EuiInMemoryTable } from '@elastic/eui';
import {
EuiTitle,
EuiSpacer,
EuiText,
EuiLink,
EuiHorizontalRule,
EuiInMemoryTable,
} from '@elastic/eui';
import {
ALERT_CASE_IDS,
ALERT_DURATION,
@ -24,27 +31,38 @@ import { useUiSetting } from '@kbn/kibana-react-plugin/public';
import { i18n } from '@kbn/i18n';
import { getPaddedAlertTimeRange } from '@kbn/observability-get-padded-alert-time-range-util';
import { paths } from '../../../../common/locators/paths';
import { TimeRange } from '../../../../common/custom_threshold_rule/types';
import { TopAlert } from '../../../typings/alerts';
import { useFetchBulkCases } from '../../../hooks/use_fetch_bulk_cases';
import { useCaseViewNavigation } from '../../../hooks/use_case_view_navigation';
import { useKibana } from '../../../utils/kibana_react';
import { get } from 'lodash';
import { paths } from '../../../common/locators/paths';
import { TimeRange } from '../../../common/custom_threshold_rule/types';
import { TopAlert } from '../../typings/alerts';
import { useFetchBulkCases } from '../../hooks/use_fetch_bulk_cases';
import { useCaseViewNavigation } from '../../hooks/use_case_view_navigation';
import { useKibana } from '../../utils/kibana_react';
import {
FlyoutThresholdData,
mapRuleParamsWithFlyout,
} from './helpers/map_rules_params_with_flyout';
import { ColumnIDs, overviewColumns } from './overview_columns';
import { getSources } from './helpers/get_sources';
import { RULE_DETAILS_PAGE_ID } from '../../pages/rule_details/constants';
export const Overview = memo(({ alert }: { alert: TopAlert }) => {
const { http } = useKibana().services;
export const AlertOverview = memo(({ alert, pageId }: { alert: TopAlert; pageId?: string }) => {
const {
http: {
basePath: { prepend },
},
} = useKibana().services;
const { cases, isLoading } = useFetchBulkCases({ ids: alert.fields[ALERT_CASE_IDS] || [] });
const dateFormat = useUiSetting<string>('dateFormat');
const [timeRange, setTimeRange] = useState<TimeRange>({ from: 'now-15m', to: 'now' });
const [ruleCriteria, setRuleCriteria] = useState<FlyoutThresholdData[] | undefined>([]);
const alertStart = alert.fields[ALERT_START];
const alertEnd = alert.fields[ALERT_END];
const ruleId = get(alert.fields, ALERT_RULE_UUID) ?? null;
const linkToRule =
pageId !== RULE_DETAILS_PAGE_ID && ruleId
? prepend(paths.observability.ruleDetails(ruleId))
: null;
useEffect(() => {
const mappedRuleParams = mapRuleParamsWithFlyout(alert);
@ -126,7 +144,7 @@ export const Overview = memo(({ alert }: { alert: TopAlert }) => {
meta: {
ruleLink:
alert.fields[ALERT_RULE_UUID] &&
http.basePath.prepend(paths.observability.ruleDetails(alert.fields[ALERT_RULE_UUID])),
prepend(paths.observability.ruleDetails(alert.fields[ALERT_RULE_UUID])),
},
},
{
@ -154,11 +172,42 @@ export const Overview = memo(({ alert }: { alert: TopAlert }) => {
alertEnd,
cases,
dateFormat,
http.basePath,
prepend,
isLoading,
navigateToCaseView,
ruleCriteria,
timeRange,
]);
return <EuiInMemoryTable width={'80%'} columns={overviewColumns} itemId="key" items={items} />;
return (
<>
<EuiTitle size="xs">
<h4>
{i18n.translate('xpack.observability.alertsFlyout.reasonTitle', {
defaultMessage: 'Reason',
})}
</h4>
</EuiTitle>
<EuiSpacer size="s" />
<EuiText size="s">{alert.reason}</EuiText>
<EuiSpacer size="s" />
{!!linkToRule && (
<EuiLink href={linkToRule} data-test-subj="viewRuleDetailsFlyout">
{i18n.translate('xpack.observability.alertsFlyout.viewRulesDetailsLinkText', {
defaultMessage: 'View rule details',
})}
</EuiLink>
)}
<EuiHorizontalRule size="full" />
<EuiTitle size="xs">
<h4>
{i18n.translate('xpack.observability.alertsFlyout.documentSummaryTitle', {
defaultMessage: 'Document Summary',
})}
</h4>
</EuiTitle>
<EuiSpacer size="m" />
<EuiInMemoryTable width={'80%'} columns={overviewColumns} itemId="key" items={items} />
</>
);
});

View file

@ -9,8 +9,8 @@ import { ALERT_GROUP_FIELD, ALERT_GROUP_VALUE } from '@kbn/rule-data-utils';
import {
apmSources,
infraSources,
} from '../../../../../common/custom_threshold_rule/helpers/get_alert_source_links';
import { TopAlert } from '../../../..';
} from '../../../../common/custom_threshold_rule/helpers/get_alert_source_links';
import { TopAlert } from '../../..';
interface AlertFields {
[key: string]: any;
@ -36,7 +36,7 @@ export const getSources = (alert: TopAlert) => {
const fieldValue = alertFields[field];
matchedSources.push({
field: source,
value: fieldValue[0],
value: Array.isArray(fieldValue) ? fieldValue[0] : fieldValue,
});
}
});

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { TopAlert } from '../../../../typings/alerts';
import { TopAlert } from '../../../typings/alerts';
import { mapRuleParamsWithFlyout } from './map_rules_params_with_flyout';
describe('Map rules params with flyout', () => {
@ -149,7 +149,7 @@ describe('Map rules params with flyout', () => {
observedValue: [4577],
threshold: [100],
comparator: 'more than',
pctAboveThreshold: ' (4477.00% above the threshold)',
pctAboveThreshold: ' (4477% above the threshold)',
},
],
},
@ -179,7 +179,7 @@ describe('Map rules params with flyout', () => {
observedValue: '6%',
threshold: '1%',
comparator: '>',
pctAboveThreshold: ' (500.00% above the threshold)',
pctAboveThreshold: ' (500% above the threshold)',
},
],
},
@ -242,7 +242,7 @@ describe('Map rules params with flyout', () => {
observedValue: [1],
threshold: [1],
comparator: '>',
pctAboveThreshold: ' (0.00% above the threshold)',
pctAboveThreshold: ' (0% above the threshold)',
},
],
},
@ -267,7 +267,7 @@ describe('Map rules params with flyout', () => {
observedValue: ['23 s'],
threshold: ['1.5 s'],
comparator: '>',
pctAboveThreshold: ' (1424.80% above the threshold)',
pctAboveThreshold: ' (1424.8% above the threshold)',
},
],
},
@ -291,7 +291,7 @@ describe('Map rules params with flyout', () => {
observedValue: ['25%'],
threshold: ['1.0%'],
comparator: '>',
pctAboveThreshold: ' (2400.00% above the threshold)',
pctAboveThreshold: ' (2400% above the threshold)',
},
],
},

View file

@ -19,16 +19,16 @@ import {
} from '@kbn/rule-data-utils';
import { EsQueryRuleParams } from '@kbn/stack-alerts-plugin/public/rule_types/es_query/types';
import { i18n } from '@kbn/i18n';
import { asDuration, asPercent } from '../../../../../common';
import { createFormatter } from '../../../../../common/custom_threshold_rule/formatters';
import { metricValueFormatter } from '../../../../../common/custom_threshold_rule/metric_value_formatter';
import { METRIC_FORMATTERS } from '../../../../../common/custom_threshold_rule/formatters/snapshot_metric_formats';
import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../../pages/alert_details/alert_details';
import { asDuration, asPercent } from '../../../../common';
import { createFormatter } from '../../../../common/custom_threshold_rule/formatters';
import { metricValueFormatter } from '../../../../common/custom_threshold_rule/metric_value_formatter';
import { METRIC_FORMATTERS } from '../../../../common/custom_threshold_rule/formatters/snapshot_metric_formats';
import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../pages/alert_details/alert_details';
import {
BaseMetricExpressionParams,
CustomMetricExpressionParams,
} from '../../../../../common/custom_threshold_rule/types';
import { TopAlert } from '../../../../typings/alerts';
} from '../../../../common/custom_threshold_rule/types';
import { TopAlert } from '../../../typings/alerts';
import { isFieldsSameType } from './is_fields_same_type';
export interface FlyoutThresholdData {
observedValue: string;
@ -42,7 +42,7 @@ const getPctAboveThreshold = (observedValue?: number, threshold?: number[]): str
return i18n.translate('xpack.observability.alertFlyout.overview.aboveThresholdLabel', {
defaultMessage: ' ({pctValue}% above the threshold)',
values: {
pctValue: (((observedValue - threshold[0]) * 100) / threshold[0]).toFixed(2),
pctValue: parseFloat((((observedValue - threshold[0]) * 100) / threshold[0]).toFixed(2)),
},
});
};

View file

@ -13,9 +13,9 @@ import { AlertStatus } from '@kbn/rule-data-utils';
import moment from 'moment';
import React from 'react';
import { Tooltip as CaseTooltip } from '@kbn/cases-components';
import type { Group } from '../../../../common/custom_threshold_rule/types';
import { NavigateToCaseView } from '../../../hooks/use_case_view_navigation';
import { Groups } from '../../custom_threshold/components/alert_details_app_section/groups';
import type { Group } from '../../../common/custom_threshold_rule/types';
import { NavigateToCaseView } from '../../hooks/use_case_view_navigation';
import { Groups } from '../custom_threshold/components/alert_details_app_section/groups';
import { formatCase } from './helpers/format_cases';
import { FlyoutThresholdData } from './helpers/map_rules_params_with_flyout';

View file

@ -5,27 +5,12 @@
* 2.0.
*/
import React, { useCallback, useMemo, useState } from 'react';
import { get } from 'lodash';
import {
EuiHorizontalRule,
EuiLink,
EuiPanel,
EuiSpacer,
EuiTabbedContentTab,
EuiText,
EuiTitle,
} from '@elastic/eui';
import { ALERT_RULE_UUID } from '@kbn/rule-data-utils';
import { EuiPanel, EuiTabbedContentTab } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { AlertFieldsTable, ScrollableFlyoutTabbedContent } from '@kbn/alerts-ui-shared';
import { AlertsTableFlyoutBaseProps } from '@kbn/triggers-actions-ui-plugin/public';
import { useKibana } from '../../utils/kibana_react';
import { paths } from '../../../common/locators/paths';
import { RULE_DETAILS_PAGE_ID } from '../../pages/rule_details/constants';
import type { TopAlert } from '../../typings/alerts';
import { Overview } from './alert_flyout_overview/alerts_flyout_overview';
import { AlertOverview } from '../alert_overview/alert_overview';
interface FlyoutProps {
rawAlert: AlertsTableFlyoutBaseProps['alert'];
@ -36,18 +21,6 @@ interface FlyoutProps {
type TabId = 'overview' | 'table';
export function AlertsFlyoutBody({ alert, rawAlert, id: pageId }: FlyoutProps) {
const {
http: {
basePath: { prepend },
},
} = useKibana().services;
const ruleId = get(alert.fields, ALERT_RULE_UUID) ?? null;
const linkToRule =
pageId !== RULE_DETAILS_PAGE_ID && ruleId && prepend
? prepend(paths.observability.ruleDetails(ruleId))
: null;
const overviewTab = useMemo(() => {
return {
id: 'overview',
@ -57,37 +30,11 @@ export function AlertsFlyoutBody({ alert, rawAlert, id: pageId }: FlyoutProps) {
}),
content: (
<EuiPanel hasShadow={false} data-test-subj="overviewTabPanel">
<EuiTitle size="xs">
<h4>
{i18n.translate('xpack.observability.alertsFlyout.reasonTitle', {
defaultMessage: 'Reason',
})}
</h4>
</EuiTitle>
<EuiSpacer size="s" />
<EuiText size="s">{alert.reason}</EuiText>
<EuiSpacer size="s" />
{!!linkToRule && (
<EuiLink href={linkToRule} data-test-subj="viewRuleDetailsFlyout">
{i18n.translate('xpack.observability.alertsFlyout.viewRulesDetailsLinkText', {
defaultMessage: 'View rule details',
})}
</EuiLink>
)}
<EuiHorizontalRule size="full" />
<EuiTitle size="xs">
<h4>
{i18n.translate('xpack.observability.alertsFlyout.documentSummaryTitle', {
defaultMessage: 'Document Summary',
})}
</h4>
</EuiTitle>
<EuiSpacer size="m" />
<Overview alert={alert} />
<AlertOverview alert={alert} pageId={pageId} />
</EuiPanel>
),
};
}, [alert, linkToRule]);
}, [alert, pageId]);
const metadataTab = useMemo(
() => ({

View file

@ -9,8 +9,6 @@ import React, { useState, useEffect } from 'react';
import { EuiFlyoutFooter, EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useKibana } from '../../utils/kibana_react';
import { usePluginContext } from '../../hooks/use_plugin_context';
import { isAlertDetailsEnabledPerApp } from '../../utils/is_alert_details_enabled';
import { paths } from '../../../common/locators/paths';
import type { TopAlert } from '../../typings/alerts';
@ -25,7 +23,6 @@ export function AlertsFlyoutFooter({ alert, isInApp }: FlyoutProps & { isInApp:
basePath: { prepend },
},
} = useKibana().services;
const { config } = usePluginContext();
const [viewInAppUrl, setViewInAppUrl] = useState<string>();
useEffect(() => {
@ -48,23 +45,20 @@ export function AlertsFlyoutFooter({ alert, isInApp }: FlyoutProps & { isInApp:
</EuiButton>
</EuiFlexItem>
)}
{!isAlertDetailsEnabledPerApp(alert, config) ? null : (
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="alertsFlyoutAlertDetailsButton"
fill
href={
prepend &&
prepend(paths.observability.alertDetails(alert.fields['kibana.alert.uuid']))
}
>
{i18n.translate('xpack.observability.alertsFlyout.alertsDetailsButtonText', {
defaultMessage: 'Alert details',
})}
</EuiButton>
</EuiFlexItem>
)}
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="alertsFlyoutAlertDetailsButton"
fill
href={
prepend &&
prepend(paths.observability.alertDetails(alert.fields['kibana.alert.uuid']))
}
>
{i18n.translate('xpack.observability.alertsFlyout.alertsDetailsButtonText', {
defaultMessage: 'Alert details',
})}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutFooter>
);

View file

@ -113,7 +113,7 @@ export const getRenderCellValue = ({
return (
<EuiLink
data-test-subj="o11yGetRenderCellValueLink"
css={{ display: 'contents' }}
css={{ ':hover': { textDecoration: 'none' } }}
onClick={() => setFlyoutAlert && setFlyoutAlert(alert.fields[ALERT_UUID])}
>
{alert.reason}

View file

@ -16,6 +16,7 @@ import { waitFor } from '@testing-library/react';
import { Chance } from 'chance';
import React, { Fragment } from 'react';
import { useLocation, useParams } from 'react-router-dom';
import { from } from 'rxjs';
import { useFetchAlertDetail } from '../../hooks/use_fetch_alert_detail';
import { ConfigSchema } from '../../plugin';
import { Subset } from '../../typings';
@ -54,6 +55,7 @@ const mockKibana = () => {
services: {
...kibanaStartMock.startContract(),
cases: casesPluginMock.createStartContract(),
application: { currentAppId$: from('mockedApp') },
http: {
basePath: {
prepend: jest.fn(),

View file

@ -38,12 +38,12 @@ import { PageTitle, pageTitleContent } from './components/page_title';
import { HeaderActions } from './components/header_actions';
import { AlertSummary, AlertSummaryField } from './components/alert_summary';
import { CenterJustifiedSpinner } from '../../components/center_justified_spinner';
import PageNotFound from '../404';
import { getTimeZone } from '../../utils/get_time_zone';
import { isAlertDetailsEnabledPerApp } from '../../utils/is_alert_details_enabled';
import { observabilityFeatureId } from '../../../common';
import { paths } from '../../../common/locators/paths';
import { HeaderMenu } from '../overview/components/header_menu/header_menu';
import { AlertOverview } from '../../components/alert_overview/alert_overview';
import { AlertDetailContextualInsights } from './alert_details_contextual_insights';
interface AlertDetailsPathParams {
@ -133,11 +133,6 @@ export function AlertDetails() {
return <CenterJustifiedSpinner />;
}
// Redirect to the 404 page when the user hit the page url directly in the browser while the feature flag is off.
if (alertDetail && !isAlertDetailsEnabledPerApp(alertDetail.formatted, config)) {
return <PageNotFound />;
}
if (!isLoading && !alertDetail)
return (
<EuiPanel data-test-subj="alertDetailsError">
@ -167,27 +162,43 @@ export function AlertDetails() {
const OVERVIEW_TAB_ID = 'overview';
const METADATA_TAB_ID = 'metadata';
const overviewTab = (
<>
<EuiSpacer size="l" />
<AlertSummary alertSummaryFields={summaryFields} />
<AlertDetailContextualInsights alert={alertDetail} />
<EuiSpacer size="l" />
{AlertDetailsAppSection && rule && alertDetail?.formatted && (
<AlertDetailsAppSection
alert={alertDetail.formatted}
rule={rule}
timeZone={timeZone}
setAlertSummaryFields={setSummaryFields}
ruleLink={http.basePath.prepend(paths.observability.ruleDetails(rule.id))}
/>
)}
</>
const overviewTab = alertDetail ? (
AlertDetailsAppSection &&
/*
when feature flag is enabled, show alert details page with customized overview tab,
otherwise show default overview tab
*/
isAlertDetailsEnabledPerApp(alertDetail.formatted, config) ? (
<>
<EuiSpacer size="l" />
<AlertSummary alertSummaryFields={summaryFields} />
<AlertDetailContextualInsights alert={alertDetail} />
<EuiSpacer size="l" />
{rule && alertDetail.formatted && (
<AlertDetailsAppSection
alert={alertDetail.formatted}
rule={rule}
timeZone={timeZone}
setAlertSummaryFields={setSummaryFields}
ruleLink={http.basePath.prepend(paths.observability.ruleDetails(rule.id))}
/>
)}
</>
) : (
<EuiPanel hasShadow={false} data-test-subj="overviewTabPanel" paddingSize="none">
<EuiSpacer size="l" />
<AlertDetailContextualInsights alert={alertDetail} />
<EuiSpacer size="l" />
<AlertOverview alert={alertDetail.formatted} />
</EuiPanel>
)
) : (
<></>
);
const metadataTab = alertDetail?.raw && (
<EuiPanel hasShadow={false} data-test-subj="metadataTabPanel">
<EuiPanel hasShadow={false} data-test-subj="metadataTabPanel" paddingSize="none">
<EuiSpacer size="l" />
<AlertFieldsTable alert={alertDetail.raw} />
</EuiPanel>
);

View file

@ -147,8 +147,6 @@ describe('ObservabilityActions component', () => {
wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click');
await waitFor(() => {
expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().length).toBe(0);
wrapper.update();
expect(wrapper.find('[data-test-subj~="viewAlertDetailsFlyout"]').exists()).toBeTruthy();
});
});
@ -157,7 +155,15 @@ describe('ObservabilityActions component', () => {
wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click');
await waitFor(() => {
expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().length).toBe(1);
expect(wrapper.find('[data-test-subj~="viewAlertDetailsFlyout"]').hostNodes().length).toBe(1);
});
});
it('"View alert details" menu item should open alert details page', async () => {
const wrapper = await setup('nothing');
wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click');
await waitFor(() => {
expect(wrapper.find('[data-test-subj~="viewAlertDetailsPage"]').hostNodes().length).toBe(1);
expect(wrapper.find('[data-test-subj~="viewAlertDetailsFlyout"]').exists()).toBeFalsy();
});
});

View file

@ -25,7 +25,6 @@ import { useRouteMatch } from 'react-router-dom';
import { SLO_ALERTS_TABLE_ID } from '@kbn/observability-shared-plugin/common';
import { RULE_DETAILS_PAGE_ID } from '../../rule_details/constants';
import { paths, SLO_DETAIL_PATH } from '../../../../common/locators/paths';
import { isAlertDetailsEnabledPerApp } from '../../../utils/is_alert_details_enabled';
import { useKibana } from '../../../utils/kibana_react';
import { parseAlert } from '../helpers/parse_alert';
import { observabilityFeatureId, ObservabilityRuleTypeRegistry } from '../../..';
@ -147,7 +146,7 @@ export function AlertActions({
triggersActionsUi.getAlertsTableDefaultAlertActions({
key: 'defaultRowActions',
onActionExecuted: closeActionsPopover,
isAlertDetailsEnabled: isAlertDetailsEnabledPerApp(observabilityAlert, config),
isAlertDetailsEnabled: true,
resolveRulePagePath: (ruleId, currentPageId) =>
currentPageId !== RULE_DETAILS_PAGE_ID ? paths.observability.ruleDetails(ruleId) : null,
resolveAlertPagePath: (alertId, currentPageId) =>
@ -156,7 +155,7 @@ export function AlertActions({
: null,
...customActionsProps,
}),
[config, customActionsProps, observabilityAlert, triggersActionsUi]
[customActionsProps, triggersActionsUi]
);
const actionsMenuItems = [

View file

@ -159,9 +159,16 @@ export function ObservabilityAlertsCommonProvider({
};
// Flyout
const getReasonMessageLinkByIndex = async (index: number) => {
const reasonMessageLinks = await find.allByCssSelector(
'[data-test-subj="o11yGetRenderCellValueLink"]'
);
return reasonMessageLinks[index] || null;
};
const openAlertsFlyout = retryOnStale.wrap(async (index: number = 0) => {
await openActionsMenuForRow(index);
await testSubjects.click('viewAlertDetailsFlyout');
const reasonMessageLink = await getReasonMessageLinkByIndex(index);
await reasonMessageLink.click();
await retry.waitFor(
'flyout open',
async () => await testSubjects.exists(ALERTS_FLYOUT_SELECTOR, { timeout: 2500 })

View file

@ -13,7 +13,7 @@ export default ({ getService }: FtrProviderContext) => {
const observability = getService('observability');
const retry = getService('retry');
describe('Observability Alert Details page - Feature flag', function () {
describe('Observability Alert Details page', function () {
this.tags('includeFirefox');
before(async () => {
@ -27,29 +27,36 @@ export default ({ getService }: FtrProviderContext) => {
await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs');
});
it('should show 404 page when the feature flag is disabled but the alert exists', async () => {
await observability.alerts.common.navigateToAlertDetails(
'4c87bd11-ff31-4a05-8a04-833e2da94858'
);
it('should show error when the alert does not exist', async () => {
await observability.alerts.common.navigateToAlertDetails('deleted-alert-Id');
await retry.waitFor(
'The 404 - Not found page to be visible',
async () => await testSubjects.exists('pageNotFound')
'Error message to be visible',
async () => await testSubjects.exists('alertDetailsError')
);
});
// This test is will be removed after removing the feature flag.
// FLAKY for the same reason: https://github.com/elastic/kibana/issues/133799
describe.skip('Alert Detail / Alert Flyout', () => {
describe('Alert components', () => {
before(async () => {
await observability.alerts.common.navigateToTimeWithData();
});
it('should open the flyout instead of the alerts details page when clicking on "View alert details" from the... (3 dots) button when the feature flag is disabled', async () => {
await observability.alerts.common.openAlertsFlyout();
await observability.alerts.common.getAlertsFlyoutOrFail();
it('should show tabbed view', async () => {
await observability.alerts.common.navigateToAlertDetails(
'4c87bd11-ff31-4a05-8a04-833e2da94858'
);
await retry.waitFor(
'Overview tab to be visible',
async () => await testSubjects.exists('overviewTab')
);
await retry.waitFor(
'Metadata tab to be visible',
async () => await testSubjects.exists('metadataTab')
);
});
/* TODO: Add more test cases regarding the feature flag for:
- alert details URL from the Action variable
- alert details button from the alert flyout.
*/
/* TODO: Add more test cases */
});
});
};