Skip to content

Commit

Permalink
[Synthetics] Failed tests by step panel (#149322)
Browse files Browse the repository at this point in the history
## Summary

Failed tests by step panel

Fixes #145368

<img width="1523" alt="image"
src="https://user-images.githubusercontent.com/3505601/214053308-458c023e-aaab-4db6-8aa4-a18ce150ce19.png">
  • Loading branch information
shahzad31 authored Jan 24, 2023
1 parent cdc3737 commit ded5f9a
Show file tree
Hide file tree
Showing 15 changed files with 181 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { journey, step, before, after } from '@elastic/synthetics';
import moment from 'moment';
import { recordVideo } from '../record_video';
import { createExploratoryViewUrl } from '../../public/components/shared/exploratory_view/configurations/exploratory_view_url';
import { loginToKibana, TIMEOUT_60_SEC, waitForLoadingToFinish } from '../utils';
Expand All @@ -28,8 +29,8 @@ journey('Exploratory view', async ({ page, params }) => {
{
dataType: 'synthetics',
time: {
from: 'now-10y',
to: 'now',
from: moment().subtract(10, 'y').toISOString(),
to: moment().toISOString(),
},
name: 'synthetics-series-1',
breakdown: 'monitor.type',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ interface Props {
lensAttributes: TypedLensByValueInput['attributes'];
setChartTimeRangeContext: Dispatch<SetStateAction<ChartTimeRange | undefined>>;
}
const EXECUTION_CONTEXT = {
type: 'observability_exploratory_view',
};

export function LensEmbeddable(props: Props) {
const { lensAttributes, setChartTimeRangeContext } = props;
Expand Down Expand Up @@ -92,9 +95,7 @@ export function LensEmbeddable(props: Props) {
attributes={lensAttributes}
onLoad={onLensLoad}
onBrushEnd={onBrushEnd}
executionContext={{
type: 'observability_exploratory_view',
}}
executionContext={EXECUTION_CONTEXT}
/>
{isSaveOpen && lensAttributes && (
<LensSaveModalComponent
Expand Down
6 changes: 4 additions & 2 deletions x-pack/plugins/observability/public/hooks/use_es_search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ export const useEsSearch = <DocumentSource extends unknown, TParams extends esty
{
params,
},
{}
{
legacyHitsTotal: false,
}
)
.subscribe({
next: (result) => {
Expand Down Expand Up @@ -106,7 +108,7 @@ export const useEsSearch = <DocumentSource extends unknown, TParams extends esty
const { rawResponse } = response as any;

return {
data: rawResponse as ESSearchResponse<DocumentSource, TParams>,
data: rawResponse as ESSearchResponse<DocumentSource, TParams, { restTotalHitsAsInt: false }>,
loading: Boolean(loading),
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,9 @@ export function syntheticsAppPageProvider({ page, kibanaUrl }: { page: Page; kib
},

async navigateToAddMonitor() {
if (await page.isVisible('text=select a different monitor type', { timeout: 0 })) {
await page.click('text=select a different monitor type');
} else if (await page.isVisible('text=Create monitor', { timeout: 0 })) {
await page.click('text=Create monitor');
} else {
await page.goto(addMonitor, {
waitUntil: 'networkidle',
});
}
await page.goto(addMonitor, {
waitUntil: 'networkidle',
});
},

async ensureIsOnMonitorConfigPage() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { useEsSearch } from '@kbn/observability-plugin/public';
import { useParams } from 'react-router-dom';
import { useMemo } from 'react';
import { createEsQuery } from '../../../../../../common/utils/es_search';
import { Ping } from '../../../../../../common/runtime_types';
import { STEP_END_FILTER } from '../../../../../../common/constants/data_filters';
import { SYNTHETICS_INDEX_PATTERN } from '../../../../../../common/constants';
import { useSyntheticsRefreshContext } from '../../../contexts';

export function useFailedTestByStep({ to, from }: { to: string; from: string }) {
const { lastRefresh } = useSyntheticsRefreshContext();

const { monitorId } = useParams<{ monitorId: string }>();

const params = createEsQuery({
index: SYNTHETICS_INDEX_PATTERN,
body: {
size: 0,
track_total_hits: true,
query: {
bool: {
filter: [
{
range: {
'@timestamp': {
lte: to,
gte: from,
},
},
},
STEP_END_FILTER,
{
term: {
'synthetics.step.status': 'failed',
},
},
{
term: {
config_id: monitorId,
},
},
],
},
},
aggs: {
steps: {
terms: {
field: 'synthetics.step.name.keyword',
size: 1000,
},
aggs: {
doc: {
top_hits: {
size: 1,
},
},
},
},
},
},
});

const { data, loading } = useEsSearch<Ping, typeof params>(params, [lastRefresh, monitorId], {
name: `getFailedTestsByStep/${monitorId}`,
});

return useMemo(() => {
const total = data?.hits.total.value;

const failedSteps = data?.aggregations?.steps.buckets.map(({ key, doc_count: count, doc }) => {
const index = doc.hits.hits?.[0]._source?.synthetics?.step?.index;
return {
index,
count,
name: key,
percent: (count / total) * 100,
};
});

return {
failedSteps,
loading,
};
}, [data, loading]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import { useHistory, useParams } from 'react-router-dom';
import moment from 'moment';
import { ErrorDetailsLink } from '../../common/links/error_details_link';
import { useSelectedLocation } from '../hooks/use_selected_location';
import { useKibanaDateFormat } from '../../../../../hooks/use_kibana_date_format';
import { Ping, PingState } from '../../../../../../common/runtime_types';
import { useErrorFailedStep } from '../hooks/use_error_failed_step';
import {
formatTestDuration,
formatTestRunAt,
useDateFormatForTest,
} from '../../../utils/monitor_test_result/test_time_formats';

export const ErrorsList = ({
Expand All @@ -46,7 +46,7 @@ export const ErrorsList = ({

const history = useHistory();

const format = useKibanaDateFormat();
const format = useDateFormatForTest();

const selectedLocation = useSelectedLocation();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import React from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FailedTestsByStep } from './failed_tests_by_step';
import { PingState } from '../../../../../../common/runtime_types';
import { PanelWithTitle } from '../../common/components/panel_with_title';
import { MonitorErrorsCount } from '../monitor_summary/monitor_errors_count';
Expand Down Expand Up @@ -61,7 +62,9 @@ export const ErrorsTabContent = ({
</PanelWithTitle>
</EuiFlexItem>
<EuiFlexItem grow={1}>
<PanelWithTitle title={FAILED_TESTS_BY_STEPS_LABEL} />
<PanelWithTitle title={FAILED_TESTS_BY_STEPS_LABEL}>
<FailedTestsByStep time={time} />
</PanelWithTitle>
</EuiFlexItem>
</EuiFlexGroup>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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 React, { Fragment } from 'react';
import { EuiProgress, EuiSpacer, EuiLoadingContent } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useFailedTestByStep } from '../hooks/use_failed_tests_by_step';

export const FailedTestsByStep = ({ time }: { time: { to: string; from: string } }) => {
const { failedSteps, loading } = useFailedTestByStep(time);

if (loading && !failedSteps) {
return <EuiLoadingContent lines={3} />;
}

return (
<>
<EuiSpacer size="m" />
<div>
{failedSteps?.map((item) => (
<Fragment key={item.name}>
<EuiProgress
valueText={
<span>
{i18n.translate('xpack.synthetics.monitorDetails.summary.failedTests.count', {
defaultMessage: 'Failed {count}',
values: { count: item.count },
})}
</span>
}
max={100}
color="danger"
size="l"
value={item.percent}
label={`${item.index}. ${item.name}`}
/>
<EuiSpacer size="s" />
</Fragment>
))}
</div>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,20 @@ export function formatTestRunAt(timestamp: string, format: string) {
return stampedMoment.format(format);
}

export function useFormatTestRunAt(timestamp?: string) {
export function useDateFormatForTest() {
let format = useKibanaDateFormat();
if (!timestamp) {
return '';
}

if (format.endsWith('.SSS')) {
format = format.replace('.SSS', '');
}
return format;
}

export function useFormatTestRunAt(timestamp?: string) {
const format = useDateFormatForTest();

if (!timestamp) {
return '';
}
return formatTestRunAt(timestamp, format);
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe('BrowserTestRunResult', function () {
size: 1000,
},
},
{}
{ legacyHitsTotal: false }
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('SimpleTestResults', function () {
size: 1000,
},
},
{}
{ legacyHitsTotal: false }
);
});

Expand Down
7 changes: 5 additions & 2 deletions x-pack/plugins/ux/e2e/journeys/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
import { expect, Page } from '@elastic/synthetics';

export async function waitForLoadingToFinish({ page }: { page: Page }) {
while (true) {
let retries = 50;
while (retries) {
retries--;
if ((await page.$(byTestId('kbnLoadingMessage'))) === null) break;
await page.waitForTimeout(2 * 1000);
}
while (true) {
retries = 50;
while (retries) {
if ((await page.$(byTestId('globalLoadingIndicator'))) === null) break;
await page.waitForTimeout(2 * 1000);
}
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/ux/public/hooks/use_client_metrics_query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function useClientMetricsQuery() {
const backendValue = backEnd.values[pkey] ?? 0;

return {
pageViews: { value: (esQueryResponse.hits.total as any as number) ?? 0 },
pageViews: { value: esQueryResponse.hits.total.value ?? 0 },
totalPageLoadDuration: { value: totalPageLoadDurationValueMs },
backEnd: { value: backendValue },
frontEnd: { value: totalPageLoadDurationValueMs - backendValue },
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/ux/public/hooks/use_js_errors_query.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ export function useJsErrorsQuery(pagination: {

return {
totalErrorPages: totalErrorPages?.value ?? 0,
totalErrors: esQueryResponse.hits.total ?? 0,
totalErrors: esQueryResponse.hits.total.value ?? 0,
totalErrorGroups: totalErrorGroups?.value ?? 0,
items: errors?.buckets.map(({ sample, key, impactedPages }: any) => {
items: errors?.buckets.map(({ sample, key, impactedPages }) => {
return {
count: impactedPages.pageCount.value,
errorGroupId: key,
Expand Down
3 changes: 1 addition & 2 deletions x-pack/plugins/ux/public/services/data/has_rum_data_query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ export function formatHasRumResult<T>(
if (!esResult) return esResult;
return {
indices,
// @ts-ignore total.value is undefined by the returned type, total is a `number`
hasData: esResult.hits.total > 0,
hasData: esResult.hits.total.value > 0,
serviceName:
esResult.aggregations?.services?.mostTraffic?.buckets?.[0]?.key,
};
Expand Down

0 comments on commit ded5f9a

Please sign in to comment.