mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Uptime][Monitor Management] Use push flyout to show Test Run Results (#125017) (uptime/issues/445)
* Make run-once test results appear in a push flyout. Fixup tooltip. Fixup action buttons order. * Wrapping Monitor Fields form rows when Test Run flyout is open. * Only show step duration trend if it's an already saved monitor. Stop showing "Failed to run steps" until Test Run steps are done loading. uptime/issues/445 Co-authored-by: shahzad31 <shahzad.muhammad@elastic.co>
This commit is contained in:
parent
c2a010367d
commit
dde4d6e9da
27 changed files with 361 additions and 146 deletions
|
@ -13,10 +13,10 @@ import {
|
|||
EuiFieldText,
|
||||
EuiCheckbox,
|
||||
EuiFormRow,
|
||||
EuiDescribedFormGroup,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { ComboBox } from '../combo_box';
|
||||
import { DescribedFormGroupWithWrap } from '../common/described_form_group_with_wrap';
|
||||
|
||||
import { useBrowserAdvancedFieldsContext, useBrowserSimpleFieldsContext } from '../contexts';
|
||||
|
||||
|
@ -28,9 +28,10 @@ import { ThrottlingFields } from './throttling_fields';
|
|||
interface Props {
|
||||
validate: Validation;
|
||||
children?: React.ReactNode;
|
||||
minColumnWidth?: string;
|
||||
}
|
||||
|
||||
export const BrowserAdvancedFields = memo<Props>(({ validate, children }) => {
|
||||
export const BrowserAdvancedFields = memo<Props>(({ validate, children, minColumnWidth }) => {
|
||||
const { fields, setFields } = useBrowserAdvancedFieldsContext();
|
||||
const { fields: simpleFields } = useBrowserSimpleFieldsContext();
|
||||
|
||||
|
@ -49,7 +50,8 @@ export const BrowserAdvancedFields = memo<Props>(({ validate, children }) => {
|
|||
>
|
||||
<EuiSpacer size="m" />
|
||||
{simpleFields[ConfigKey.SOURCE_ZIP_URL] && (
|
||||
<EuiDescribedFormGroup
|
||||
<DescribedFormGroupWithWrap
|
||||
minColumnWidth={minColumnWidth}
|
||||
title={
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
|
@ -115,9 +117,10 @@ export const BrowserAdvancedFields = memo<Props>(({ validate, children }) => {
|
|||
data-test-subj="syntheticsBrowserJourneyFiltersTags"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</DescribedFormGroupWithWrap>
|
||||
)}
|
||||
<EuiDescribedFormGroup
|
||||
<DescribedFormGroupWithWrap
|
||||
minColumnWidth={minColumnWidth}
|
||||
title={
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
|
@ -211,9 +214,9 @@ export const BrowserAdvancedFields = memo<Props>(({ validate, children }) => {
|
|||
data-test-subj="syntheticsBrowserSyntheticsArgs"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</DescribedFormGroupWithWrap>
|
||||
|
||||
<ThrottlingFields validate={validate} />
|
||||
<ThrottlingFields validate={validate} minColumnWidth={minColumnWidth} />
|
||||
{children}
|
||||
</EuiAccordion>
|
||||
);
|
||||
|
|
|
@ -7,14 +7,8 @@
|
|||
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import {
|
||||
EuiDescribedFormGroup,
|
||||
EuiSwitch,
|
||||
EuiSpacer,
|
||||
EuiFormRow,
|
||||
EuiFieldNumber,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { EuiSwitch, EuiSpacer, EuiFormRow, EuiFieldNumber, EuiText } from '@elastic/eui';
|
||||
import { DescribedFormGroupWithWrap } from '../common/described_form_group_with_wrap';
|
||||
|
||||
import { OptionalLabel } from '../optional_label';
|
||||
import { useBrowserAdvancedFieldsContext } from '../contexts';
|
||||
|
@ -22,6 +16,7 @@ import { Validation, ConfigKey } from '../types';
|
|||
|
||||
interface Props {
|
||||
validate: Validation;
|
||||
minColumnWidth?: string;
|
||||
}
|
||||
|
||||
type ThrottlingConfigs =
|
||||
|
@ -30,7 +25,7 @@ type ThrottlingConfigs =
|
|||
| ConfigKey.UPLOAD_SPEED
|
||||
| ConfigKey.LATENCY;
|
||||
|
||||
export const ThrottlingFields = memo<Props>(({ validate }) => {
|
||||
export const ThrottlingFields = memo<Props>(({ validate, minColumnWidth }) => {
|
||||
const { fields, setFields } = useBrowserAdvancedFieldsContext();
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
|
@ -148,7 +143,8 @@ export const ThrottlingFields = memo<Props>(({ validate }) => {
|
|||
) : null;
|
||||
|
||||
return (
|
||||
<EuiDescribedFormGroup
|
||||
<DescribedFormGroupWithWrap
|
||||
minColumnWidth={minColumnWidth}
|
||||
title={
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
|
@ -183,6 +179,6 @@ export const ThrottlingFields = memo<Props>(({ validate }) => {
|
|||
}
|
||||
/>
|
||||
{throttlingInputs}
|
||||
</EuiDescribedFormGroup>
|
||||
</DescribedFormGroupWithWrap>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ import React from 'react';
|
|||
import styled from 'styled-components';
|
||||
|
||||
import { EuiPanel } from '@elastic/eui';
|
||||
import { euiStyled } from '../../../../../../src/plugins/kibana_react/common';
|
||||
import { CodeEditor as MonacoCodeEditor } from '../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
import { MonacoEditorLangId } from './types';
|
||||
|
@ -28,7 +29,11 @@ interface Props {
|
|||
export const CodeEditor = ({ ariaLabel, id, languageId, onChange, value }: Props) => {
|
||||
return (
|
||||
<CodeEditorContainer borderRadius="none" hasShadow={false} hasBorder={true}>
|
||||
<div id={`${id}-editor`} aria-label={ariaLabel} data-test-subj="codeEditorContainer">
|
||||
<MonacoCodeContainer
|
||||
id={`${id}-editor`}
|
||||
aria-label={ariaLabel}
|
||||
data-test-subj="codeEditorContainer"
|
||||
>
|
||||
<MonacoCodeEditor
|
||||
languageId={languageId}
|
||||
width="100%"
|
||||
|
@ -41,7 +46,13 @@ export const CodeEditor = ({ ariaLabel, id, languageId, onChange, value }: Props
|
|||
isCopyable={true}
|
||||
allowFullScreen={true}
|
||||
/>
|
||||
</div>
|
||||
</MonacoCodeContainer>
|
||||
</CodeEditorContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const MonacoCodeContainer = euiStyled.div`
|
||||
& > .kibanaCodeEditor {
|
||||
z-index: 0;
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 { EuiDescribedFormGroup } from '@elastic/eui';
|
||||
import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
|
||||
|
||||
/**
|
||||
* EuiForm group doesn't expose props to control the flex wrapping on flex groups defining form rows.
|
||||
* This override allows to define a minimum column width to which the Described Form's flex rows should wrap.
|
||||
*/
|
||||
export const DescribedFormGroupWithWrap = euiStyled(EuiDescribedFormGroup)<{
|
||||
minColumnWidth?: string;
|
||||
}>`
|
||||
> .euiFlexGroup {
|
||||
${({ minColumnWidth }) => (minColumnWidth ? `flex-wrap: wrap;` : '')}
|
||||
> .euiFlexItem {
|
||||
${({ minColumnWidth }) => (minColumnWidth ? `min-width: ${minColumnWidth};` : '')}
|
||||
}
|
||||
}
|
||||
`;
|
|
@ -14,11 +14,11 @@ import {
|
|||
EuiFormRow,
|
||||
EuiSelect,
|
||||
EuiSpacer,
|
||||
EuiDescribedFormGroup,
|
||||
EuiSwitch,
|
||||
EuiCallOut,
|
||||
EuiLink,
|
||||
} from '@elastic/eui';
|
||||
import { DescribedFormGroupWithWrap } from './common/described_form_group_with_wrap';
|
||||
import { ConfigKey, DataStream, Validation } from './types';
|
||||
import { usePolicyConfigContext } from './contexts';
|
||||
import { TLSFields } from './tls_fields';
|
||||
|
@ -36,6 +36,7 @@ interface Props {
|
|||
dataStreams?: DataStream[];
|
||||
children?: React.ReactNode;
|
||||
appendAdvancedFields?: React.ReactNode;
|
||||
minColumnWidth?: string;
|
||||
}
|
||||
|
||||
const dataStreamToString = [
|
||||
|
@ -54,7 +55,7 @@ const dataStreamToString = [
|
|||
];
|
||||
|
||||
export const CustomFields = memo<Props>(
|
||||
({ validate, dataStreams = [], children, appendAdvancedFields }) => {
|
||||
({ validate, dataStreams = [], children, appendAdvancedFields, minColumnWidth }) => {
|
||||
const { monitorType, setMonitorType, isTLSEnabled, setIsTLSEnabled, isEditable } =
|
||||
usePolicyConfigContext();
|
||||
|
||||
|
@ -86,7 +87,8 @@ export const CustomFields = memo<Props>(
|
|||
|
||||
return (
|
||||
<EuiForm component="form">
|
||||
<EuiDescribedFormGroup
|
||||
<DescribedFormGroupWithWrap
|
||||
minColumnWidth={minColumnWidth}
|
||||
title={
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
|
@ -165,9 +167,10 @@ export const CustomFields = memo<Props>(
|
|||
{renderSimpleFields(monitorType)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiDescribedFormGroup>
|
||||
</DescribedFormGroupWithWrap>
|
||||
{(isHTTP || isTCP) && (
|
||||
<EuiDescribedFormGroup
|
||||
<DescribedFormGroupWithWrap
|
||||
minColumnWidth={minColumnWidth}
|
||||
title={
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
|
@ -197,15 +200,23 @@ export const CustomFields = memo<Props>(
|
|||
onChange={(event) => setIsTLSEnabled(event.target.checked)}
|
||||
/>
|
||||
<TLSFields />
|
||||
</EuiDescribedFormGroup>
|
||||
</DescribedFormGroupWithWrap>
|
||||
)}
|
||||
<EuiSpacer size="m" />
|
||||
{isHTTP && (
|
||||
<HTTPAdvancedFields validate={validate}>{appendAdvancedFields}</HTTPAdvancedFields>
|
||||
<HTTPAdvancedFields validate={validate} minColumnWidth={minColumnWidth}>
|
||||
{appendAdvancedFields}
|
||||
</HTTPAdvancedFields>
|
||||
)}
|
||||
{isTCP && (
|
||||
<TCPAdvancedFields minColumnWidth={minColumnWidth}>
|
||||
{appendAdvancedFields}
|
||||
</TCPAdvancedFields>
|
||||
)}
|
||||
{isTCP && <TCPAdvancedFields>{appendAdvancedFields}</TCPAdvancedFields>}
|
||||
{isBrowser && (
|
||||
<BrowserAdvancedFields validate={validate}>{appendAdvancedFields}</BrowserAdvancedFields>
|
||||
<BrowserAdvancedFields validate={validate} minColumnWidth={minColumnWidth}>
|
||||
{appendAdvancedFields}
|
||||
</BrowserAdvancedFields>
|
||||
)}
|
||||
{isICMP && <ICMPAdvancedFields>{appendAdvancedFields}</ICMPAdvancedFields>}
|
||||
</EuiForm>
|
||||
|
|
|
@ -14,11 +14,11 @@ import {
|
|||
EuiFieldText,
|
||||
EuiFormRow,
|
||||
EuiSelect,
|
||||
EuiDescribedFormGroup,
|
||||
EuiCheckbox,
|
||||
EuiSpacer,
|
||||
EuiFieldPassword,
|
||||
} from '@elastic/eui';
|
||||
import { DescribedFormGroupWithWrap } from '../common/described_form_group_with_wrap';
|
||||
|
||||
import { useHTTPAdvancedFieldsContext } from '../contexts';
|
||||
|
||||
|
@ -33,9 +33,10 @@ import { ComboBox } from '../combo_box';
|
|||
interface Props {
|
||||
validate: Validation;
|
||||
children?: React.ReactNode;
|
||||
minColumnWidth?: string;
|
||||
}
|
||||
|
||||
export const HTTPAdvancedFields = memo<Props>(({ validate, children }) => {
|
||||
export const HTTPAdvancedFields = memo<Props>(({ validate, children, minColumnWidth }) => {
|
||||
const { fields, setFields } = useHTTPAdvancedFieldsContext();
|
||||
const handleInputChange = useCallback(
|
||||
({ value, configKey }: { value: unknown; configKey: ConfigKey }) => {
|
||||
|
@ -56,7 +57,8 @@ export const HTTPAdvancedFields = memo<Props>(({ validate, children }) => {
|
|||
data-test-subj="syntheticsHTTPAdvancedFieldsAccordion"
|
||||
>
|
||||
<EuiSpacer size="xl" />
|
||||
<EuiDescribedFormGroup
|
||||
<DescribedFormGroupWithWrap
|
||||
minColumnWidth={minColumnWidth}
|
||||
title={
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
|
@ -248,9 +250,10 @@ export const HTTPAdvancedFields = memo<Props>(({ validate, children }) => {
|
|||
)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</DescribedFormGroupWithWrap>
|
||||
<EuiSpacer size="xl" />
|
||||
<EuiDescribedFormGroup
|
||||
<DescribedFormGroupWithWrap
|
||||
minColumnWidth={minColumnWidth}
|
||||
title={
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
|
@ -316,8 +319,9 @@ export const HTTPAdvancedFields = memo<Props>(({ validate, children }) => {
|
|||
)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
<EuiDescribedFormGroup
|
||||
</DescribedFormGroupWithWrap>
|
||||
<DescribedFormGroupWithWrap
|
||||
minColumnWidth={minColumnWidth}
|
||||
title={
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
|
@ -461,7 +465,7 @@ export const HTTPAdvancedFields = memo<Props>(({ validate, children }) => {
|
|||
data-test-subj="syntheticsResponseBodyCheckNegative"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</DescribedFormGroupWithWrap>
|
||||
{children}
|
||||
</EuiAccordion>
|
||||
);
|
||||
|
|
|
@ -7,14 +7,8 @@
|
|||
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import {
|
||||
EuiAccordion,
|
||||
EuiCheckbox,
|
||||
EuiFormRow,
|
||||
EuiDescribedFormGroup,
|
||||
EuiFieldText,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { EuiAccordion, EuiCheckbox, EuiFormRow, EuiFieldText, EuiSpacer } from '@elastic/eui';
|
||||
import { DescribedFormGroupWithWrap } from '../common/described_form_group_with_wrap';
|
||||
|
||||
import { useTCPAdvancedFieldsContext } from '../contexts';
|
||||
|
||||
|
@ -24,9 +18,10 @@ import { OptionalLabel } from '../optional_label';
|
|||
|
||||
interface Props {
|
||||
children?: React.ReactNode;
|
||||
minColumnWidth?: string;
|
||||
}
|
||||
|
||||
export const TCPAdvancedFields = memo<Props>(({ children }) => {
|
||||
export const TCPAdvancedFields = memo<Props>(({ children, minColumnWidth }) => {
|
||||
const { fields, setFields } = useTCPAdvancedFieldsContext();
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
|
@ -43,7 +38,8 @@ export const TCPAdvancedFields = memo<Props>(({ children }) => {
|
|||
data-test-subj="syntheticsTCPAdvancedFieldsAccordion"
|
||||
>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiDescribedFormGroup
|
||||
<DescribedFormGroupWithWrap
|
||||
minColumnWidth={minColumnWidth}
|
||||
title={
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
|
@ -134,8 +130,9 @@ export const TCPAdvancedFields = memo<Props>(({ children }) => {
|
|||
data-test-subj="syntheticsTCPRequestSendCheck"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
<EuiDescribedFormGroup
|
||||
</DescribedFormGroupWithWrap>
|
||||
<DescribedFormGroupWithWrap
|
||||
minColumnWidth={minColumnWidth}
|
||||
title={
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
|
@ -179,7 +176,7 @@ export const TCPAdvancedFields = memo<Props>(({ children }) => {
|
|||
data-test-subj="syntheticsTCPResponseReceiveCheck"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</DescribedFormGroupWithWrap>
|
||||
{children}
|
||||
</EuiAccordion>
|
||||
);
|
||||
|
|
|
@ -35,7 +35,7 @@ describe('<ActionBar />', () => {
|
|||
});
|
||||
|
||||
it('only calls setMonitor when valid and after submission', () => {
|
||||
render(<ActionBar monitor={monitor} isValid={true} />);
|
||||
render(<ActionBar monitor={monitor} isTestRunInProgress={false} isValid={true} />);
|
||||
|
||||
act(() => {
|
||||
userEvent.click(screen.getByText('Save monitor'));
|
||||
|
@ -45,7 +45,7 @@ describe('<ActionBar />', () => {
|
|||
});
|
||||
|
||||
it('does not call setMonitor until submission', () => {
|
||||
render(<ActionBar monitor={monitor} isValid={true} />);
|
||||
render(<ActionBar monitor={monitor} isTestRunInProgress={false} isValid={true} />);
|
||||
|
||||
expect(setMonitor).not.toBeCalled();
|
||||
|
||||
|
@ -57,7 +57,7 @@ describe('<ActionBar />', () => {
|
|||
});
|
||||
|
||||
it('does not call setMonitor if invalid', () => {
|
||||
render(<ActionBar monitor={monitor} isValid={false} />);
|
||||
render(<ActionBar monitor={monitor} isTestRunInProgress={false} isValid={false} />);
|
||||
|
||||
expect(setMonitor).not.toBeCalled();
|
||||
|
||||
|
@ -69,7 +69,7 @@ describe('<ActionBar />', () => {
|
|||
});
|
||||
|
||||
it('disables button and displays help text when form is invalid after first submission', async () => {
|
||||
render(<ActionBar monitor={monitor} isValid={false} />);
|
||||
render(<ActionBar monitor={monitor} isTestRunInProgress={false} isValid={false} />);
|
||||
|
||||
expect(
|
||||
screen.queryByText('Your monitor has errors. Please fix them before saving.')
|
||||
|
@ -90,7 +90,9 @@ describe('<ActionBar />', () => {
|
|||
|
||||
it('calls option onSave when saving monitor', () => {
|
||||
const onSave = jest.fn();
|
||||
render(<ActionBar monitor={monitor} isValid={false} onSave={onSave} />);
|
||||
render(
|
||||
<ActionBar monitor={monitor} isTestRunInProgress={false} isValid={false} onSave={onSave} />
|
||||
);
|
||||
|
||||
act(() => {
|
||||
userEvent.click(screen.getByText('Save monitor'));
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiText,
|
||||
EuiToolTip,
|
||||
EuiPopover,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
|
@ -37,11 +37,19 @@ export interface ActionBarProps {
|
|||
monitor: SyntheticsMonitor;
|
||||
isValid: boolean;
|
||||
testRun?: TestRun;
|
||||
isTestRunInProgress: boolean;
|
||||
onSave?: () => void;
|
||||
onTestNow?: () => void;
|
||||
}
|
||||
|
||||
export const ActionBar = ({ monitor, isValid, onSave, onTestNow, testRun }: ActionBarProps) => {
|
||||
export const ActionBar = ({
|
||||
monitor,
|
||||
isValid,
|
||||
onSave,
|
||||
onTestNow,
|
||||
testRun,
|
||||
isTestRunInProgress,
|
||||
}: ActionBarProps) => {
|
||||
const { monitorId } = useParams<{ monitorId: string }>();
|
||||
const { basePath } = useContext(UptimeSettingsContext);
|
||||
const { locations } = useSelector(monitorManagementListSelector);
|
||||
|
@ -49,6 +57,7 @@ export const ActionBar = ({ monitor, isValid, onSave, onTestNow, testRun }: Acti
|
|||
const [hasBeenSubmitted, setHasBeenSubmitted] = useState(false);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [isSuccessful, setIsSuccessful] = useState(false);
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState<boolean | undefined>(undefined);
|
||||
|
||||
const { data, status } = useFetcher(() => {
|
||||
if (!isSaving || !isValid) {
|
||||
|
@ -94,7 +103,7 @@ export const ActionBar = ({ monitor, isValid, onSave, onTestNow, testRun }: Acti
|
|||
});
|
||||
setIsSuccessful(true);
|
||||
} else if (hasErrors && !loading) {
|
||||
Object.values(data).forEach((location) => {
|
||||
Object.values(data!).forEach((location) => {
|
||||
const { status: responseStatus, reason } = location.error || {};
|
||||
kibanaService.toasts.addWarning({
|
||||
title: i18n.translate('xpack.uptime.monitorManagement.service.error.title', {
|
||||
|
@ -144,35 +153,51 @@ export const ActionBar = ({ monitor, isValid, onSave, onTestNow, testRun }: Acti
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="s">
|
||||
{onTestNow && (
|
||||
<EuiFlexItem grow={false} style={{ marginRight: 20 }}>
|
||||
<EuiToolTip content={TEST_NOW_DESCRIPTION}>
|
||||
<EuiButton
|
||||
fill
|
||||
size="s"
|
||||
color="success"
|
||||
iconType="play"
|
||||
onClick={() => onTestNow()}
|
||||
disabled={!isValid}
|
||||
data-test-subj={'monitorTestNowRunBtn'}
|
||||
>
|
||||
{testRun ? RE_RUN_TEST_LABEL : RUN_TEST_LABEL}
|
||||
</EuiButton>
|
||||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
color="ghost"
|
||||
size="s"
|
||||
iconType="cross"
|
||||
href={`${basePath}/app/uptime/${MONITOR_MANAGEMENT_ROUTE}`}
|
||||
>
|
||||
{DISCARD_LABEL}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
|
||||
{onTestNow && (
|
||||
<EuiFlexItem grow={false}>
|
||||
{/* Popover is used instead of EuiTooltip until the resolution of https://github.com/elastic/eui/issues/5604 */}
|
||||
<EuiPopover
|
||||
repositionOnScroll={true}
|
||||
initialFocus={false}
|
||||
button={
|
||||
<EuiButton
|
||||
css={{ width: '100%' }}
|
||||
fill
|
||||
size="s"
|
||||
color="success"
|
||||
iconType="play"
|
||||
disabled={!isValid || isTestRunInProgress}
|
||||
data-test-subj={'monitorTestNowRunBtn'}
|
||||
onClick={() => onTestNow()}
|
||||
onMouseEnter={() => {
|
||||
setIsPopoverOpen(true);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setIsPopoverOpen(false);
|
||||
}}
|
||||
>
|
||||
{testRun ? RE_RUN_TEST_LABEL : RUN_TEST_LABEL}
|
||||
</EuiButton>
|
||||
}
|
||||
isOpen={isPopoverOpen}
|
||||
>
|
||||
<EuiText style={{ width: 260, outline: 'none' }}>
|
||||
<p>{TEST_NOW_DESCRIPTION}</p>
|
||||
</EuiText>
|
||||
</EuiPopover>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
|
|
|
@ -59,7 +59,9 @@ describe('<ActionBar /> Service Errors', () => {
|
|||
status: FETCH_STATUS.SUCCESS,
|
||||
refetch: () => {},
|
||||
});
|
||||
render(<ActionBar monitor={monitor} isValid={true} />, { state: mockLocationsState });
|
||||
render(<ActionBar monitor={monitor} isTestRunInProgress={false} isValid={true} />, {
|
||||
state: mockLocationsState,
|
||||
});
|
||||
userEvent.click(screen.getByText('Save monitor'));
|
||||
|
||||
await waitFor(() => {
|
||||
|
|
|
@ -88,7 +88,7 @@ export const EditMonitorConfig = ({ monitor }: Props) => {
|
|||
browserDefaultValues={fullDefaultConfig[DataStream.BROWSER]}
|
||||
tlsDefaultValues={defaultTLSConfig}
|
||||
>
|
||||
<MonitorConfig />
|
||||
<MonitorConfig isEdit={true} />
|
||||
</SyntheticsProviders>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,17 +6,19 @@
|
|||
*/
|
||||
import React, { memo } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiFormRow, EuiSpacer, EuiDescribedFormGroup, EuiLink, EuiFieldText } from '@elastic/eui';
|
||||
import type { Validation } from '../../../../common/types/index';
|
||||
import { ConfigKey } from '../../../../common/runtime_types/monitor_management';
|
||||
import { EuiFormRow, EuiSpacer, EuiLink, EuiFieldText } from '@elastic/eui';
|
||||
import type { Validation } from '../../../../common/types';
|
||||
import { ConfigKey } from '../../../../common/runtime_types';
|
||||
import { DescribedFormGroupWithWrap } from '../../fleet_package/common/described_form_group_with_wrap';
|
||||
import { usePolicyConfigContext } from '../../fleet_package/contexts';
|
||||
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
interface Props {
|
||||
validate: Validation;
|
||||
minColumnWidth?: string;
|
||||
}
|
||||
|
||||
export const MonitorManagementAdvancedFields = memo<Props>(({ validate }) => {
|
||||
export const MonitorManagementAdvancedFields = memo<Props>(({ validate, minColumnWidth }) => {
|
||||
const { namespace, setNamespace } = usePolicyConfigContext();
|
||||
|
||||
const namespaceErrorMsg = validate[ConfigKey.NAMESPACE]?.({
|
||||
|
@ -26,7 +28,8 @@ export const MonitorManagementAdvancedFields = memo<Props>(({ validate }) => {
|
|||
const { services } = useKibana();
|
||||
|
||||
return (
|
||||
<EuiDescribedFormGroup
|
||||
<DescribedFormGroupWithWrap
|
||||
minColumnWidth={minColumnWidth}
|
||||
title={
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
|
@ -83,6 +86,6 @@ export const MonitorManagementAdvancedFields = memo<Props>(({ validate }) => {
|
|||
name="namespace"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
</DescribedFormGroupWithWrap>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -5,9 +5,17 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
|
||||
import { EuiResizableContainer } from '@elastic/eui';
|
||||
import {
|
||||
EuiFlyoutBody,
|
||||
EuiFlyoutHeader,
|
||||
EuiFlyout,
|
||||
EuiSpacer,
|
||||
EuiFlyoutFooter,
|
||||
EuiButtonEmpty,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { defaultConfig, usePolicyConfigContext } from '../../fleet_package/contexts';
|
||||
|
||||
|
@ -19,7 +27,7 @@ import { MonitorFields } from './monitor_fields';
|
|||
import { TestNowMode, TestRun } from '../test_now_mode/test_now_mode';
|
||||
import { MonitorFields as MonitorFieldsType } from '../../../../common/runtime_types';
|
||||
|
||||
export const MonitorConfig = () => {
|
||||
export const MonitorConfig = ({ isEdit = false }: { isEdit: boolean }) => {
|
||||
const { monitorType } = usePolicyConfigContext();
|
||||
|
||||
/* raw policy config compatible with the UI. Save this to saved objects */
|
||||
|
@ -37,46 +45,70 @@ export const MonitorConfig = () => {
|
|||
});
|
||||
|
||||
const [testRun, setTestRun] = useState<TestRun>();
|
||||
const [isTestRunInProgress, setIsTestRunInProgress] = useState<boolean>(false);
|
||||
const [isFlyoutOpen, setIsFlyoutOpen] = useState<boolean>(false);
|
||||
|
||||
const onTestNow = () => {
|
||||
const handleTestNow = () => {
|
||||
if (config) {
|
||||
setTestRun({ id: uuidv4(), monitor: config as MonitorFieldsType });
|
||||
setIsTestRunInProgress(true);
|
||||
setIsFlyoutOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTestDone = useCallback(() => {
|
||||
setIsTestRunInProgress(false);
|
||||
}, [setIsTestRunInProgress]);
|
||||
|
||||
const handleFlyoutClose = useCallback(() => {
|
||||
handleTestDone();
|
||||
setIsFlyoutOpen(false);
|
||||
}, [handleTestDone, setIsFlyoutOpen]);
|
||||
|
||||
const flyout = isFlyoutOpen && config && (
|
||||
<EuiFlyout
|
||||
type="push"
|
||||
size="m"
|
||||
paddingSize="m"
|
||||
maxWidth="44%"
|
||||
aria-labelledby={TEST_RESULT}
|
||||
onClose={handleFlyoutClose}
|
||||
>
|
||||
<EuiFlyoutHeader>
|
||||
<EuiSpacer size="xl" />
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<TestNowMode testRun={testRun} isMonitorSaved={isEdit} onDone={handleTestDone} />
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
<EuiButtonEmpty iconType="cross" onClick={handleFlyoutClose} flush="left">
|
||||
{CLOSE_LABEL}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlyoutFooter>
|
||||
</EuiFlyout>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiResizableContainer>
|
||||
{(EuiResizablePanel, EuiResizableButton) => (
|
||||
<>
|
||||
<EuiResizablePanel
|
||||
initialSize={55}
|
||||
minSize="30%"
|
||||
mode={[
|
||||
'collapsible',
|
||||
{
|
||||
position: 'top',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<MonitorFields />
|
||||
</EuiResizablePanel>
|
||||
<MonitorFields />
|
||||
|
||||
<EuiResizableButton />
|
||||
|
||||
<EuiResizablePanel initialSize={45} minSize="200px" mode="main">
|
||||
{config && <TestNowMode testRun={testRun} />}
|
||||
</EuiResizablePanel>
|
||||
</>
|
||||
)}
|
||||
</EuiResizableContainer>
|
||||
{flyout}
|
||||
|
||||
<ActionBarPortal
|
||||
monitor={policyConfig[monitorType]}
|
||||
isValid={isValid}
|
||||
onTestNow={onTestNow}
|
||||
onTestNow={handleTestNow}
|
||||
testRun={testRun}
|
||||
isTestRunInProgress={isTestRunInProgress}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const TEST_RESULT = i18n.translate('xpack.uptime.monitorManagement.testResult', {
|
||||
defaultMessage: 'Test result',
|
||||
});
|
||||
|
||||
const CLOSE_LABEL = i18n.translate('xpack.uptime.monitorManagement.closeButtonLabel', {
|
||||
defaultMessage: 'Close',
|
||||
});
|
||||
|
|
|
@ -15,14 +15,22 @@ import { validate } from '../validation';
|
|||
import { MonitorNameAndLocation } from './monitor_name_location';
|
||||
import { MonitorManagementAdvancedFields } from './monitor_advanced_fields';
|
||||
|
||||
const MIN_COLUMN_WRAP_WIDTH = '360px';
|
||||
|
||||
export const MonitorFields = () => {
|
||||
const { monitorType } = usePolicyConfigContext();
|
||||
return (
|
||||
<EuiForm id="syntheticsServiceCreateMonitorForm" component="form">
|
||||
<CustomFields
|
||||
minColumnWidth={MIN_COLUMN_WRAP_WIDTH}
|
||||
validate={validate[monitorType]}
|
||||
dataStreams={[DataStream.HTTP, DataStream.TCP, DataStream.ICMP, DataStream.BROWSER]}
|
||||
appendAdvancedFields={<MonitorManagementAdvancedFields validate={validate[monitorType]} />}
|
||||
appendAdvancedFields={
|
||||
<MonitorManagementAdvancedFields
|
||||
validate={validate[monitorType]}
|
||||
minColumnWidth={MIN_COLUMN_WRAP_WIDTH}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<MonitorNameAndLocation validate={validate[monitorType]} />
|
||||
</CustomFields>
|
||||
|
|
|
@ -43,7 +43,7 @@ export const MonitorNameAndLocation = ({ validate }: Props) => {
|
|||
defaultMessage="Monitor name"
|
||||
/>
|
||||
}
|
||||
fullWidth={true}
|
||||
fullWidth={false}
|
||||
isInvalid={isNameInvalid || nameAlreadyExists}
|
||||
error={
|
||||
nameAlreadyExists ? (
|
||||
|
|
|
@ -14,8 +14,16 @@ import { BrowserTestRunResult } from './browser_test_results';
|
|||
import { fireEvent } from '@testing-library/dom';
|
||||
|
||||
describe('BrowserTestRunResult', function () {
|
||||
const onDone = jest.fn();
|
||||
let testId: string;
|
||||
|
||||
beforeEach(() => {
|
||||
testId = 'test-id';
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should render properly', async function () {
|
||||
render(<BrowserTestRunResult monitorId={'test-id'} />);
|
||||
render(<BrowserTestRunResult monitorId={testId} isMonitorSaved={true} onDone={onDone} />);
|
||||
expect(await screen.findByText('Test result')).toBeInTheDocument();
|
||||
expect(await screen.findByText('0 steps completed')).toBeInTheDocument();
|
||||
const dataApi = (kibanaService.core as any).data.search;
|
||||
|
@ -28,7 +36,7 @@ describe('BrowserTestRunResult', function () {
|
|||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{ term: { config_id: 'test-id' } },
|
||||
{ term: { config_id: testId } },
|
||||
{
|
||||
terms: {
|
||||
'synthetics.type': ['heartbeat/summary', 'journey/start'],
|
||||
|
@ -52,12 +60,13 @@ describe('BrowserTestRunResult', function () {
|
|||
data,
|
||||
stepListData: { steps: [stepEndDoc._source] } as any,
|
||||
loading: false,
|
||||
stepsLoading: false,
|
||||
journeyStarted: true,
|
||||
summaryDoc: summaryDoc._source,
|
||||
stepEnds: [stepEndDoc._source],
|
||||
});
|
||||
|
||||
render(<BrowserTestRunResult monitorId={'test-id'} />);
|
||||
render(<BrowserTestRunResult monitorId={testId} isMonitorSaved={true} onDone={onDone} />);
|
||||
|
||||
expect(await screen.findByText('Test result')).toBeInTheDocument();
|
||||
|
||||
|
@ -69,6 +78,9 @@ describe('BrowserTestRunResult', function () {
|
|||
|
||||
expect(await screen.findByText('Go to https://www.elastic.co/')).toBeInTheDocument();
|
||||
expect(await screen.findByText('21.8 seconds')).toBeInTheDocument();
|
||||
|
||||
// Calls onDone on completion
|
||||
expect(onDone).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import * as React from 'react';
|
||||
import { EuiAccordion, EuiText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
@ -16,13 +17,21 @@ import { TestResultHeader } from '../test_result_header';
|
|||
|
||||
interface Props {
|
||||
monitorId: string;
|
||||
isMonitorSaved: boolean;
|
||||
onDone: () => void;
|
||||
}
|
||||
export const BrowserTestRunResult = ({ monitorId }: Props) => {
|
||||
const { data, loading, stepEnds, journeyStarted, summaryDoc, stepListData } =
|
||||
export const BrowserTestRunResult = ({ monitorId, isMonitorSaved, onDone }: Props) => {
|
||||
const { data, loading, stepsLoading, stepEnds, journeyStarted, summaryDoc, stepListData } =
|
||||
useBrowserRunOnceMonitors({
|
||||
configId: monitorId,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (Boolean(summaryDoc)) {
|
||||
onDone();
|
||||
}
|
||||
}, [summaryDoc, onDone]);
|
||||
|
||||
const hits = data?.hits.hits;
|
||||
const doc = hits?.[0]?._source as JourneyStep;
|
||||
|
||||
|
@ -50,6 +59,10 @@ export const BrowserTestRunResult = ({ monitorId }: Props) => {
|
|||
</div>
|
||||
);
|
||||
|
||||
const isStepsLoading =
|
||||
journeyStarted && stepEnds.length === 0 && (!summaryDoc || (summaryDoc && stepsLoading));
|
||||
const isStepsLoadingFailed = summaryDoc && stepEnds.length === 0 && !isStepsLoading;
|
||||
|
||||
return (
|
||||
<AccordionWrapper
|
||||
id={monitorId}
|
||||
|
@ -59,13 +72,16 @@ export const BrowserTestRunResult = ({ monitorId }: Props) => {
|
|||
buttonContent={buttonContent}
|
||||
paddingSize="s"
|
||||
data-test-subj="expandResults"
|
||||
initialIsOpen={true}
|
||||
>
|
||||
{summaryDoc && stepEnds.length === 0 && <EuiText color="danger">{FAILED_TO_RUN}</EuiText>}
|
||||
{!summaryDoc && journeyStarted && stepEnds.length === 0 && <EuiText>{LOADING_STEPS}</EuiText>}
|
||||
{isStepsLoading && <EuiText>{LOADING_STEPS}</EuiText>}
|
||||
{isStepsLoadingFailed && <EuiText color="danger">{FAILED_TO_RUN}</EuiText>}
|
||||
|
||||
{stepEnds.length > 0 && stepListData?.steps && (
|
||||
<StepsList
|
||||
data={stepListData.steps}
|
||||
compactView={true}
|
||||
showStepDurationTrend={isMonitorSaved}
|
||||
loading={Boolean(loading)}
|
||||
error={undefined}
|
||||
/>
|
||||
|
|
|
@ -34,6 +34,7 @@ describe('useBrowserRunOnceMonitors', function () {
|
|||
data: undefined,
|
||||
journeyStarted: false,
|
||||
loading: true,
|
||||
stepsLoading: true,
|
||||
stepEnds: [],
|
||||
stepListData: undefined,
|
||||
summaryDoc: undefined,
|
||||
|
|
|
@ -86,7 +86,7 @@ export const useBrowserRunOnceMonitors = ({
|
|||
|
||||
const { data, loading } = useBrowserEsResults({ configId, testRunId, lastRefresh });
|
||||
|
||||
const { data: stepListData } = useFetcher(() => {
|
||||
const { data: stepListData, loading: stepsLoading } = useFetcher(() => {
|
||||
if (checkGroupId && !skipDetails) {
|
||||
return fetchJourneySteps({
|
||||
checkGroup: checkGroupId,
|
||||
|
@ -122,6 +122,7 @@ export const useBrowserRunOnceMonitors = ({
|
|||
data,
|
||||
stepEnds,
|
||||
loading,
|
||||
stepsLoading,
|
||||
stepListData,
|
||||
summaryDoc: summary,
|
||||
journeyStarted: Boolean(checkGroupId),
|
||||
|
|
|
@ -14,8 +14,16 @@ import * as runOnceHooks from './use_simple_run_once_monitors';
|
|||
import { Ping } from '../../../../../common/runtime_types';
|
||||
|
||||
describe('SimpleTestResults', function () {
|
||||
const onDone = jest.fn();
|
||||
let testId: string;
|
||||
|
||||
beforeEach(() => {
|
||||
testId = 'test-id';
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should render properly', async function () {
|
||||
render(<SimpleTestResults monitorId={'test-id'} />);
|
||||
render(<SimpleTestResults monitorId={testId} onDone={onDone} />);
|
||||
expect(await screen.findByText('Test result')).toBeInTheDocument();
|
||||
const dataApi = (kibanaService.core as any).data.search;
|
||||
|
||||
|
@ -26,7 +34,7 @@ describe('SimpleTestResults', function () {
|
|||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
filter: [{ term: { config_id: 'test-id' } }, { exists: { field: 'summary' } }],
|
||||
filter: [{ term: { config_id: testId } }, { exists: { field: 'summary' } }],
|
||||
},
|
||||
},
|
||||
sort: [{ '@timestamp': 'desc' }],
|
||||
|
@ -51,7 +59,7 @@ describe('SimpleTestResults', function () {
|
|||
loading: false,
|
||||
});
|
||||
|
||||
render(<SimpleTestResults monitorId={'test-id'} />);
|
||||
render(<SimpleTestResults monitorId={'test-id'} onDone={onDone} />);
|
||||
|
||||
expect(await screen.findByText('Test result')).toBeInTheDocument();
|
||||
|
||||
|
@ -61,6 +69,9 @@ describe('SimpleTestResults', function () {
|
|||
expect(await screen.findByText('Checked Jan 12, 2022 11:54:27 AM')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Took 191 ms')).toBeInTheDocument();
|
||||
|
||||
// Calls onDone on completion
|
||||
expect(onDone).toHaveBeenCalled();
|
||||
|
||||
screen.debug();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,16 +12,18 @@ import { TestResultHeader } from '../test_result_header';
|
|||
|
||||
interface Props {
|
||||
monitorId: string;
|
||||
onDone: () => void;
|
||||
}
|
||||
export function SimpleTestResults({ monitorId }: Props) {
|
||||
export function SimpleTestResults({ monitorId, onDone }: Props) {
|
||||
const [summaryDocs, setSummaryDocs] = useState<Ping[]>([]);
|
||||
const { summaryDoc, loading } = useSimpleRunOnceMonitors({ configId: monitorId });
|
||||
|
||||
useEffect(() => {
|
||||
if (summaryDoc) {
|
||||
setSummaryDocs((prevState) => [summaryDoc, ...prevState]);
|
||||
onDone();
|
||||
}
|
||||
}, [summaryDoc]);
|
||||
}, [summaryDoc, onDone]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -13,9 +13,19 @@ import { kibanaService } from '../../../state/kibana_service';
|
|||
import { MonitorFields } from '../../../../common/runtime_types';
|
||||
|
||||
describe('TestNowMode', function () {
|
||||
const onDone = jest.fn();
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should render properly', async function () {
|
||||
render(
|
||||
<TestNowMode testRun={{ id: 'test-run', monitor: { type: 'browser' } as MonitorFields }} />
|
||||
<TestNowMode
|
||||
testRun={{ id: 'test-run', monitor: { type: 'browser' } as MonitorFields }}
|
||||
isMonitorSaved={false}
|
||||
onDone={onDone}
|
||||
/>
|
||||
);
|
||||
expect(await screen.findByText('Test result')).toBeInTheDocument();
|
||||
expect(await screen.findByText('PENDING')).toBeInTheDocument();
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiCallOut,
|
||||
|
@ -26,13 +26,25 @@ export interface TestRun {
|
|||
monitor: MonitorFields;
|
||||
}
|
||||
|
||||
export function TestNowMode({ testRun }: { testRun?: TestRun }) {
|
||||
export function TestNowMode({
|
||||
testRun,
|
||||
isMonitorSaved,
|
||||
onDone,
|
||||
}: {
|
||||
testRun?: TestRun;
|
||||
isMonitorSaved: boolean;
|
||||
onDone: () => void;
|
||||
}) {
|
||||
const [serviceError, setServiceError] = useState<null | Error>(null);
|
||||
|
||||
const { data, loading: isPushing } = useFetcher(() => {
|
||||
if (testRun) {
|
||||
return runOnceMonitor({
|
||||
monitor: testRun.monitor,
|
||||
id: testRun.id,
|
||||
});
|
||||
})
|
||||
.then(() => setServiceError(null))
|
||||
.catch((error) => setServiceError(error));
|
||||
}
|
||||
return new Promise((resolve) => resolve(null));
|
||||
}, [testRun]);
|
||||
|
@ -49,7 +61,13 @@ export function TestNowMode({ testRun }: { testRun?: TestRun }) {
|
|||
|
||||
const errors = (data as { errors?: Array<{ error: Error }> })?.errors;
|
||||
|
||||
const hasErrors = errors && errors?.length > 0;
|
||||
const hasErrors = serviceError || (errors && errors?.length > 0);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPushing && (!testRun || hasErrors)) {
|
||||
onDone();
|
||||
}
|
||||
}, [testRun, hasErrors, isPushing, onDone]);
|
||||
|
||||
if (!testRun) {
|
||||
return null;
|
||||
|
@ -68,7 +86,12 @@ export function TestNowMode({ testRun }: { testRun?: TestRun }) {
|
|||
{testRun && !hasErrors && !isPushing && (
|
||||
<EuiFlexGroup direction="column" gutterSize="xs">
|
||||
<EuiFlexItem key={testRun.id}>
|
||||
<TestRunResult monitorId={testRun.id} monitor={testRun.monitor} />
|
||||
<TestRunResult
|
||||
monitorId={testRun.id}
|
||||
monitor={testRun.monitor}
|
||||
isMonitorSaved={isMonitorSaved}
|
||||
onDone={onDone}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
|
|
|
@ -13,11 +13,13 @@ import { SimpleTestResults } from './simple/simple_test_results';
|
|||
interface Props {
|
||||
monitorId: string;
|
||||
monitor: SyntheticsMonitor;
|
||||
isMonitorSaved: boolean;
|
||||
onDone: () => void;
|
||||
}
|
||||
export const TestRunResult = ({ monitorId, monitor }: Props) => {
|
||||
export const TestRunResult = ({ monitorId, monitor, isMonitorSaved, onDone }: Props) => {
|
||||
return monitor.type === 'browser' ? (
|
||||
<BrowserTestRunResult monitorId={monitorId} />
|
||||
<BrowserTestRunResult monitorId={monitorId} isMonitorSaved={isMonitorSaved} onDone={onDone} />
|
||||
) : (
|
||||
<SimpleTestResults monitorId={monitorId} />
|
||||
<SimpleTestResults monitorId={monitorId} onDone={onDone} />
|
||||
);
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import type { MouseEvent } from 'react';
|
||||
|
||||
import * as React from 'react';
|
||||
import { EuiButtonEmpty, EuiPopover } from '@elastic/eui';
|
||||
import { EuiButtonEmpty, EuiPopover, EuiText } from '@elastic/eui';
|
||||
import { useMemo } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { JourneyStep } from '../../../../common/runtime_types';
|
||||
|
@ -16,6 +16,7 @@ import { StepFieldTrend } from './step_field_trend';
|
|||
import { microToSec } from '../../../lib/formatting';
|
||||
|
||||
interface Props {
|
||||
showStepDurationTrend?: boolean;
|
||||
compactView?: boolean;
|
||||
step: JourneyStep;
|
||||
durationPopoverOpenIndex: number | null;
|
||||
|
@ -26,8 +27,20 @@ export const StepDuration = ({
|
|||
step,
|
||||
durationPopoverOpenIndex,
|
||||
setDurationPopoverOpenIndex,
|
||||
showStepDurationTrend = true,
|
||||
compactView = false,
|
||||
}: Props) => {
|
||||
const stepDurationText = useMemo(
|
||||
() =>
|
||||
i18n.translate('xpack.uptime.synthetics.step.duration', {
|
||||
defaultMessage: '{value} seconds',
|
||||
values: {
|
||||
value: microToSec(step.synthetics.step?.duration.us!, 1),
|
||||
},
|
||||
}),
|
||||
[step.synthetics.step?.duration.us]
|
||||
);
|
||||
|
||||
const component = useMemo(
|
||||
() => (
|
||||
<StepFieldTrend
|
||||
|
@ -43,17 +56,16 @@ export const StepDuration = ({
|
|||
return <span>--</span>;
|
||||
}
|
||||
|
||||
if (!showStepDurationTrend) {
|
||||
return <EuiText>{stepDurationText}</EuiText>;
|
||||
}
|
||||
|
||||
const button = (
|
||||
<EuiButtonEmpty
|
||||
onMouseEnter={() => setDurationPopoverOpenIndex(step.synthetics.step?.index ?? null)}
|
||||
iconType={compactView ? undefined : 'visArea'}
|
||||
>
|
||||
{i18n.translate('xpack.uptime.synthetics.step.duration', {
|
||||
defaultMessage: '{value} seconds',
|
||||
values: {
|
||||
value: microToSec(step.synthetics.step?.duration.us!, 1),
|
||||
},
|
||||
})}
|
||||
{stepDurationText}
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ interface Props {
|
|||
error?: Error;
|
||||
loading: boolean;
|
||||
compactView?: boolean;
|
||||
showStepDurationTrend?: boolean;
|
||||
}
|
||||
|
||||
interface StepStatusCount {
|
||||
|
@ -85,7 +86,13 @@ function reduceStepStatus(prev: StepStatusCount, cur: JourneyStep): StepStatusCo
|
|||
return prev;
|
||||
}
|
||||
|
||||
export const StepsList = ({ data, error, loading, compactView = false }: Props) => {
|
||||
export const StepsList = ({
|
||||
data,
|
||||
error,
|
||||
loading,
|
||||
showStepDurationTrend = true,
|
||||
compactView = false,
|
||||
}: Props) => {
|
||||
const steps: JourneyStep[] = data.filter(isStepEnd);
|
||||
|
||||
const { expandedRows, toggleExpand } = useExpandedRow({ steps, allSteps: data, loading });
|
||||
|
@ -140,6 +147,7 @@ export const StepsList = ({ data, error, loading, compactView = false }: Props)
|
|||
step={item}
|
||||
durationPopoverOpenIndex={durationPopoverOpenIndex}
|
||||
setDurationPopoverOpenIndex={setDurationPopoverOpenIndex}
|
||||
showStepDurationTrend={showStepDurationTrend}
|
||||
compactView={compactView}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -37,7 +37,7 @@ export const AddMonitorPage: React.FC = () => {
|
|||
allowedScheduleUnits: [ScheduleUnit.MINUTES],
|
||||
}}
|
||||
>
|
||||
<MonitorConfig />
|
||||
<MonitorConfig isEdit={false} />
|
||||
</SyntheticsProviders>
|
||||
</Loader>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue