Skip to content

Commit

Permalink
Test Merge Duplicates error state and move mutations into helper file
Browse files Browse the repository at this point in the history
  • Loading branch information
caleballdrin committed Jun 29, 2024
1 parent 13e6070 commit 25f26e1
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 227 deletions.
67 changes: 62 additions & 5 deletions src/components/Tool/MergeContacts/MergeContacts.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { ThemeProvider } from '@mui/material/styles';
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ApolloErgonoMockMap } from 'graphql-ergonomock';
import { SnackbarProvider } from 'notistack';
import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
Expand Down Expand Up @@ -30,18 +31,20 @@ jest.mock('notistack', () => ({

interface MergeContactsWrapperProps {
mutationSpy?: () => void;
mocks?: ApolloErgonoMockMap;
}

const MergeContactsWrapper: React.FC<MergeContactsWrapperProps> = ({
mutationSpy,
mocks = getContactDuplicatesMocks,
}) => {
return (
<ThemeProvider theme={theme}>
<TestRouter>
<GqlMockedProvider<{
GetContactDuplicates: GetContactDuplicatesQuery;
}>
mocks={getContactDuplicatesMocks}
mocks={mocks}
onCall={mutationSpy}
>
<ContactsProvider
Expand Down Expand Up @@ -80,7 +83,16 @@ describe('Tools - MergeContacts', () => {

const { getByText, queryAllByTestId, findByText, getByRole } = render(
<SnackbarProvider>
<MergeContactsWrapper mutationSpy={mutationSpy} />
<MergeContactsWrapper
mutationSpy={mutationSpy}
mocks={{
GetContactDuplicates:
getContactDuplicatesMocks.GetContactDuplicates,
MassActionsMerge: () => {
return { mergeContacts: ['contact-2'] };
},
}}
/>
</SnackbarProvider>,
);

Expand All @@ -90,15 +102,24 @@ describe('Tools - MergeContacts', () => {
const confirmButton = getByRole('button', { name: 'Confirm and Continue' });

expect(confirmButton).toBeDisabled();
userEvent.click(getByText('123 John St Orlando, FL 32832'));
userEvent.click(queryAllByTestId('ignoreButton')[1]);
userEvent.click(
getByText('(contact-2 address) 123 John St Orlando, FL 32832'),
);
userEvent.click(
getByText('(contact-1 address) 123 Main St Orlando, FL 32832'),
);
userEvent.click(
getByText('(contact-2 address) 123 John St Orlando, FL 32832'),
);
expect(await findByText('Use this one')).toBeInTheDocument();
expect(
getByRole('button', { name: 'Confirm and Continue' }),
).not.toBeDisabled();

userEvent.click(getByRole('button', { name: 'Confirm and Continue' }));
await waitFor(() =>
expect(mockEnqueue).toHaveBeenCalledWith('Updated 1 duplicate(s)', {
expect(mockEnqueue).toHaveBeenCalledWith('Updated 2 duplicate(s)', {
variant: 'success',
}),
);
Expand All @@ -111,8 +132,8 @@ describe('Tools - MergeContacts', () => {
input: {
winnersAndLosers: [
{
loserId: 'contact-1',
winnerId: 'contact-2',
loserId: 'contact-1',
},
],
},
Expand Down Expand Up @@ -178,6 +199,42 @@ describe('Tools - MergeContacts', () => {
).toBeInTheDocument();
});

it('should show error', async () => {
const mutationSpy = jest.fn();

const { queryAllByTestId, getByRole } = render(
<SnackbarProvider>
<MergeContactsWrapper
mutationSpy={mutationSpy}
mocks={{
GetContactDuplicates:
getContactDuplicatesMocks.GetContactDuplicates,
UpdateDuplicate: () => {
throw new Error('Server Error');
},
MassActionsMerge: () => {
throw new Error('Server Error');
},
}}
/>
</SnackbarProvider>,
);
await waitFor(() =>
expect(queryAllByTestId('MergeContactPair')).toHaveLength(2),
);
userEvent.click(queryAllByTestId('ignoreButton')[0]);

userEvent.click(getByRole('button', { name: 'Confirm and Continue' }));
await waitFor(() =>
expect(mockEnqueue).toHaveBeenCalledWith(
'Failed to update 1 duplicate(s)',
{
variant: 'error',
},
),
);
});

describe('setContactFocus()', () => {
it('should open up contact details', async () => {
const mutationSpy = jest.fn();
Expand Down
118 changes: 11 additions & 107 deletions src/components/Tool/MergeContacts/MergeContacts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import NoData from '../NoData';
import ContactPair from './ContactPair';
import { useGetContactDuplicatesQuery } from './GetContactDuplicates.generated';
import { StickyConfirmButtons } from './StickyConfirmButtons';
import { bulkUpdateDuplicates } from './mergeDuplicatesHelper';

const useStyles = makeStyles()(() => ({
container: {
Expand Down Expand Up @@ -100,112 +101,15 @@ const MergeContacts: React.FC<Props> = ({
}
};

const handleBulkUpdateDuplicates = async () => {
try {
const callsByDuplicate: (() => Promise<{ success: boolean }>)[] = [];

const mergeActions = Object.entries(actions).filter(
(action) => action[1].action === 'merge',
);

if (mergeActions.length) {
const winnersAndLosers: { winnerId: string; loserId: string }[] =
mergeActions.map((action) => {
return { winnerId: action[0], loserId: action[1].mergeId || '' };
});

const callMergeDuplicatesMutation = () =>
mergeDuplicates(winnersAndLosers);
callsByDuplicate.push(callMergeDuplicatesMutation);
}

const duplicatesToIgnore = Object.entries(actions)
.filter((action) => action[1].action === 'ignore')
.map((action) => action[0]);

if (duplicatesToIgnore.length) {
duplicatesToIgnore.forEach((duplicateId) => {
const callIgnoreDuplicateMutation = () =>
ignoreDuplicates(duplicateId);
callsByDuplicate.push(callIgnoreDuplicateMutation);
});
}

if (callsByDuplicate.length) {
const results = await Promise.all(
callsByDuplicate.map((call) => call()),
);

const failedUpdates = results.filter(
(result) => !result.success,
).length;
const successfulUpdates = results.length - failedUpdates;

if (successfulUpdates) {
enqueueSnackbar(t(`Updated ${successfulUpdates} duplicate(s)`), {
variant: 'success',
});
}
if (failedUpdates) {
enqueueSnackbar(
t(`Error when updating ${failedUpdates} duplicate(s)`),
{
variant: 'error',
},
);
}
} else {
enqueueSnackbar(t(`No duplicates were updated`), {
variant: 'warning',
});
}
} catch (error) {
enqueueSnackbar(t(`Error updating duplicates`), { variant: 'error' });
}
};

const ignoreDuplicates = async (duplicateId: string) => {
await updateDuplicates({
variables: {
input: {
attributes: {
ignore: true,
},
type: TypeEnum.Contact,
id: duplicateId,
},
},
update: (cache) => {
// Delete the duplicate
cache.evict({ id: `ContactDuplicate:${duplicateId}` });
cache.gc();
},
onError: () => {
return { success: false };
},
});
return { success: true };
};

const mergeDuplicates = async (winnersAndLosers) => {
await contactsMerge({
variables: {
input: {
winnersAndLosers,
},
},
update: (cache) => {
// Delete the contacts and remove dangling references to them
winnersAndLosers.forEach((contact) => {
cache.evict({ id: `Contact:${contact.loserId}` });
});
cache.gc();
},
onError: () => {
return { success: false };
},
});
return { success: true };
const handleSubmit = () => {
bulkUpdateDuplicates(
TypeEnum.Contact,
actions,
contactsMerge,
updateDuplicates,
enqueueSnackbar,
t,
);
};

return (
Expand Down Expand Up @@ -251,7 +155,7 @@ const MergeContacts: React.FC<Props> = ({
duplicatesDisplayedCount={duplicatesDisplayedCount}
disabled={disabled}
totalCount={totalCount}
confirmAction={handleBulkUpdateDuplicates}
confirmAction={handleSubmit}
setActions={setActions}
/>
<Grid item xs={12} sx={{ margin: '0px 2px 20px 2px' }}>
Expand Down
4 changes: 2 additions & 2 deletions src/components/Tool/MergeContacts/MergeContactsMock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const getContactDuplicatesMocks = {
status: null,
primaryAddress: {
id: 'address-1',
street: '123 Main St',
street: '(contact-1 address) 123 Main St',
city: 'Orlando',
state: 'FL',
postalCode: '32832',
Expand All @@ -30,7 +30,7 @@ export const getContactDuplicatesMocks = {
status: null,
primaryAddress: {
id: 'address-1',
street: '123 John St',
street: '(contact-2 address) 123 John St',
city: 'Orlando',
state: 'FL',
postalCode: '32832',
Expand Down
Loading

0 comments on commit 25f26e1

Please sign in to comment.