Skip to content

Commit

Permalink
Added unit testing for unsaved listing
Browse files Browse the repository at this point in the history
  • Loading branch information
ThomThomson committed Jan 14, 2021
1 parent 25da78c commit ea9162d
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,21 @@
* under the License.
*/

import { EUI_MODAL_CANCEL_BUTTON } from '@elastic/eui';
import {
EuiButton,
EuiButtonEmpty,
EuiModal,
EuiModalBody,
EuiModalFooter,
EuiModalHeader,
EuiModalHeaderTitle,
EuiText,
EUI_MODAL_CANCEL_BUTTON,
} from '@elastic/eui';
import React from 'react';
import { OverlayStart } from '../../../../../core/public';
import { createConfirmStrings, leaveConfirmStrings } from '../../dashboard_strings';
import { toMountPoint } from '../../services/kibana_react';

export const confirmDiscardUnsavedChanges = (overlays: OverlayStart, discardCallback: () => void) =>
overlays
Expand All @@ -40,17 +52,45 @@ export const confirmCreateWithUnsaved = (
overlays: OverlayStart,
startBlankCallback: () => void,
contineCallback: () => void
) =>
overlays
.openConfirm(createConfirmStrings.getCreateSubtitle(), {
confirmButtonText: createConfirmStrings.getConfirmButtonText(),
cancelButtonText: createConfirmStrings.getCancelButtonText(),
defaultFocusedButton: EUI_MODAL_CANCEL_BUTTON,
title: createConfirmStrings.getCreateTitle(),
})
.then((isConfirmed) => {
if (isConfirmed) {
startBlankCallback();
}
contineCallback();
});
) => {
const session = overlays.openModal(
toMountPoint(
<EuiModal onClose={() => session.close()}>
<EuiModalHeader>
<EuiModalHeaderTitle>{createConfirmStrings.getCreateTitle()}</EuiModalHeaderTitle>
</EuiModalHeader>

<EuiModalBody>
<EuiText>{createConfirmStrings.getCreateSubtitle()}</EuiText>
</EuiModalBody>

<EuiModalFooter>
<EuiButtonEmpty onClick={() => session.close()}>
{createConfirmStrings.getCancelButtonText()}
</EuiButtonEmpty>
<EuiButtonEmpty
color="danger"
onClick={() => {
startBlankCallback();
session.close();
}}
>
{createConfirmStrings.getStartOverButtonText()}
</EuiButtonEmpty>
<EuiButton
fill
onClick={() => {
contineCallback();
session.close();
}}
>
{createConfirmStrings.getContinueButtonText()}
</EuiButton>
</EuiModalFooter>
</EuiModal>
),
{
'data-test-subj': 'dashboardCreateConfirmModal',
}
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { I18nProvider } from '@kbn/i18n/react';
import { findTestSubject } from '@elastic/eui/lib/test';
import { waitFor } from '@testing-library/react';
import { mount } from 'enzyme';
import React from 'react';
import { DashboardSavedObject } from '../..';
import { coreMock } from '../../../../../core/public/mocks';
import { KibanaContextProvider } from '../../services/kibana_react';
import { SavedObjectLoader } from '../../services/saved_objects';
import { DashboardPanelStorage } from '../lib';
import { DASHBOARD_PANELS_UNSAVED_ID } from '../lib/dashboard_panel_storage';
import { DashboardAppServices, DashboardRedirect } from '../types';
import { DashboardUnsavedListing } from './dashboard_unsaved_listing';

const mockedDashboards: { [key: string]: DashboardSavedObject } = {
dashboardUnsavedOne: {
id: `dashboardUnsavedOne`,
title: `Dashboard Unsaved One`,
} as DashboardSavedObject,
dashboardUnsavedTwo: {
id: `dashboardUnsavedTwo`,
title: `Dashboard Unsaved Two`,
} as DashboardSavedObject,
dashboardUnsavedThree: {
id: `dashboardUnsavedThree`,
title: `Dashboard Unsaved Three`,
} as DashboardSavedObject,
};

function makeDefaultServices(): DashboardAppServices {
const core = coreMock.createStart();
core.overlays.openConfirm = jest.fn().mockResolvedValue(true);
const savedDashboards = {} as SavedObjectLoader;
savedDashboards.get = jest.fn().mockImplementation((id: string) => mockedDashboards[id]);
const dashboardPanelStorage = {} as DashboardPanelStorage;
dashboardPanelStorage.clearPanels = jest.fn();
dashboardPanelStorage.getDashboardIdsWithUnsavedChanges = jest
.fn()
.mockImplementation(() => [
'dashboardUnsavedOne',
'dashboardUnsavedTwo',
'dashboardUnsavedThree',
]);
return ({
dashboardPanelStorage,
savedDashboards,
core,
} as unknown) as DashboardAppServices;
}

const makeDefaultProps = () => ({ redirectTo: jest.fn() });

function mountWith({
services: incomingServices,
props: incomingProps,
}: {
services?: DashboardAppServices;
props?: { redirectTo: DashboardRedirect };
}) {
const services = incomingServices ?? makeDefaultServices();
const props = incomingProps ?? makeDefaultProps();
const wrappingComponent: React.FC<{
children: React.ReactNode;
}> = ({ children }) => {
return (
<I18nProvider>
<KibanaContextProvider services={services}>{children}</KibanaContextProvider>
</I18nProvider>
);
};
const component = mount(<DashboardUnsavedListing {...props} />, { wrappingComponent });
return { component, props, services };
}

describe('Unsaved listing', () => {
it('Gets information for each unsaved dashboard', async () => {
const { services } = mountWith({});
await waitFor(() => {
expect(services.savedDashboards.get).toHaveBeenCalledTimes(3);
});
});

it('Does not attempt to get unsaved dashboard id', async () => {
const services = makeDefaultServices();
services.dashboardPanelStorage.getDashboardIdsWithUnsavedChanges = jest
.fn()
.mockImplementation(() => ['dashboardUnsavedOne', DASHBOARD_PANELS_UNSAVED_ID]);
mountWith({ services });
await waitFor(() => {
expect(services.savedDashboards.get).toHaveBeenCalledTimes(1);
});
});

it('Redirects to the requested dashboard in edit mode when continue editing clicked', async () => {
const { props, component } = mountWith({});
await waitFor(() => {
component.update();
const editButton = findTestSubject(component, 'edit-unsaved-dashboardUnsavedOne');
editButton.simulate('click');
component.update();
expect(props.redirectTo).toHaveBeenCalledWith({
destination: 'dashboard',
id: 'dashboardUnsavedOne',
editMode: true,
});
});
});

it('Redirects to new dashboard when continue editing clicked', async () => {
const services = makeDefaultServices();
services.dashboardPanelStorage.getDashboardIdsWithUnsavedChanges = jest
.fn()
.mockImplementation(() => [DASHBOARD_PANELS_UNSAVED_ID]);
const { props, component } = mountWith({ services });
await waitFor(() => {
component.update();
const editButton = findTestSubject(component, `edit-unsaved-${DASHBOARD_PANELS_UNSAVED_ID}`);
editButton.simulate('click');
component.update();
expect(props.redirectTo).toHaveBeenCalledWith({
destination: 'dashboard',
id: undefined,
editMode: true,
});
});
});

it('Shows a warning then clears changes when delete unsaved changes is pressed', async () => {
const { services, component } = mountWith({});
await waitFor(() => {
component.update();
const discardButton = findTestSubject(component, 'discard-unsaved-dashboardUnsavedOne');
discardButton.simulate('click');
component.update();
expect(services.core.overlays.openConfirm).toHaveBeenCalled();
expect(services.dashboardPanelStorage.clearPanels).toHaveBeenCalledWith(
'dashboardUnsavedOne'
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,23 @@ const DashboardUnsavedItem = ({
</EuiTitle>
<EuiFlexGroup gutterSize="s" alignItems="flexStart" className="dshUnsavedListingButtons">
<EuiFlexItem grow={false}>
<EuiButtonEmpty flush="left" size="xs" color="primary" onClick={onOpenClick}>
<EuiButtonEmpty
flush="left"
size="xs"
color="primary"
onClick={onOpenClick}
data-test-subj={`edit-unsaved-${dashboard?.id ?? DASHBOARD_PANELS_UNSAVED_ID}`}
>
{dashboardUnsavedListingStrings.getEditTitle()}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty size="xs" color="danger" onClick={onDiscardClick}>
<EuiButtonEmpty
size="xs"
color="danger"
onClick={onDiscardClick}
data-test-subj={`discard-unsaved-${dashboard?.id ?? DASHBOARD_PANELS_UNSAVED_ID}`}
>
{dashboardUnsavedListingStrings.getDiscardTitle()}
</EuiButtonEmpty>
</EuiFlexItem>
Expand Down
5 changes: 3 additions & 2 deletions src/plugins/dashboard/public/dashboard_strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,14 @@ export const createConfirmStrings = {
i18n.translate('dashboard.createConfirmModal.unsavedChangesSubtitle', {
defaultMessage: 'You can continue editing or start with a blank dashboard.',
}),
getConfirmButtonText: () =>
getStartOverButtonText: () =>
i18n.translate('dashboard.createConfirmModal.confirmButtonLabel', {
defaultMessage: 'Start over',
}),
getContinueButtonText: () => leaveConfirmStrings.getCancelButtonText(),
getCancelButtonText: () =>
i18n.translate('dashboard.createConfirmModal.cancelButtonLabel', {
defaultMessage: 'Continue editing',
defaultMessage: 'Cancel',
}),
};

Expand Down

0 comments on commit ea9162d

Please sign in to comment.