Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MPDX-8229] Add duplicate contact button #1083

Merged
merged 4 commits into from
Sep 20, 2024
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
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useRouter } from 'next/router';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { loadSession } from 'pages/api/utils/pagePropsHelpers';
Expand All @@ -7,6 +8,7 @@ import { SetContactFocus, useToolsHelper } from '../../useToolsHelper';

const MergeContactsPage: React.FC = () => {
const { t } = useTranslation();
const { query } = useRouter();
const { accountListId, handleSelectContact } = useToolsHelper();
const pageUrl = 'tools/merge/contacts';

Expand All @@ -22,6 +24,9 @@ const MergeContactsPage: React.FC = () => {
>
<MergeContacts
accountListId={accountListId || ''}
contactId={
typeof query.duplicateId === 'string' ? query.duplicateId : undefined
}
setContactFocus={setContactFocus}
/>
</ToolsWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,29 @@ fragment ContactDetailsHeader on Contact {
...ContactHeaderNewsletter
}

query GetContactDetailsHeader($accountListId: ID!, $contactId: ID!) {
query GetContactDetailsHeader(
$accountListId: ID!
$contactId: ID!
$loadDuplicate: Boolean!
) {
contact(accountListId: $accountListId, id: $contactId) {
...ContactDetailsHeader
}
contactDuplicates(
accountListId: $accountListId
contactIds: [$contactId]
ignore: false
first: 1
) @include(if: $loadDuplicate) {
nodes {
id
reason
recordOne {
id
}
recordTwo {
id
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,148 +16,144 @@ import { GetContactDetailsHeaderQuery } from './ContactDetailsHeader.generated';
const accountListId = 'abc';
const contactId = 'contact-1';

const router = {
query: { accountListId },
};

const mocks = {
GetContactDetailsHeader: {
contact: {
name: 'Lname, Fname',
avatar: `https://cru.org/assets/image.jpg`,
primaryPerson: null,
pledgeCurrency: 'USD',
lastDonation: null,
},
},
};
const mutationSpy = jest.fn();

interface TestComponentProps {
duplicateRecord?: 1 | 2;
pathname?: string;
}

const TestComponent: React.FC<TestComponentProps> = ({
duplicateRecord,
pathname,
}) => (
<SnackbarProvider>
<LocalizationProvider dateAdapter={AdapterLuxon}>
<TestRouter router={{ query: { accountListId }, pathname }}>
<ThemeProvider theme={theme}>
<GqlMockedProvider<{
GetContactDetailsHeader: GetContactDetailsHeaderQuery;
}>
mocks={{
GetContactDetailsHeader: {
contact: {
name: 'Lname, Fname',
avatar: 'https://cru.org/assets/image.jpg',
primaryPerson: null,
pledgeCurrency: 'USD',
lastDonation: null,
},
contactDuplicates: {
nodes:
typeof duplicateRecord === 'number'
? [
{
recordOne: {
id:
duplicateRecord === 1
? contactId
: 'duplicate-contact',
},
recordTwo: {
id:
duplicateRecord === 1
? 'duplicate-contact'
: contactId,
},
},
]
: [],
},
},
}}
onCall={mutationSpy}
>
<ContactsWrapper>
<ContactDetailProvider>
<ContactDetailsHeader
accountListId={accountListId}
contactId={contactId}
onClose={() => {}}
setContactDetailsLoaded={() => {}}
contactDetailsLoaded={false}
/>
</ContactDetailProvider>
</ContactsWrapper>
</GqlMockedProvider>
</ThemeProvider>
</TestRouter>
</LocalizationProvider>
</SnackbarProvider>
);

describe('ContactDetails', () => {
it('should show loading state', async () => {
const { queryByTestId } = render(
<SnackbarProvider>
<TestRouter router={router}>
<ThemeProvider theme={theme}>
<GqlMockedProvider>
<ContactsWrapper>
<ContactDetailProvider>
<ContactDetailsHeader
accountListId={accountListId}
contactId={contactId}
onClose={() => {}}
setContactDetailsLoaded={() => {}}
contactDetailsLoaded={false}
/>
</ContactDetailProvider>
</ContactsWrapper>
</GqlMockedProvider>
</ThemeProvider>
</TestRouter>
</SnackbarProvider>,
);
const { getByTestId } = render(<TestComponent />);

expect(getByTestId('Skeleton')).toBeInTheDocument();

expect(queryByTestId('Skeleton')).toBeInTheDocument();
await waitFor(() =>
expect(mutationSpy).toHaveGraphqlOperation('GetContactDetailsHeader', {
accountListId,
contactId,
loadDuplicate: true,
}),
);
});

it('should render with contact details', async () => {
const { findByText, queryByTestId } = render(
<SnackbarProvider>
<TestRouter router={router}>
<ThemeProvider theme={theme}>
<GqlMockedProvider<{
GetContactDetailsHeader: GetContactDetailsHeaderQuery;
}>
mocks={{
GetContactDetailsHeader: {
contact: {
name: 'Fname Lname',
lastDonation: null,
pledgeCurrency: 'USD',
},
},
}}
>
<ContactsWrapper>
<ContactDetailProvider>
<ContactDetailsHeader
accountListId={accountListId}
contactId={contactId}
onClose={() => {}}
setContactDetailsLoaded={() => {}}
contactDetailsLoaded={false}
/>
</ContactDetailProvider>
</ContactsWrapper>
</GqlMockedProvider>
</ThemeProvider>
</TestRouter>
</SnackbarProvider>,
describe('duplicate contact', () => {
it.each([[1], [2]] as const)(
'should render duplicate contact when the current contact is record %i',
async (duplicateRecord) => {
const { findByRole, getByRole } = render(
<TestComponent duplicateRecord={duplicateRecord} />,
);

const matchButton = await findByRole('link', { name: 'See Match' });
expect(matchButton).toHaveAttribute(
'href',
'/accountLists/abc/tools/merge/contacts?duplicateId=duplicate-contact',
);

userEvent.click(getByRole('button', { name: 'Dismiss Duplicate' }));
expect(matchButton).not.toBeInTheDocument();
},
);

expect(await findByText('Fname Lname')).toBeVisible();
it('does not render duplicate contact where there is no duplicate', async () => {
const { queryByRole } = render(<TestComponent />);

expect(
queryByRole('link', { name: 'See Match' }),
).not.toBeInTheDocument();
});

it('does not load on the merge contacts page', async () => {
render(
<TestComponent pathname="/accountLists/[accountListId]/tools/merge/contacts/[[...contactId]]" />,
);

expect(queryByTestId('Skeleton')).toBeNull();
await waitFor(() =>
expect(mutationSpy).toHaveGraphqlOperation('GetContactDetailsHeader', {
loadDuplicate: false,
}),
);
});
});

it('should render without primaryPerson', async () => {
const { findByText, queryByTestId } = render(
<SnackbarProvider>
<TestRouter router={router}>
<ThemeProvider theme={theme}>
<GqlMockedProvider<{
GetContactDetailsHeader: GetContactDetailsHeaderQuery;
}>
mocks={mocks}
>
<ContactsWrapper>
<ContactDetailProvider>
<ContactDetailsHeader
accountListId={accountListId}
contactId={contactId}
onClose={() => {}}
setContactDetailsLoaded={() => {}}
contactDetailsLoaded={false}
/>
</ContactDetailProvider>
</ContactsWrapper>
</GqlMockedProvider>
</ThemeProvider>
</TestRouter>
</SnackbarProvider>,
);
it('should render with contact details', async () => {
const { findByText, queryByTestId } = render(<TestComponent />);

expect(await findByText('Lname, Fname')).toBeVisible();

expect(queryByTestId('Skeleton')).toBeNull();
expect(queryByTestId('Skeleton')).not.toBeInTheDocument();
});

it('should open Edit Partnership modal', async () => {
const { queryByText, getAllByLabelText } = render(
<SnackbarProvider>
<LocalizationProvider dateAdapter={AdapterLuxon}>
<TestRouter router={router}>
<ThemeProvider theme={theme}>
<GqlMockedProvider<{
GetContactDetailsHeader: GetContactDetailsHeaderQuery;
}>
mocks={mocks}
>
<ContactsWrapper>
<ContactDetailProvider>
<ContactDetailsHeader
accountListId={accountListId}
contactId={contactId}
onClose={() => {}}
setContactDetailsLoaded={() => {}}
contactDetailsLoaded={false}
/>
</ContactDetailProvider>
</ContactsWrapper>
</GqlMockedProvider>
</ThemeProvider>
</TestRouter>
</LocalizationProvider>
</SnackbarProvider>,
<LocalizationProvider dateAdapter={AdapterLuxon}>
<TestComponent />
</LocalizationProvider>,
);
await waitFor(() =>
expect(getAllByLabelText('Edit Partnership Info')[0]).toBeInTheDocument(),
Expand All @@ -170,32 +166,11 @@ describe('ContactDetails', () => {

it('should close Edit Partnership modal', async () => {
const { queryByText, getAllByLabelText, getByLabelText } = render(
<SnackbarProvider>
<LocalizationProvider dateAdapter={AdapterLuxon}>
<TestRouter router={router}>
<ThemeProvider theme={theme}>
<GqlMockedProvider<{
GetContactDetailsHeader: GetContactDetailsHeaderQuery;
}>
mocks={mocks}
>
<ContactsWrapper>
<ContactDetailProvider>
<ContactDetailsHeader
accountListId={accountListId}
contactId={contactId}
onClose={() => {}}
setContactDetailsLoaded={() => {}}
contactDetailsLoaded={false}
/>
</ContactDetailProvider>
</ContactsWrapper>
</GqlMockedProvider>
</ThemeProvider>
</TestRouter>
</LocalizationProvider>
</SnackbarProvider>,
<LocalizationProvider dateAdapter={AdapterLuxon}>
<TestComponent />
</LocalizationProvider>,
);

await waitFor(() =>
expect(getAllByLabelText('Edit Partnership Info')[0]).toBeInTheDocument(),
);
Expand All @@ -208,34 +183,10 @@ describe('ContactDetails', () => {
expect(queryByText('Edit Partnership')).not.toBeInTheDocument(),
);
});

it('should render avatar', async () => {
const { queryByText, getAllByLabelText } = render(
<SnackbarProvider>
<LocalizationProvider dateAdapter={AdapterLuxon}>
<TestRouter router={router}>
<ThemeProvider theme={theme}>
<GqlMockedProvider<{
GetContactDetailsHeader: GetContactDetailsHeaderQuery;
}>
mocks={mocks}
>
<ContactsWrapper>
<ContactDetailProvider>
<ContactDetailsHeader
accountListId={accountListId}
contactId={contactId}
onClose={() => {}}
setContactDetailsLoaded={() => {}}
contactDetailsLoaded={false}
/>
</ContactDetailProvider>
</ContactsWrapper>
</GqlMockedProvider>
</ThemeProvider>
</TestRouter>
</LocalizationProvider>
</SnackbarProvider>,
);
const { queryByText, getAllByLabelText } = render(<TestComponent />);

await waitFor(() =>
expect(getAllByLabelText('Edit Partnership Info')[0]).toBeInTheDocument(),
);
Expand Down
Loading
Loading