mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Embeddable] Improve embeddable errors appearance (#134997)
* Provide a unified error handler for the embeddable * Provide a way to customize embeddable error * Update visualizations embeddable to render a custom error
This commit is contained in:
parent
292a6a6afb
commit
ed07efd794
15 changed files with 205 additions and 96 deletions
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { EuiText, EuiIcon, EuiSpacer, EuiPopover, EuiLink } from '@elastic/eui';
|
||||
import { EuiText, EuiIcon, EuiPopover, EuiLink, EuiEmptyPrompt } from '@elastic/eui';
|
||||
import React, { useState } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { KibanaThemeProvider, Markdown } from '@kbn/kibana-react-plugin/public';
|
||||
|
@ -58,12 +58,13 @@ export class ErrorEmbeddable extends Embeddable<EmbeddableInput, EmbeddableOutpu
|
|||
const node = this.compact ? (
|
||||
<CompactEmbeddableError>{errorMarkdown}</CompactEmbeddableError>
|
||||
) : (
|
||||
<div className="embPanel__error embPanel__content" data-test-subj="embeddableStackError">
|
||||
<EuiText color="subdued" size="xs">
|
||||
<EuiIcon type="alert" color="danger" />
|
||||
<EuiSpacer size="s" />
|
||||
{errorMarkdown}
|
||||
</EuiText>
|
||||
<div className="embPanel__content" data-test-subj="embeddableStackError">
|
||||
<EuiEmptyPrompt
|
||||
className="embPanel__error"
|
||||
iconType="alert"
|
||||
iconColor="danger"
|
||||
body={errorMarkdown}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
const content =
|
||||
|
|
|
@ -159,6 +159,13 @@ export interface IEmbeddable<
|
|||
*/
|
||||
render(domNode: HTMLElement | Element): void;
|
||||
|
||||
/**
|
||||
* Renders a custom embeddable error at the given node.
|
||||
* @param domNode
|
||||
* @returns A callback that will be called on error destroy.
|
||||
*/
|
||||
renderError?(domNode: HTMLElement | Element, error: ErrorLike): () => void;
|
||||
|
||||
/**
|
||||
* Reload the embeddable so output and rendering is up to date. Especially relevant
|
||||
* if the embeddable takes relative time as input (e.g. now to now-15)
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
flex: 1 1 100%;
|
||||
z-index: 1;
|
||||
min-height: 0; // Absolute must for Firefox to scroll contents
|
||||
|
||||
&[data-error] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// SASSTODO: this MIGHT be fixing IE
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiBadge, EuiToolTip } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EmbeddableError } from '../embeddables/i_embeddable';
|
||||
|
||||
interface Props {
|
||||
error?: EmbeddableError;
|
||||
}
|
||||
|
||||
export function EmbeddableErrorLabel(props: Props) {
|
||||
if (!props.error) return null;
|
||||
const labelText =
|
||||
props.error.name === 'AbortError'
|
||||
? i18n.translate('embeddableApi.panel.labelAborted', {
|
||||
defaultMessage: 'Aborted',
|
||||
})
|
||||
: i18n.translate('embeddableApi.panel.labelError', {
|
||||
defaultMessage: 'Error',
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="embPanel__labelWrapper">
|
||||
<div className="embPanel__label">
|
||||
<EuiToolTip data-test-subj="embeddableErrorMessage" content={props.error.message}>
|
||||
<EuiBadge data-test-subj="embeddableErrorLabel" color="danger">
|
||||
{labelText}
|
||||
</EuiBadge>
|
||||
</EuiToolTip>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { ReactWrapper, mount } from 'enzyme';
|
||||
import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers';
|
||||
|
||||
import { findTestSubject } from '@elastic/eui/lib/test';
|
||||
|
@ -165,6 +165,105 @@ test('HelloWorldContainer in view mode hides edit mode actions', async () => {
|
|||
expect(findTestSubject(component, `embeddablePanelAction-${editModeAction.id}`).length).toBe(0);
|
||||
});
|
||||
|
||||
describe('HelloWorldContainer in error state', () => {
|
||||
let component: ReactWrapper<unknown>;
|
||||
let embeddable: ContactCardEmbeddable;
|
||||
|
||||
beforeEach(async () => {
|
||||
const inspector = inspectorPluginMock.createStartContract();
|
||||
const container = new HelloWorldContainer({ id: '123', panels: {}, viewMode: ViewMode.VIEW }, {
|
||||
getEmbeddableFactory,
|
||||
} as any);
|
||||
|
||||
embeddable = (await container.addNewEmbeddable<
|
||||
ContactCardEmbeddableInput,
|
||||
ContactCardEmbeddableOutput,
|
||||
ContactCardEmbeddable
|
||||
>(CONTACT_CARD_EMBEDDABLE, {})) as ContactCardEmbeddable;
|
||||
|
||||
component = mount(
|
||||
<I18nProvider>
|
||||
<EmbeddablePanel
|
||||
embeddable={embeddable}
|
||||
getActions={() => Promise.resolve([])}
|
||||
getAllEmbeddableFactories={start.getEmbeddableFactories}
|
||||
getEmbeddableFactory={start.getEmbeddableFactory}
|
||||
notifications={{} as any}
|
||||
application={applicationMock}
|
||||
overlays={{} as any}
|
||||
inspector={inspector}
|
||||
SavedObjectFinder={() => null}
|
||||
theme={theme}
|
||||
/>
|
||||
</I18nProvider>
|
||||
);
|
||||
|
||||
jest.spyOn(embeddable, 'renderError');
|
||||
});
|
||||
|
||||
test('renders a custom error', () => {
|
||||
embeddable.triggerError(new Error('something'));
|
||||
component.update();
|
||||
|
||||
const embeddableError = findTestSubject(component, 'embeddableError');
|
||||
|
||||
expect(embeddable.renderError).toHaveBeenCalledWith(
|
||||
expect.any(HTMLElement),
|
||||
new Error('something')
|
||||
);
|
||||
expect(embeddableError).toHaveProperty('length', 1);
|
||||
expect(embeddableError.text()).toBe('something');
|
||||
});
|
||||
|
||||
test('renders a custom fatal error', () => {
|
||||
embeddable.triggerError(new Error('something'), true);
|
||||
component.update();
|
||||
|
||||
const embeddableError = findTestSubject(component, 'embeddableError');
|
||||
|
||||
expect(embeddable.renderError).toHaveBeenCalledWith(
|
||||
expect.any(HTMLElement),
|
||||
new Error('something')
|
||||
);
|
||||
expect(embeddableError).toHaveProperty('length', 1);
|
||||
expect(embeddableError.text()).toBe('something');
|
||||
});
|
||||
|
||||
test('destroys previous error', () => {
|
||||
const { renderError } = embeddable as Required<typeof embeddable>;
|
||||
let destroyError: jest.MockedFunction<ReturnType<typeof renderError>>;
|
||||
|
||||
(embeddable.renderError as jest.MockedFunction<typeof renderError>).mockImplementationOnce(
|
||||
(...args) => {
|
||||
destroyError = jest.fn(renderError(...args));
|
||||
|
||||
return destroyError;
|
||||
}
|
||||
);
|
||||
embeddable.triggerError(new Error('something'));
|
||||
component.update();
|
||||
embeddable.triggerError(new Error('another error'));
|
||||
component.update();
|
||||
|
||||
const embeddableError = findTestSubject(component, 'embeddableError');
|
||||
|
||||
expect(embeddableError).toHaveProperty('length', 1);
|
||||
expect(embeddableError.text()).toBe('another error');
|
||||
expect(destroyError!).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('renders a default error', async () => {
|
||||
embeddable.renderError = undefined;
|
||||
embeddable.triggerError(new Error('something'));
|
||||
component.update();
|
||||
|
||||
const embeddableError = findTestSubject(component, 'embeddableError');
|
||||
|
||||
expect(embeddableError).toHaveProperty('length', 1);
|
||||
expect(embeddableError.children.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
const renderInEditModeAndOpenContextMenu = async (
|
||||
embeddableInputs: any,
|
||||
getActions: UiActionsStart['getTriggerCompatibleActions'] = () => Promise.resolve([])
|
||||
|
|
|
@ -40,7 +40,6 @@ import { InspectPanelAction } from './panel_header/panel_actions/inspect_panel_a
|
|||
import { EditPanelAction } from '../actions';
|
||||
import { CustomizePanelModal } from './panel_header/panel_actions/customize_title/customize_panel_modal';
|
||||
import { EmbeddableStart } from '../../plugin';
|
||||
import { EmbeddableErrorLabel } from './embeddable_error_label';
|
||||
import { EmbeddableStateTransfer, ErrorEmbeddable } from '..';
|
||||
|
||||
const sortByOrderField = (
|
||||
|
@ -104,7 +103,7 @@ interface State {
|
|||
notifications: Array<Action<EmbeddableContext>>;
|
||||
loading?: boolean;
|
||||
error?: EmbeddableError;
|
||||
errorEmbeddable?: ErrorEmbeddable;
|
||||
destroyError?(): void;
|
||||
}
|
||||
|
||||
interface InspectorPanelAction {
|
||||
|
@ -129,7 +128,8 @@ type PanelUniversalActions =
|
|||
| EmptyObject;
|
||||
|
||||
export class EmbeddablePanel extends React.Component<Props, State> {
|
||||
private embeddableRoot: React.RefObject<HTMLDivElement>;
|
||||
private embeddableRoot = React.createRef<HTMLDivElement>();
|
||||
private errorRoot = React.createRef<HTMLDivElement>();
|
||||
private parentSubscription?: Subscription;
|
||||
private subscription: Subscription = new Subscription();
|
||||
private mounted: boolean = false;
|
||||
|
@ -152,8 +152,13 @@ export class EmbeddablePanel extends React.Component<Props, State> {
|
|||
badges: [],
|
||||
notifications: [],
|
||||
};
|
||||
}
|
||||
|
||||
this.embeddableRoot = React.createRef();
|
||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||
if (this.state.error !== prevState.error) {
|
||||
prevState.destroyError?.();
|
||||
this.setState({ destroyError: this.renderError() });
|
||||
}
|
||||
}
|
||||
|
||||
private async refreshBadges() {
|
||||
|
@ -242,9 +247,8 @@ export class EmbeddablePanel extends React.Component<Props, State> {
|
|||
if (this.parentSubscription) {
|
||||
this.parentSubscription.unsubscribe();
|
||||
}
|
||||
if (this.state.errorEmbeddable) {
|
||||
this.state.errorEmbeddable.destroy();
|
||||
}
|
||||
|
||||
this.state.destroyError?.();
|
||||
this.props.embeddable.destroy();
|
||||
}
|
||||
|
||||
|
@ -258,6 +262,24 @@ export class EmbeddablePanel extends React.Component<Props, State> {
|
|||
}
|
||||
};
|
||||
|
||||
private renderError() {
|
||||
if (!this.state.error || !this.errorRoot.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.props.embeddable.renderError) {
|
||||
return this.props.embeddable.renderError(this.errorRoot.current, this.state.error);
|
||||
}
|
||||
|
||||
const errorEmbeddable = new ErrorEmbeddable(this.state.error, {
|
||||
id: this.props.embeddable.id,
|
||||
});
|
||||
|
||||
errorEmbeddable.render(this.errorRoot.current);
|
||||
|
||||
return () => errorEmbeddable.destroy();
|
||||
}
|
||||
|
||||
public render() {
|
||||
const viewOnlyMode = [ViewMode.VIEW, ViewMode.PRINT].includes(this.state.viewMode);
|
||||
const classes = classNames('embPanel', {
|
||||
|
@ -300,7 +322,13 @@ export class EmbeddablePanel extends React.Component<Props, State> {
|
|||
headerId={headerId}
|
||||
/>
|
||||
)}
|
||||
<EmbeddableErrorLabel error={this.state.error} />
|
||||
{this.state.error && (
|
||||
<div
|
||||
className="embPanel__content"
|
||||
data-test-subj="embeddableError"
|
||||
ref={this.errorRoot}
|
||||
/>
|
||||
)}
|
||||
<div className="embPanel__content" ref={this.embeddableRoot} {...contentAttrs} />
|
||||
</EuiPanel>
|
||||
);
|
||||
|
@ -317,11 +345,7 @@ export class EmbeddablePanel extends React.Component<Props, State> {
|
|||
});
|
||||
},
|
||||
(error) => {
|
||||
if (this.embeddableRoot.current) {
|
||||
const errorEmbeddable = new ErrorEmbeddable(error, { id: this.props.embeddable.id });
|
||||
errorEmbeddable.render(this.embeddableRoot.current);
|
||||
this.setState({ errorEmbeddable });
|
||||
}
|
||||
this.setState({ error });
|
||||
}
|
||||
)
|
||||
);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import React from 'react';
|
||||
import ReactDom from 'react-dom';
|
||||
import { Subscription } from 'rxjs';
|
||||
import type { ErrorLike } from '@kbn/expressions-plugin/common';
|
||||
import { UiActionsStart } from '@kbn/ui-actions-plugin/public';
|
||||
import { Container } from '../../../containers';
|
||||
import { EmbeddableOutput, Embeddable, EmbeddableInput } from '../../../embeddables';
|
||||
|
@ -76,6 +77,12 @@ export class ContactCardEmbeddable extends Embeddable<
|
|||
);
|
||||
}
|
||||
|
||||
public renderError?(node: HTMLElement, error: ErrorLike) {
|
||||
ReactDom.render(<div data-test-subj="error">{error.message}</div>, node);
|
||||
|
||||
return () => ReactDom.unmountComponentAtNode(node);
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
super.destroy();
|
||||
this.subscription.unsubscribe();
|
||||
|
@ -85,6 +92,14 @@ export class ContactCardEmbeddable extends Embeddable<
|
|||
}
|
||||
|
||||
public reload() {}
|
||||
|
||||
public triggerError(error: ErrorLike, fatal = false) {
|
||||
if (fatal) {
|
||||
this.onFatalError(error);
|
||||
} else {
|
||||
this.updateOutput({ error });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const CONTACT_USER_TRIGGER = 'CONTACT_USER_TRIGGER';
|
||||
|
|
|
@ -10,10 +10,11 @@ import _, { get } from 'lodash';
|
|||
import { Subscription } from 'rxjs';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import { EuiLoadingChart } from '@elastic/eui';
|
||||
import { Filter, onlyDisabledFiltersChanged, Query, TimeRange } from '@kbn/es-query';
|
||||
import type { KibanaExecutionContext, SavedObjectAttributes } from '@kbn/core/public';
|
||||
import type { ErrorLike } from '@kbn/expressions-plugin/common';
|
||||
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { TimefilterContract } from '@kbn/data-plugin/public';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
|
@ -393,24 +394,7 @@ export class VisualizeEmbeddable
|
|||
const { error } = this.getOutput();
|
||||
|
||||
if (error) {
|
||||
if (isFallbackDataView(this.vis.data.indexPattern)) {
|
||||
render(
|
||||
<VisualizationMissedSavedObjectError
|
||||
viewMode={this.input.viewMode ?? ViewMode.VIEW}
|
||||
renderMode={this.input.renderMode ?? 'view'}
|
||||
savedObjectMeta={{
|
||||
savedObjectId: this.vis.data.indexPattern.id,
|
||||
savedObjectType: this.vis.data.savedSearchId
|
||||
? 'search'
|
||||
: DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
}}
|
||||
application={getApplication()}
|
||||
/>,
|
||||
this.domNode
|
||||
);
|
||||
} else {
|
||||
render(<VisualizationError error={error} />, this.domNode);
|
||||
}
|
||||
this.renderError(this.domNode, error);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
@ -418,6 +402,27 @@ export class VisualizeEmbeddable
|
|||
await this.updateHandler();
|
||||
}
|
||||
|
||||
public renderError(domNode: HTMLElement, error: ErrorLike | string) {
|
||||
if (isFallbackDataView(this.vis.data.indexPattern)) {
|
||||
render(
|
||||
<VisualizationMissedSavedObjectError
|
||||
viewMode={this.input.viewMode ?? ViewMode.VIEW}
|
||||
renderMode={this.input.renderMode ?? 'view'}
|
||||
savedObjectMeta={{
|
||||
savedObjectId: this.vis.data.indexPattern.id,
|
||||
savedObjectType: this.vis.data.savedSearchId ? 'search' : DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
}}
|
||||
application={getApplication()}
|
||||
/>,
|
||||
domNode
|
||||
);
|
||||
} else {
|
||||
render(<VisualizationError error={error} />, domNode);
|
||||
}
|
||||
|
||||
return () => unmountComponentAtNode(domNode);
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
super.destroy();
|
||||
this.subscriptions.forEach((s) => s.unsubscribe());
|
||||
|
|
|
@ -3287,8 +3287,6 @@
|
|||
"embeddableApi.panel.enhancedDashboardPanelAriaLabel": "Panneau du tableau de bord : {title}",
|
||||
"embeddableApi.panel.inspectPanel.displayName": "Inspecter",
|
||||
"embeddableApi.panel.inspectPanel.untitledEmbeddableFilename": "sans titre",
|
||||
"embeddableApi.panel.labelAborted": "Annulé",
|
||||
"embeddableApi.panel.labelError": "Erreur",
|
||||
"embeddableApi.panel.optionsMenu.panelOptionsButtonAriaLabel": "Options de panneau",
|
||||
"embeddableApi.panel.optionsMenu.panelOptionsButtonAriaLabelWithIndex": "Options pour le panneau {index}",
|
||||
"embeddableApi.panel.optionsMenu.panelOptionsButtonEnhancedAriaLabel": "Options de panneau pour {title}",
|
||||
|
|
|
@ -3285,8 +3285,6 @@
|
|||
"embeddableApi.panel.enhancedDashboardPanelAriaLabel": "ダッシュボードパネル:{title}",
|
||||
"embeddableApi.panel.inspectPanel.displayName": "検査",
|
||||
"embeddableApi.panel.inspectPanel.untitledEmbeddableFilename": "無題",
|
||||
"embeddableApi.panel.labelAborted": "中断しました",
|
||||
"embeddableApi.panel.labelError": "エラー",
|
||||
"embeddableApi.panel.optionsMenu.panelOptionsButtonAriaLabel": "パネルオプション",
|
||||
"embeddableApi.panel.optionsMenu.panelOptionsButtonAriaLabelWithIndex": "パネル{index}のオプション",
|
||||
"embeddableApi.panel.optionsMenu.panelOptionsButtonEnhancedAriaLabel": "{title} のパネルオプション",
|
||||
|
|
|
@ -3289,8 +3289,6 @@
|
|||
"embeddableApi.panel.enhancedDashboardPanelAriaLabel": "仪表板面板:{title}",
|
||||
"embeddableApi.panel.inspectPanel.displayName": "检查",
|
||||
"embeddableApi.panel.inspectPanel.untitledEmbeddableFilename": "未命名",
|
||||
"embeddableApi.panel.labelAborted": "已中止",
|
||||
"embeddableApi.panel.labelError": "错误",
|
||||
"embeddableApi.panel.optionsMenu.panelOptionsButtonAriaLabel": "面板选项",
|
||||
"embeddableApi.panel.optionsMenu.panelOptionsButtonAriaLabelWithIndex": "面板 {index} 的选项",
|
||||
"embeddableApi.panel.optionsMenu.panelOptionsButtonEnhancedAriaLabel": "{title} 的面板选项",
|
||||
|
|
|
@ -36,7 +36,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.loadSavedDashboard('Not Delayed');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await testSubjects.missingOrFail('embeddableErrorLabel');
|
||||
await testSubjects.missingOrFail('embeddableError');
|
||||
await enableNewChartLibraryDebug();
|
||||
const data = await PageObjects.visChart.getBarChartData(xyChartSelector, 'Sum of bytes');
|
||||
expect(data.length).to.be(5);
|
||||
|
@ -46,7 +46,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.loadSavedDashboard('Delayed 5s');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await testSubjects.missingOrFail('embeddableErrorLabel');
|
||||
await testSubjects.missingOrFail('embeddableError');
|
||||
await enableNewChartLibraryDebug();
|
||||
const data = await PageObjects.visChart.getBarChartData(xyChartSelector, 'Sum of bytes');
|
||||
expect(data.length).to.be(5);
|
||||
|
@ -56,7 +56,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.loadSavedDashboard('Delayed 15s');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await testSubjects.existOrFail('embeddableErrorLabel');
|
||||
await testSubjects.existOrFail('embeddableError');
|
||||
await testSubjects.existOrFail('searchTimeoutError');
|
||||
});
|
||||
|
||||
|
@ -64,9 +64,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.loadSavedDashboard('Multiple delayed');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await testSubjects.existOrFail('embeddableErrorLabel');
|
||||
await testSubjects.existOrFail('embeddableError');
|
||||
// there should be two failed panels
|
||||
expect((await testSubjects.findAll('embeddableErrorLabel')).length).to.be(2);
|
||||
expect((await testSubjects.findAll('embeddableError')).length).to.be(2);
|
||||
// but only single error toast because searches are grouped
|
||||
expect((await testSubjects.findAll('searchTimeoutError')).length).to.be(1);
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await browser.get(savedSessionURL);
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await searchSessions.expectState('restored');
|
||||
await testSubjects.existOrFail('embeddableErrorLabel'); // expected that panel errors out because of non existing session
|
||||
await testSubjects.existOrFail('embeddableError'); // expected that panel errors out because of non existing session
|
||||
|
||||
const session1 = await dashboardPanelActions.getSearchSessionIdByTitle(
|
||||
'Sum of Bytes by Extension'
|
||||
|
@ -56,7 +56,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await queryBar.clickQuerySubmitButton();
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await searchSessions.expectState('completed');
|
||||
await testSubjects.missingOrFail('embeddableErrorLabel');
|
||||
await testSubjects.missingOrFail('embeddableError');
|
||||
const session2 = await dashboardPanelActions.getSearchSessionIdByTitle(
|
||||
'Sum of Bytes by Extension'
|
||||
);
|
||||
|
@ -97,7 +97,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
// Check that session is restored
|
||||
await searchSessions.expectState('restored');
|
||||
await testSubjects.missingOrFail('embeddableErrorLabel');
|
||||
await testSubjects.missingOrFail('embeddableError');
|
||||
|
||||
// switching dashboard to edit mode (or any other non-fetch required) state change
|
||||
// should leave session state untouched
|
||||
|
|
|
@ -85,7 +85,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
async function checkSampleDashboardLoaded(visualizationContainer?: string) {
|
||||
log.debug('Checking no error labels');
|
||||
await testSubjects.missingOrFail('embeddableErrorLabel');
|
||||
await testSubjects.missingOrFail('embeddableError');
|
||||
log.debug('Checking charts rendered');
|
||||
await elasticChart.waitForRenderComplete(visualizationContainer ?? 'lnsVisualizationContainer');
|
||||
log.debug('Checking saved searches rendered');
|
||||
|
|
|
@ -115,7 +115,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
// Check that session is restored
|
||||
await searchSessions.expectState('restored');
|
||||
await testSubjects.missingOrFail('embeddableErrorLabel');
|
||||
await testSubjects.missingOrFail('embeddableError');
|
||||
expect(await toasts.getToastCount()).to.be(0); // no session restoration related warnings
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue