Skip to content

Commit

Permalink
Merge pull request #1216 from CruGlobal/hs-1265656-only-delete-MPDX-s…
Browse files Browse the repository at this point in the history
…ourced-contacts

MPDX-8488 - Prevent users from deleting Siebel contacts
  • Loading branch information
dr-bizz authored Dec 11, 2024
2 parents 321d8db + 33966f8 commit 0c76edd
Show file tree
Hide file tree
Showing 18 changed files with 616 additions and 37 deletions.
132 changes: 116 additions & 16 deletions .pnp.cjs

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@
"@types/testing-library__jest-dom": "^5.14.5",
"@types/uuid": "^9.0.1",
"@typescript-eslint/eslint-plugin": "^7.5.0",
"@typescript-eslint/parser": "^7.5.0",
"@typescript-eslint/parser": "^8.17.0",
"concurrently": "^8.2.2",
"cross-env": "^7.0.3",
"css-mediaquery": "^0.1.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ export const ContactDetailsMoreAcitions: React.FC<
setOpen={setDeleteModalOpen}
deleting={deleting}
deleteContact={handleDeleteContact}
contactId={contactId}
/>
)}
{openHideModal && (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
query ContactSource($accountListId: ID!, $contactId: ID!) {
contact(accountListId: $accountListId, id: $contactId) {
id
source
addresses {
nodes {
id
source
}
}
people {
nodes {
id
emailAddresses {
nodes {
id
source
}
}
phoneNumbers {
nodes {
id
source
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
import React from 'react';
import { ThemeProvider } from '@mui/material/styles';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterLuxon } from '@mui/x-date-pickers/AdapterLuxon';
import { render, waitFor } from '@testing-library/react';
import { SnackbarProvider } from 'notistack';
import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
import { ContactSourceEnum } from 'src/graphql/types.generated';
import theme from 'src/theme';
import { ContactSourceQuery } from './ContactSource.generated';
import { DeleteContactModal } from './DeleteContactModal';

const contactId = 'contact-id';
const mutationSpy = jest.fn();
const setOpen = jest.fn();
const deleteContact = jest.fn();

interface TestComponentProps {
open?: boolean;
deleting?: boolean;
contactSource?: ContactSourceEnum;
addressSources?: string[];
emailSources?: string[];
phoneSources?: string[];
}

const TestComponent: React.FC<TestComponentProps> = ({
open = true,
deleting = false,
contactSource = ContactSourceEnum.Mpdx,
addressSources = [],
emailSources = [],
phoneSources = [],
}) => (
<SnackbarProvider>
<LocalizationProvider dateAdapter={AdapterLuxon}>
<TestRouter router={{ query: { accountListId: 'accountListId' } }}>
<ThemeProvider theme={theme}>
<GqlMockedProvider<{
ContactSource: ContactSourceQuery;
}>
mocks={{
ContactSource: {
contact: {
id: contactId,
source: contactSource,
addresses: {
nodes: addressSources.map((source) => ({ source })),
},
people: {
nodes: [
{
emailAddresses: {
nodes: emailSources.map((source) => ({ source })),
},
phoneNumbers: {
nodes: phoneSources.map((source) => ({ source })),
},
},
{
emailAddresses: {
nodes: emailSources.map((source) => ({ source })),
},
phoneNumbers: {
nodes: phoneSources.map((source) => ({ source })),
},
},
],
},
},
},
}}
onCall={mutationSpy}
>
<DeleteContactModal
open={open}
setOpen={setOpen}
deleting={deleting}
deleteContact={deleteContact}
contactId={contactId}
/>
</GqlMockedProvider>
</ThemeProvider>
</TestRouter>
</LocalizationProvider>
</SnackbarProvider>
);

describe('DeleteContactModal', () => {
it('should not show modal if not open', () => {
const { queryByText } = render(<TestComponent open={false} />);

expect(
queryByText(/Are you sure you want to permanently delete this contact?/),
).not.toBeInTheDocument();
});

it('should be able to delete contact', async () => {
const { getByText, getByRole } = render(<TestComponent />);

await waitFor(() => {
expect(mutationSpy).toHaveGraphqlOperation('ContactSource');
});

expect(
getByText(/Are you sure you want to permanently delete this contact?/),
).toBeInTheDocument();

expect(getByRole('button', { name: 'delete contact' })).toBeInTheDocument();
});

it('should prevent user from deleting contact while currently deleting contact', async () => {
const { getByRole } = render(<TestComponent deleting={true} />);

await waitFor(() => {
expect(mutationSpy).toHaveGraphqlOperation('ContactSource');
});

expect(getByRole('button', { name: 'delete contact' })).toBeDisabled();
});

describe('Show third party message', () => {
interface TestProps {
testName: string;
props: TestComponentProps;
}
const tests: TestProps[] = [
{
testName: 'disables deletion if contact created by third party',
props: { contactSource: ContactSourceEnum.GiveSite },
},
{
testName:
"disables deletion if a contact's address is sourced by a third party",
props: {
addressSources: ['Siebel', 'MPDX'],
},
},
{
testName:
"disables deletion if a contact's phone or email is sourced by a third party",
props: {
emailSources: ['Siebel', 'MPDX'],
phoneSources: ['Siebel', 'MPDX'],
},
},
{
testName:
"disables deletion if only one contact's phone number is sourced by a third party",
props: {
emailSources: ['MPDX', 'MPDX'],
phoneSources: ['MPDX', 'Siebel'],
},
},
];

test.each(tests)('$testName', async ({ props }) => {
const { findByText, getByRole } = render(<TestComponent {...props} />);

expect(
await findByText(
/its data may sync with Donation Services or other third-party systems/,
),
).toBeInTheDocument();

expect(
getByRole('button', { name: 'delete contact' }),
).toBeInTheDocument();
});

it('should show third party source for contact', async () => {
const { findByText } = render(
<TestComponent
contactSource={ContactSourceEnum.GiveSite}
addressSources={['Siebel', 'MPDX']}
emailSources={['Siebel', 'MPDX']}
phoneSources={['Siebel', 'MPDX']}
/>,
);

expect(await findByText('Contact: GIVE_SITE')).toBeInTheDocument();
expect(
await findByText('Address: US Donation Services'),
).toBeInTheDocument();
expect(
await findByText('Email: US Donation Services'),
).toBeInTheDocument();
expect(
await findByText('Phone: US Donation Services'),
).toBeInTheDocument();
});
});

describe('Show normal delete message', () => {
it('should show modal and be able to delete user', async () => {
const { getByText, getByRole } = render(
<TestComponent
contactSource={ContactSourceEnum.Mpdx}
addressSources={['MPDX', 'MPDX']}
emailSources={['MPDX', 'MPDX']}
phoneSources={['MPDX', 'MPDX']}
/>,
);

await waitFor(() => {
expect(mutationSpy).toHaveGraphqlOperation('ContactSource');
});

expect(
getByText(/Are you sure you want to permanently delete this contact?/),
).toBeInTheDocument();

expect(
getByRole('button', { name: 'delete contact' }),
).toBeInTheDocument();
});
});
});
Loading

0 comments on commit 0c76edd

Please sign in to comment.