Skip to content

Commit

Permalink
[Synthetics] Step details page screenshot (#143452)
Browse files Browse the repository at this point in the history
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
shahzad31 and kibanamachine committed Nov 1, 2022
1 parent 74829e5 commit 80310d8
Show file tree
Hide file tree
Showing 30 changed files with 632 additions and 60 deletions.
3 changes: 2 additions & 1 deletion x-pack/plugins/synthetics/common/runtime_types/ping/ping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,12 @@ export const MonitorType = t.intersection([
id: t.string,
status: t.string,
type: t.string,
check_group: t.string,
}),
t.partial({
duration: t.type({
us: t.number,
}),
check_group: t.string,
ip: t.string,
name: t.string,
timespan: t.type({
Expand Down Expand Up @@ -268,6 +268,7 @@ export const makePing = (f: {
status: f.status || 'up',
duration: { us: f.duration || 100000 },
name: f.name,
check_group: 'myCheckGroup',
},
...(f.location ? { observer: { geo: { name: f.location } } } : {}),
...(f.url ? { url: { full: f.url } } : {}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export function syntheticsAppPageProvider({ page, kibanaUrl }: { page: Page; kib

async navigateToEditMonitor() {
await this.clickByTestSubj('syntheticsMonitorListActions');
await page.click('text=Edit');
await page.click('text=Edit', { timeout: 2 * 60 * 1000 });
await this.findByText('Edit monitor');
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export const BrowserStepsList = ({ steps, error, loading, showStepNumber = false
aria-label={VIEW_DETAILS}
title={VIEW_DETAILS}
size="s"
href={`${basePath}/app/uptime/journey/${item.monitor.check_group}/step/${item.synthetics?.step?.index}`}
href={`${basePath}/app/synthetics/journey/${item.monitor.check_group}/step/${item.synthetics?.step?.index}`}
target="_self"
iconType="apmTrace"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const THUMBNAIL_HEIGHT = 64;

export const thumbnailStyle = css`
padding: 0;
margin: 0;
margin: auto;
width: ${THUMBNAIL_WIDTH}px;
height: ${THUMBNAIL_HEIGHT}px;
object-fit: contain;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ import React from 'react';
import { css } from '@emotion/react';
import { EuiImage, EuiPopover, useEuiTheme } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useRouteMatch } from 'react-router-dom';
import { EmptyImage } from '../screenshot/empty_image';
import { ScreenshotRefImageData } from '../../../../../../common/runtime_types';
import { useCompositeImage } from '../../../hooks/use_composite_image';

import { EmptyThumbnail, thumbnailStyle } from './empty_thumbnail';
import { STEP_DETAIL_ROUTE } from '../../../../../../common/constants';

const POPOVER_IMG_HEIGHT = 360;
const POPOVER_IMG_WIDTH = 640;
Expand All @@ -22,6 +25,7 @@ interface ScreenshotImageProps {
imageCaption: JSX.Element;
isStepFailed: boolean;
isLoading: boolean;
asThumbnail?: boolean;
}

const ScreenshotThumbnail: React.FC<ScreenshotImageProps & { imageData?: string }> = ({
Expand All @@ -30,6 +34,7 @@ const ScreenshotThumbnail: React.FC<ScreenshotImageProps & { imageData?: string
imageData,
isStepFailed,
isLoading,
asThumbnail = true,
}) => {
return imageData ? (
<EuiImage
Expand All @@ -39,11 +44,13 @@ const ScreenshotThumbnail: React.FC<ScreenshotImageProps & { imageData?: string
data-test-subj="stepScreenshotThumbnail"
hasShadow
url={imageData}
size="s"
size="l"
className="syntheticsStepImage"
/>
) : (
) : asThumbnail ? (
<EmptyThumbnail isLoading={isLoading} />
) : (
<EmptyImage isLoading={isLoading} />
);
};
/**
Expand All @@ -64,6 +71,7 @@ const RecomposedScreenshotImage: React.FC<
setImageData,
isStepFailed,
isLoading,
asThumbnail,
}) => {
// initially an undefined URL value is passed to the image display, and a loading spinner is rendered.
// `useCompositeImage` will call `setImageData` when the image is composited, and the updated `imageData` will display.
Expand All @@ -76,6 +84,7 @@ const RecomposedScreenshotImage: React.FC<
imageData={imageData}
isStepFailed={isStepFailed}
isLoading={isLoading}
asThumbnail={asThumbnail}
/>
);
};
Expand All @@ -88,6 +97,7 @@ export interface StepImagePopoverProps {
isImagePopoverOpen: boolean;
isStepFailed: boolean;
isLoading: boolean;
asThumbnail?: boolean;
}

const JourneyStepImage: React.FC<
Expand All @@ -104,6 +114,7 @@ const JourneyStepImage: React.FC<
setImageData,
isStepFailed,
isLoading,
asThumbnail = true,
}) => {
if (imgSrc) {
return (
Expand All @@ -113,6 +124,7 @@ const JourneyStepImage: React.FC<
imageData={imageData}
isStepFailed={isStepFailed}
isLoading={isLoading}
asThumbnail={asThumbnail}
/>
);
} else if (imgRef) {
Expand All @@ -125,6 +137,7 @@ const JourneyStepImage: React.FC<
setImageData={setImageData}
isStepFailed={isStepFailed}
isLoading={isLoading}
asThumbnail={asThumbnail}
/>
);
}
Expand All @@ -139,6 +152,7 @@ export const JourneyStepImagePopover: React.FC<StepImagePopoverProps> = ({
isImagePopoverOpen,
isStepFailed,
isLoading,
asThumbnail = true,
}) => {
const { euiTheme } = useEuiTheme();

Expand All @@ -158,12 +172,16 @@ export const JourneyStepImagePopover: React.FC<StepImagePopoverProps> = ({

const isImageLoading = isLoading || (!!imgRef && !imageData);

const isStepDetailPage = useRouteMatch(STEP_DETAIL_ROUTE)?.isExact;

const thumbnailS = isStepDetailPage ? null : thumbnailStyle;

return (
<EuiPopover
css={css`
figure {
img {
${thumbnailStyle};
${thumbnailS};
border: ${euiTheme.border.thin};
${isStepFailed ? `border-color: ${euiTheme.colors.danger}` : ``};
}
Expand All @@ -180,6 +198,7 @@ export const JourneyStepImagePopover: React.FC<StepImagePopoverProps> = ({
imageData={imageData}
isStepFailed={isStepFailed}
isLoading={isImageLoading}
asThumbnail={asThumbnail}
/>
}
isOpen={isImagePopoverOpen}
Expand All @@ -195,12 +214,10 @@ export const JourneyStepImagePopover: React.FC<StepImagePopoverProps> = ({
object-fit: contain;
`}
/>
) : asThumbnail ? (
<EmptyThumbnail isLoading={isLoading} />
) : (
<EmptyThumbnail
isLoading={isLoading}
width={POPOVER_IMG_WIDTH}
height={POPOVER_IMG_HEIGHT}
/>
<EmptyImage isLoading={isLoading} />
)}
</EuiPopover>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import React, { CSSProperties } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiText, useEuiTheme } from '@elastic/eui';
import { JourneyStep } from '../../../../../../common/runtime_types';
import { JourneyStepScreenshotContainer } from './journey_step_screenshot_container';
import { JourneyStepScreenshotContainer } from '../screenshot/journey_step_screenshot_container';
import { getTextColorForMonitorStatus, parseBadgeStatus } from './status_badge';

interface Props {
Expand Down
Original file line number Diff line number Diff line change
@@ -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 React from 'react';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import {
useEuiTheme,
useEuiBackgroundColor,
EuiIcon,
EuiLoadingContent,
EuiText,
} from '@elastic/eui';

export const IMAGE_WIDTH = 360;
export const IMAGE_HEIGHT = 203;

export const imageStyle = css`
padding: 0;
margin: auto;
width: ${IMAGE_WIDTH}px;
height: ${IMAGE_HEIGHT}px;
object-fit: contain;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
`;

export const EmptyImage = ({
isLoading = false,
width = IMAGE_WIDTH,
height = IMAGE_HEIGHT,
}: {
isLoading: boolean;
width?: number;
height?: number;
}) => {
const { euiTheme } = useEuiTheme();

return (
<div
data-test-subj="stepScreenshotPlaceholder"
role="img"
aria-label={isLoading ? SCREENSHOT_LOADING_ARIA_LABEL : SCREENSHOT_NOT_AVAILABLE}
title={isLoading ? SCREENSHOT_LOADING_ARIA_LABEL : SCREENSHOT_NOT_AVAILABLE}
css={css`
${imageStyle};
width: ${width}px;
height: ${height}px;
background: ${useEuiBackgroundColor('subdued')};
border: ${euiTheme.border.thin};
`}
>
{isLoading ? (
<EuiLoadingContent
lines={1}
data-test-subj="stepScreenshotPlaceholderLoading"
css={css`
width: 100%;
height: 8%;
transform: scale(1, 13); // To create a skeleton loading effect
`}
/>
) : (
<div>
<EuiIcon
data-test-subj="stepScreenshotNotAvailable"
type="eyeClosed"
color={euiTheme.colors.disabledText}
/>
<EuiText color={euiTheme.colors.disabledText}>{IMAGE_UN_AVAILABLE}</EuiText>
</div>
)}
</div>
);
};

export const SCREENSHOT_LOADING_ARIA_LABEL = i18n.translate(
'xpack.synthetics.monitor.step.screenshot.ariaLabel',
{
defaultMessage: 'Step screenshot is being loaded.',
}
);

export const SCREENSHOT_NOT_AVAILABLE = i18n.translate(
'xpack.synthetics.monitor.step.screenshot.notAvailable',
{
defaultMessage: 'Step screenshot is not available.',
}
);

export const IMAGE_UN_AVAILABLE = i18n.translate(
'xpack.synthetics.monitor.step.screenshot.unAvailable',
{
defaultMessage: 'Image unavailable',
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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, { useMemo } from 'react';
import { useJourneySteps } from '../../monitor_details/hooks/use_journey_steps';
import { parseBadgeStatus } from '../monitor_test_result/status_badge';
import { JourneyStepScreenshotContainer } from './journey_step_screenshot_container';

export const JourneyScreenshot = ({ checkGroupId }: { checkGroupId: string }) => {
const { loading: stepsLoading, stepEnds } = useJourneySteps(checkGroupId);
const stepLabels = stepEnds.map((stepEnd) => stepEnd?.synthetics?.step?.name ?? '');

const lastSignificantStep = useMemo(() => {
const copy = [...stepEnds];
// Sort desc by timestamp
copy.sort(
(stepA, stepB) =>
Number(new Date(stepB['@timestamp'])) - Number(new Date(stepA['@timestamp']))
);
return copy.find(
(stepEnd) => parseBadgeStatus(stepEnd?.synthetics?.step?.status ?? 'skipped') !== 'skipped'
);
}, [stepEnds]);

return (
<JourneyStepScreenshotContainer
checkGroup={lastSignificantStep?.monitor.check_group}
initialStepNo={lastSignificantStep?.synthetics?.step?.index}
stepStatus={lastSignificantStep?.synthetics.payload?.status}
allStepsLoaded={!stepsLoading}
stepLabels={stepLabels}
retryFetchOnRevisit={false}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import * as observabilityPublic from '@kbn/observability-plugin/public';
import { getShortTimeStamp } from '../../../utils/monitor_test_result/timestamp';
import '../../../utils/testing/__mocks__/use_composite_image.mock';
import { mockRef } from '../../../utils/testing/__mocks__/screenshot_ref.mock';
import * as retrieveHooks from './use_retrieve_step_image';
import * as retrieveHooks from '../monitor_test_result/use_retrieve_step_image';

jest.mock('@kbn/observability-plugin/public');

Expand Down
Loading

0 comments on commit 80310d8

Please sign in to comment.