Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 48 additions & 10 deletions packages/compass-crud/src/components/bulk-actions-toasts.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from './bulk-actions-toasts';
import { expect } from 'chai';
import sinon from 'sinon';
import { MongoNetworkError } from 'mongodb';

function renderToastPortal() {
return render(<ToastArea></ToastArea>);
Expand Down Expand Up @@ -67,30 +68,48 @@ describe('Bulk Action Toasts', function () {
{
modal: openBulkDeleteFailureToast,
affected: undefined,
expected: 'The delete operation failed.',
error: new Error('Test error'),
expected: ['The delete operation failed.', 'Test error'],
},
{
modal: openBulkDeleteFailureToast,
affected: 1,
expected: '1 document could not been deleted.',
error: new Error('Another test error'),
expected: ['1 document could not be deleted.', 'Another test error'],
},
{
modal: openBulkDeleteFailureToast,
affected: 2,
expected: '2 documents could not been deleted.',
error: new Error('Another failure'),
expected: ['2 documents could not be deleted.', 'Another failure'],
},
{
modal: openBulkDeleteFailureToast,
affected: 2,
error: new MongoNetworkError('Connection lost'),
expected: [
'Delete operation - network error occurred.',
'Connection lost',
],
},
];

for (const useCase of USE_CASES) {
it(`${useCase.modal.name} shows the text '${useCase.expected}' when affected document/s is/are '${useCase.affected}'`, async function () {
useCase.modal({
affectedDocuments: useCase.affected,
error: useCase.error as Error,
onRefresh: () => {},
});

await waitFor(async function () {
const node = await screen.findByText(useCase.expected);
expect(node).to.exist;
if (!Array.isArray(useCase.expected)) {
expect(await screen.findByText(useCase.expected)).to.exist;
} else {
for (const expectedText of useCase.expected) {
expect(await screen.findByText(expectedText)).to.exist;
}
}
});
});
}
Expand Down Expand Up @@ -160,30 +179,49 @@ describe('Bulk Action Toasts', function () {
{
modal: openBulkUpdateFailureToast,
affected: undefined,
expected: 'The update operation failed.',
error: new Error('Test error'),
expected: ['The update operation failed.', 'Test error'],
},
{
modal: openBulkUpdateFailureToast,
affected: 1,
expected: '1 document could not been updated.',
error: new Error('Could not update'),
expected: ['1 document could not be updated.', 'Could not update'],
},
{
modal: openBulkUpdateFailureToast,
affected: 2,
expected: '2 documents could not been updated.',
error: new Error('Update failed'),
expected: ['2 documents could not be updated.', 'Update failed'],
},

{
modal: openBulkUpdateFailureToast,
affected: 2,
error: new MongoNetworkError('Connection lost'),
expected: [
'Update operation - network error occurred.',
'Connection lost',
],
},
];

for (const useCase of USE_CASES) {
it(`${useCase.modal.name} shows the text '${useCase.expected}' when ${useCase.affected} document/s affected`, async function () {
useCase.modal({
affectedDocuments: useCase.affected,
error: useCase.error,
onRefresh: () => {},
});

await waitFor(async function () {
const node = await screen.findByText(useCase.expected);
expect(node).to.exist;
if (!Array.isArray(useCase.expected)) {
expect(await screen.findByText(useCase.expected)).to.exist;
} else {
for (const expectedText of useCase.expected) {
expect(await screen.findByText(expectedText)).to.exist;
}
}
});
});
}
Expand Down
77 changes: 35 additions & 42 deletions packages/compass-crud/src/components/bulk-actions-toasts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
closeToast,
ToastBody,
} from '@mongodb-js/compass-components';
import { MongoNetworkError } from 'mongodb';

type BulkDeleteSuccessToastProps = {
affectedDocuments?: number;
Expand Down Expand Up @@ -72,33 +73,48 @@ export function openBulkDeleteProgressToast({
});
}

type BulkDeleteFailureToastProps = {
type BulkOperationFailureToastProps = {
affectedDocuments?: number;
error: Error;
type: 'delete' | 'update';
};

export function openBulkDeleteFailureToast({
const isNetworkError = (error: Error) => error instanceof MongoNetworkError;

export function openBulkOperationFailureToast({
affectedDocuments,
}: BulkDeleteFailureToastProps): void {
let text;
switch (affectedDocuments) {
case undefined:
text = 'The delete operation failed.';
break;
case 1:
text = `${affectedDocuments} document could not been deleted.`;
break;
default:
text = `${affectedDocuments} documents could not been deleted.`;
error,
type,
}: BulkOperationFailureToastProps): void {
let title: string;
if (isNetworkError(error)) {
title = `${
type === 'delete' ? 'Delete' : 'Update'
} operation - network error occurred.`;
} else if (affectedDocuments === undefined) {
title = `The ${type} operation failed.`;
} else if (affectedDocuments === 1) {
title = `${affectedDocuments} document could not be ${
type === 'delete' ? 'deleted' : 'updated'
}.`;
} else {
title = `${affectedDocuments} documents could not be ${
type === 'delete' ? 'deleted' : 'updated'
}.`;
}

openToast('bulk-delete-toast', {
title: '',
openToast(`bulk-${type}-toast`, {
title,
variant: 'warning',
dismissible: true,
description: <ToastBody statusMessage={text} />,
description: <ToastBody statusMessage={error.message} />,
});
}

export const openBulkDeleteFailureToast = (
props: Omit<BulkOperationFailureToastProps, 'type'>
): void => openBulkOperationFailureToast({ ...props, type: 'delete' });

type BulkUpdateSuccessToastProps = {
affectedDocuments?: number;
onRefresh: () => void;
Expand Down Expand Up @@ -166,29 +182,6 @@ export function openBulkUpdateProgressToast({
});
}

type BulkUpdateFailureToastProps = {
affectedDocuments?: number;
};

export function openBulkUpdateFailureToast({
affectedDocuments,
}: BulkUpdateFailureToastProps): void {
let text;
switch (affectedDocuments) {
case undefined:
text = 'The update operation failed.';
break;
case 1:
text = `${affectedDocuments} document could not been updated.`;
break;
default:
text = `${affectedDocuments} documents could not been updated.`;
}

openToast('bulk-update-toast', {
title: '',
variant: 'warning',
dismissible: true,
description: <ToastBody statusMessage={text} />,
});
}
export const openBulkUpdateFailureToast = (
props: Omit<BulkOperationFailureToastProps, 'type'>
): void => openBulkOperationFailureToast({ ...props, type: 'update' });
4 changes: 3 additions & 1 deletion packages/compass-crud/src/stores/crud-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { Listenable, Store } from 'reflux';
import Reflux from 'reflux';
import toNS from 'mongodb-ns';
import { findIndex, isEmpty, isEqual } from 'lodash';
import type { MongoServerError } from 'mongodb';
import semver from 'semver';
import StateMixin from '@mongodb-js/reflux-state-mixin';
import type { Element } from 'hadron-document';
Expand Down Expand Up @@ -69,6 +68,7 @@ import type {
} from '@mongodb-js/compass-connections/provider';
import type { Query, QueryBarService } from '@mongodb-js/compass-query-bar';
import type { TrackFunction } from '@mongodb-js/compass-telemetry';
import type { MongoServerError } from 'mongodb';

export type BSONObject = TypeCastMap['Object'];
export type BSONArray = TypeCastMap['Array'];
Expand Down Expand Up @@ -1243,6 +1243,7 @@ class CrudStoreImpl
} catch (err: any) {
openBulkUpdateFailureToast({
affectedDocuments: this.state.bulkUpdate.affected,
error: err as Error,
});

this.logger.log.error(
Expand Down Expand Up @@ -1899,6 +1900,7 @@ class CrudStoreImpl
bulkDeleteFailed(ex: Error) {
openBulkDeleteFailureToast({
affectedDocuments: this.state.bulkDelete.affected,
error: ex,
});

this.logger.log.error(
Expand Down
Loading