From d08575f19425bb7314b9be6c854c3da5a5a26849 Mon Sep 17 00:00:00 2001 From: Collin Pastika Date: Mon, 8 Jul 2024 16:23:57 -0400 Subject: [PATCH 1/5] added graphQL mutation squash --- .../Tool/FixEmailAddresses/AddEmailAddress.graphql | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/components/Tool/FixEmailAddresses/AddEmailAddress.graphql diff --git a/src/components/Tool/FixEmailAddresses/AddEmailAddress.graphql b/src/components/Tool/FixEmailAddresses/AddEmailAddress.graphql new file mode 100644 index 000000000..e6fabd887 --- /dev/null +++ b/src/components/Tool/FixEmailAddresses/AddEmailAddress.graphql @@ -0,0 +1,11 @@ +mutation EmailAddresses($input: PersonUpdateMutationInput!) { + updatePerson(input: $input) { + person { + emailAddresses { + nodes { + email + } + } + } + } +} From fb0e7c37627fd66a8cef2f0fde9e7306016f130a Mon Sep 17 00:00:00 2001 From: Collin Pastika Date: Mon, 17 Jun 2024 14:33:20 -0400 Subject: [PATCH 2/5] fixed add email functionality --- .../FixEmailAddresses/EmailValidationForm.tsx | 74 ++++++- .../FixEmailAddressPerson.test.tsx | 94 +++++++-- .../FixEmailAddressPerson.tsx | 8 +- ...sses.graphql => FixEmailAddresses.graphql} | 0 .../FixEmailAddresses.test.tsx | 188 +++++++++++++++--- .../FixEmailAddresses/FixEmailAddresses.tsx | 17 +- .../FixEmailAddressesMocks.ts | 158 +++++++++++---- 7 files changed, 422 insertions(+), 117 deletions(-) rename src/components/Tool/FixEmailAddresses/{GetInvalidEmailAddresses.graphql => FixEmailAddresses.graphql} (100%) diff --git a/src/components/Tool/FixEmailAddresses/EmailValidationForm.tsx b/src/components/Tool/FixEmailAddresses/EmailValidationForm.tsx index 9c10ce480..2487ee12a 100644 --- a/src/components/Tool/FixEmailAddresses/EmailValidationForm.tsx +++ b/src/components/Tool/FixEmailAddresses/EmailValidationForm.tsx @@ -4,7 +4,13 @@ import { Form, Formik } from 'formik'; import { useTranslation } from 'react-i18next'; import * as Yup from 'yup'; import { AddIcon } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/StyledComponents'; +import { useAccountListId } from 'src/hooks/useAccountListId'; +import { useEmailAddressesMutation } from './AddEmailAddress.generated'; import { RowWrapper } from './FixEmailAddressPerson'; +import { + GetInvalidEmailAddressesDocument, + GetInvalidEmailAddressesQuery, +} from './FixEmailAddresses.generated'; const ContactInputField = styled(TextField, { shouldForwardProp: (prop) => prop !== 'destroyed', @@ -24,16 +30,12 @@ interface EmailValidationFormEmail { interface EmailValidationFormProps { index: number; personId: string; - handleAdd?: (personId: string, email: string) => void; } -//TODO: Implement during MPDX-7946 -const onSubmit = (values, actions) => { - actions.resetForm(); -}; - const EmailValidationForm = ({ personId }: EmailValidationFormProps) => { const { t } = useTranslation(); + const accountListId = useAccountListId(); + const [emailAddressesMutation] = useEmailAddressesMutation(); const initialEmail = { email: '', @@ -55,6 +57,66 @@ const EmailValidationForm = ({ personId }: EmailValidationFormProps) => { isValid: Yup.bool().default(false), }); + const onSubmit = (values, actions) => { + emailAddressesMutation({ + variables: { + input: { + attributes: { + id: personId, + emailAddresses: [ + { + email: values.email, + }, + ], + }, + accountListId: accountListId || '', + }, + }, + update: (cache, { data: addEmailAddressData }) => { + actions.resetForm(); + const query = { + query: GetInvalidEmailAddressesDocument, + variables: { + accountListId: accountListId, + }, + }; + const dataFromCache = + cache.readQuery(query); + if (dataFromCache) { + const peopleWithNewEmail = dataFromCache.people.nodes.map( + (person) => { + if ( + person.id === personId && + addEmailAddressData?.updatePerson?.person.emailAddresses.nodes + ) { + return { + ...person, + emailAddresses: { + nodes: + addEmailAddressData?.updatePerson?.person.emailAddresses + .nodes, + }, + }; + } else { + return person; + } + }, + ); + + cache.writeQuery({ + ...query, + data: { + people: { + ...dataFromCache.people, + nodes: peopleWithNewEmail, + }, + }, + }); + } + }, + }); + }; + return ( { +const TestComponent = ({ mocks }: { mocks: ApolloErgonoMockMap }) => { return ( - + + mocks={mocks} + > + + ); @@ -66,7 +76,15 @@ const TestComponent: React.FC = () => { describe('FixEmailAddressPerson', () => { it('default', () => { const { getByText, getByTestId, getByDisplayValue } = render( - , + , ); expect(getByText(testData.name)).toBeInTheDocument(); @@ -80,7 +98,17 @@ describe('FixEmailAddressPerson', () => { }); it('input reset after adding an email address', async () => { - const { getByTestId, getByLabelText } = render(); + const { getByTestId, getByLabelText } = render( + , + ); const addInput = getByLabelText('New Email Address'); const addButton = getByTestId('addButton-testid'); @@ -98,7 +126,15 @@ describe('FixEmailAddressPerson', () => { describe('validation', () => { it('should show an error message if there is no email', async () => { const { getByLabelText, getByTestId, getByText } = render( - , + , ); const addInput = getByLabelText('New Email Address'); @@ -114,7 +150,15 @@ describe('FixEmailAddressPerson', () => { it('should show an error message if there is an invalid email', async () => { const { getByLabelText, getByTestId, getByText } = render( - , + , ); const addInput = getByLabelText('New Email Address'); @@ -129,7 +173,17 @@ describe('FixEmailAddressPerson', () => { }); it('should not disable the add button', async () => { - const { getByLabelText, getByTestId } = render(); + const { getByLabelText, getByTestId } = render( + , + ); const addInput = getByLabelText('New Email Address'); userEvent.type(addInput, 'new@new.com'); diff --git a/src/components/Tool/FixEmailAddresses/FixEmailAddressPerson.tsx b/src/components/Tool/FixEmailAddresses/FixEmailAddressPerson.tsx index 6fbc00973..b2795932c 100644 --- a/src/components/Tool/FixEmailAddresses/FixEmailAddressPerson.tsx +++ b/src/components/Tool/FixEmailAddresses/FixEmailAddressPerson.tsx @@ -104,7 +104,6 @@ export interface FixEmailAddressPersonProps { event: React.ChangeEvent, ) => void; handleDelete: (personId: string, emailAddress: number) => void; - handleAdd: (personId: string, email: string) => void; handleChangePrimary: (personId: string, emailIndex: number) => void; setContactFocus: SetContactFocus; } @@ -118,7 +117,6 @@ export const FixEmailAddressPerson: React.FC = ({ handleDelete, handleChangePrimary, setContactFocus, - handleAdd, }) => { const { t } = useTranslation(); const locale = useLocale(); @@ -277,11 +275,7 @@ export const FixEmailAddressPerson: React.FC = ({ { //TODO: index will need to be mapped to the correct personId } - + diff --git a/src/components/Tool/FixEmailAddresses/GetInvalidEmailAddresses.graphql b/src/components/Tool/FixEmailAddresses/FixEmailAddresses.graphql similarity index 100% rename from src/components/Tool/FixEmailAddresses/GetInvalidEmailAddresses.graphql rename to src/components/Tool/FixEmailAddresses/FixEmailAddresses.graphql diff --git a/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx b/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx index 645e76eab..03b0903af 100644 --- a/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx +++ b/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx @@ -1,18 +1,25 @@ import React from 'react'; +import { ApolloCache, InMemoryCache } from '@apollo/client'; import { ThemeProvider } from '@mui/material/styles'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { ErgonoMockShape } from 'graphql-ergonomock'; +import { ApolloErgonoMockMap, ErgonoMockShape } from 'graphql-ergonomock'; import TestRouter from '__tests__/util/TestRouter'; import TestWrapper from '__tests__/util/TestWrapper'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; +import { GetInvalidEmailAddressesQuery } from 'src/components/Tool/FixEmailAddresses/FixEmailAddresses.generated'; import theme from '../../../theme'; +import { EmailAddressesMutation } from './AddEmailAddress.generated'; import { FixEmailAddresses } from './FixEmailAddresses'; import { contactId, + contactOneEmailAddressNodes, + contactTwoEmailAddressNodes, + mockCacheWriteData, + mockCacheWriteDataContactTwo, mockInvalidEmailAddressesResponse, + newEmail, } from './FixEmailAddressesMocks'; -import { GetInvalidEmailAddressesQuery } from './GetInvalidEmailAddresses.generated'; const accountListId = 'test121'; const router = { @@ -23,23 +30,25 @@ const router = { const setContactFocus = jest.fn(); const Components = ({ - mockNodes = mockInvalidEmailAddressesResponse, + mocks = { + GetInvalidEmailAddresses: { + people: { nodes: mockInvalidEmailAddressesResponse }, + }, + }, + cache, }: { - mockNodes?: ErgonoMockShape[]; + mocks?: ApolloErgonoMockMap; + cache?: ApolloCache; }) => ( - mocks={{ - GetInvalidEmailAddresses: { - people: { - nodes: mockNodes, - }, - }, - }} + mocks={mocks} + cache={cache} > { expect(getByTestId('starIcon-testid2-0')).toBeInTheDocument(); }); - //TODO: Fix during MPDX-7946 - it.skip('add an email address to second person', async () => { - const { getByTestId, getByDisplayValue } = render(); - await waitFor(() => - expect(getByTestId('starIcon-testid2-0')).toBeInTheDocument(), - ); - expect(getByTestId('textfield-testid2-0')).toBeInTheDocument(); - - const textfieldNew1 = getByTestId( - 'addNewEmailInput-testid2', - ) as HTMLInputElement; - userEvent.type(textfieldNew1, 'email12345@gmail.com'); - const addButton1 = getByTestId('addButton-testid2'); - userEvent.click(addButton1); - - expect(textfieldNew1.value).toBe(''); - expect(getByTestId('textfield-testid2-2')).toBeInTheDocument(); - expect(getByDisplayValue('email12345@gmail.com')).toBeInTheDocument(); + describe('add email address', () => { + interface AddEmailAddressProps { + postSaveResponse: object; + emailAddressNodes: object[]; + elementToWaitFor: string; + textFieldIndex: number; + addButtonId: string; + cache: InMemoryCache; + } + + const addEmailAddress = async ({ + postSaveResponse, + emailAddressNodes, + elementToWaitFor, + textFieldIndex, + addButtonId, + cache, + }: AddEmailAddressProps) => { + let cardinality = 0; + jest.spyOn(cache, 'readQuery').mockReturnValue(postSaveResponse); + jest.spyOn(cache, 'writeQuery'); + + const updatePerson = { + person: { + emailAddresses: { + nodes: [ + ...emailAddressNodes, + { + email: newEmail.email, + }, + ], + }, + }, + } as ErgonoMockShape; + + const { getByTestId, getAllByLabelText } = render( + { + let queryResult; + if (cardinality === 0) { + queryResult = { + people: { + nodes: mockInvalidEmailAddressesResponse, + }, + }; + } else { + queryResult = postSaveResponse; + } + cardinality++; + return queryResult; + }, + EmailAddresses: { updatePerson }, + }} + cache={cache} + />, + ); + await waitFor(() => { + expect(getByTestId(elementToWaitFor)).toBeInTheDocument(); + }); + + const textFieldNew = + getAllByLabelText('New Email Address')[textFieldIndex]; + userEvent.type(textFieldNew, newEmail.email); + const addButton = getByTestId(addButtonId); + userEvent.click(addButton); + }; + + it('should add an email address to the first person', async () => { + const cache = new InMemoryCache(); + const postSaveResponse = { + people: { + nodes: [ + { + ...mockInvalidEmailAddressesResponse[0], + emailAddresses: { + nodes: [...contactOneEmailAddressNodes, newEmail], + }, + }, + { ...mockInvalidEmailAddressesResponse[1] }, + ], + }, + }; + await addEmailAddress({ + postSaveResponse, + emailAddressNodes: contactOneEmailAddressNodes, + elementToWaitFor: 'textfield-testid-0', + textFieldIndex: 0, + addButtonId: 'addButton-testid', + cache, + }); + + await waitFor(() => { + expect(cache.writeQuery).toHaveBeenLastCalledWith( + expect.objectContaining({ data: mockCacheWriteData }), + ); + }); + }); + + it('should add an email address to the second person', async () => { + const cache = new InMemoryCache(); + const postSaveResponse = { + people: { + nodes: [ + { ...mockInvalidEmailAddressesResponse[0] }, + { + ...mockInvalidEmailAddressesResponse[1], + emailAddresses: { + nodes: [...contactTwoEmailAddressNodes, newEmail], + }, + }, + ], + }, + }; + await addEmailAddress({ + postSaveResponse, + emailAddressNodes: contactTwoEmailAddressNodes, + elementToWaitFor: 'textfield-testid2-0', + textFieldIndex: 1, + addButtonId: 'addButton-testid2', + cache, + }); + + await waitFor(() => { + expect(cache.writeQuery).toHaveBeenLastCalledWith( + expect.objectContaining({ data: mockCacheWriteDataContactTwo }), + ); + }); + }); }); it('should render no contacts with no data', async () => { - const { getByText, getByTestId } = render(); + const { getByText, getByTestId } = render( + , + ); await waitFor(() => expect(getByTestId('fixEmailAddresses-null-state')).toBeInTheDocument(), ); diff --git a/src/components/Tool/FixEmailAddresses/FixEmailAddresses.tsx b/src/components/Tool/FixEmailAddresses/FixEmailAddresses.tsx index c89c99262..00fb2288f 100644 --- a/src/components/Tool/FixEmailAddresses/FixEmailAddresses.tsx +++ b/src/components/Tool/FixEmailAddresses/FixEmailAddresses.tsx @@ -11,6 +11,7 @@ import { import { styled } from '@mui/material/styles'; import { Trans, useTranslation } from 'react-i18next'; import { SetContactFocus } from 'pages/accountLists/[accountListId]/tools/useToolsHelper'; +import { useGetInvalidEmailAddressesQuery } from 'src/components/Tool/FixEmailAddresses/FixEmailAddresses.generated'; import { PersonEmailAddressInput } from 'src/graphql/types.generated'; import theme from '../../../theme'; import { ConfirmButtonIcon } from '../ConfirmButtonIcon'; @@ -18,7 +19,6 @@ import NoData from '../NoData'; import { StyledInput } from '../StyledInput'; import DeleteModal from './DeleteModal'; import { FixEmailAddressPerson } from './FixEmailAddressPerson'; -import { useGetInvalidEmailAddressesQuery } from './GetInvalidEmailAddresses.generated'; const Container = styled(Box)(() => ({ padding: theme.spacing(3), @@ -206,25 +206,13 @@ export const FixEmailAddresses: React.FC = ({ handleDeleteModalClose(); }; - // Add a new email address to the state - const handleAdd = (personId: string, email: string): void => { - const temp = { ...dataState }; - temp[personId].emailAddresses.push({ - updatedAt: new Date().toISOString(), - email: email, - primary: false, - source: 'MPDX', - }); - setDataState(temp); - }; - // Change the primary address in the state const handleChangePrimary = (personId: string, emailIndex: number): void => { const temp = { ...dataState }; temp[personId].emailAddresses = temp[personId].emailAddresses.map( (email, index) => ({ ...email, - primary: index === emailIndex ? true : false, + primary: index === emailIndex, }), ); setDataState(temp); @@ -296,7 +284,6 @@ export const FixEmailAddresses: React.FC = ({ contactId={person.contactId} handleChange={handleChange} handleDelete={handleDeleteModalOpen} - handleAdd={handleAdd} handleChangePrimary={handleChangePrimary} setContactFocus={setContactFocus} /> diff --git a/src/components/Tool/FixEmailAddresses/FixEmailAddressesMocks.ts b/src/components/Tool/FixEmailAddresses/FixEmailAddressesMocks.ts index 744daf715..040722f63 100644 --- a/src/components/Tool/FixEmailAddresses/FixEmailAddressesMocks.ts +++ b/src/components/Tool/FixEmailAddresses/FixEmailAddressesMocks.ts @@ -2,6 +2,61 @@ import { ErgonoMockShape } from 'graphql-ergonomock'; export const contactId = 'contactId'; +export const newEmail = { + __typename: 'EmailAddress', + id: 'id6', + updatedAt: new Date('2021-06-22T03:40:05-06:00').toISOString(), + email: 'email12345@gmail.com', + primary: false, + source: 'MPDX', +}; + +export const contactOneEmailAddressNodes = [ + { + __typename: 'EmailAddress', + id: 'id1', + updatedAt: new Date('2021-06-21T03:40:05-06:00').toISOString(), + email: 'email1@gmail.com', + primary: true, + source: 'MPDX', + }, + { + __typename: 'EmailAddress', + id: 'id12', + updatedAt: new Date('2021-06-21T03:40:05-06:00').toISOString(), + email: 'email2@gmail.com', + primary: false, + source: 'MPDX', + }, + { + __typename: 'EmailAddress', + id: 'id3', + updatedAt: new Date('2021-06-21T03:40:05-06:00').toISOString(), + email: 'email3@gmail.com', + primary: false, + source: 'MPDX', + }, +]; + +export const contactTwoEmailAddressNodes = [ + { + __typename: 'EmailAddress', + id: 'id4', + updatedAt: new Date('2021-06-21T03:40:05-06:00').toISOString(), + email: 'email4@gmail.com', + primary: true, + source: 'MPDX', + }, + { + __typename: 'EmailAddress', + id: 'id5', + updatedAt: new Date('2021-06-22T03:40:05-06:00').toISOString(), + email: 'email5@gmail.com', + primary: false, + source: 'MPDX', + }, +]; + export const mockInvalidEmailAddressesResponse: ErgonoMockShape[] = [ { id: 'testid', @@ -9,29 +64,7 @@ export const mockInvalidEmailAddressesResponse: ErgonoMockShape[] = [ lastName: 'Contact', contactId, emailAddresses: { - nodes: [ - { - id: 'id1', - updatedAt: new Date('2021-06-21T03:40:05-06:00').toISOString(), - email: 'email1@gmail.com', - primary: true, - source: 'MPDX', - }, - { - id: 'id12', - updatedAt: new Date('2021-06-21T03:40:05-06:00').toISOString(), - email: 'email2@gmail.com', - primary: false, - source: 'MPDX', - }, - { - id: 'id3', - updatedAt: new Date('2021-06-21T03:40:05-06:00').toISOString(), - email: 'email3@gmail.com', - primary: false, - source: 'MPDX', - }, - ], + nodes: contactOneEmailAddressNodes, }, }, { @@ -40,22 +73,69 @@ export const mockInvalidEmailAddressesResponse: ErgonoMockShape[] = [ lastName: 'Lion', contactId: 'contactId2', emailAddresses: { - nodes: [ - { - id: 'id4', - updatedAt: new Date('2021-06-21T03:40:05-06:00').toISOString(), - number: 'email4@gmail.com', - primary: true, - source: 'MPDX', - }, - { - id: 'id5', - updatedAt: new Date('2021-06-22T03:40:05-06:00').toISOString(), - number: 'email5@gmail.com', - primary: false, - source: 'MPDX', - }, - ], + nodes: contactTwoEmailAddressNodes, }, }, ]; + +export const mockCacheWriteData = { + people: { + nodes: [ + { + ...mockInvalidEmailAddressesResponse[0], + emailAddresses: { + nodes: [ + { + __typename: contactOneEmailAddressNodes[0].__typename, + email: contactOneEmailAddressNodes[0].email, + }, + { + __typename: contactOneEmailAddressNodes[1].__typename, + email: contactOneEmailAddressNodes[1].email, + }, + { + __typename: contactOneEmailAddressNodes[2].__typename, + email: contactOneEmailAddressNodes[2].email, + }, + { + __typename: newEmail.__typename, + email: newEmail.email, + }, + ], + }, + }, + { + ...mockInvalidEmailAddressesResponse[1], + }, + ], + }, +}; + +export const mockCacheWriteDataContactTwo = { + people: { + nodes: [ + { + ...mockInvalidEmailAddressesResponse[0], + }, + { + ...mockInvalidEmailAddressesResponse[1], + emailAddresses: { + nodes: [ + { + __typename: contactTwoEmailAddressNodes[0].__typename, + email: contactTwoEmailAddressNodes[0].email, + }, + { + __typename: contactTwoEmailAddressNodes[1].__typename, + email: contactTwoEmailAddressNodes[1].email, + }, + { + __typename: newEmail.__typename, + email: newEmail.email, + }, + ], + }, + }, + ], + }, +}; From 0394ce2830325bdd741b2050517c87ab77999325 Mon Sep 17 00:00:00 2001 From: Bill Randall Date: Fri, 19 Jul 2024 10:52:08 -0400 Subject: [PATCH 3/5] Fix inline email address editing --- .../FixEmailAddressPerson.test.tsx | 14 ++++++++++++-- .../FixEmailAddresses/FixEmailAddressPerson.tsx | 6 ++++-- .../FixEmailAddresses/FixEmailAddresses.test.tsx | 9 ++++----- .../Tool/FixEmailAddresses/FixEmailAddresses.tsx | 3 ++- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/components/Tool/FixEmailAddresses/FixEmailAddressPerson.test.tsx b/src/components/Tool/FixEmailAddresses/FixEmailAddressPerson.test.tsx index cacf070d5..e76b9c00a 100644 --- a/src/components/Tool/FixEmailAddresses/FixEmailAddressPerson.test.tsx +++ b/src/components/Tool/FixEmailAddresses/FixEmailAddressPerson.test.tsx @@ -6,13 +6,14 @@ import { DateTime } from 'luxon'; import TestWrapper from '__tests__/util/TestWrapper'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; import { render, waitFor } from '__tests__/util/testingLibraryReactMock'; +import { PersonEmailAddressInput } from 'src/graphql/types.generated'; import theme from '../../../theme'; import { EmailAddressesMutation } from './AddEmailAddress.generated'; import { FixEmailAddressPerson, FixEmailAddressPersonProps, } from './FixEmailAddressPerson'; -import { EmailAddressData } from './FixEmailAddresses'; +import { EmailAddressData, PersonEmailAddresses } from './FixEmailAddresses'; import { GetInvalidEmailAddressesQuery } from './FixEmailAddresses.generated'; import { mockInvalidEmailAddressesResponse } from './FixEmailAddressesMocks'; @@ -46,6 +47,14 @@ const handleDeleteModalOpenMock = jest.fn(); const handleChangePrimaryMock = jest.fn(); const TestComponent = ({ mocks }: { mocks: ApolloErgonoMockMap }) => { + const toDelete = [] as PersonEmailAddressInput[]; + const dataState = { + id: { + emailAddresses: testData.emailAddresses as EmailAddressData[], + toDelete, + }, + } as { [key: string]: PersonEmailAddresses }; + return ( @@ -56,10 +65,11 @@ const TestComponent = ({ mocks }: { mocks: ApolloErgonoMockMap }) => { mocks={mocks} > ({ [theme.breakpoints.up('md')]: { @@ -96,6 +96,7 @@ export interface FixEmailAddressPersonProps { name: string; emailAddresses?: EmailAddressData[]; personId: string; + dataState: { [key: string]: PersonEmailAddresses }; toDelete: PersonEmailAddressInput[]; contactId: string; handleChange: ( @@ -112,6 +113,7 @@ export const FixEmailAddressPerson: React.FC = ({ name, emailAddresses, personId, + dataState, contactId, handleChange, handleDelete, @@ -130,7 +132,7 @@ export const FixEmailAddressPerson: React.FC = ({ personId: personId, isPrimary: email.primary, })) || [], - [emailAddresses], + [emailAddresses, dataState], ); const handleContactNameClick = () => { diff --git a/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx b/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx index 03b0903af..63f4ab966 100644 --- a/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx +++ b/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx @@ -273,17 +273,16 @@ describe('FixPhoneNumbers-Home', () => { ).toBeInTheDocument(); }); - //TODO: Fix during MPDX-7946 - it.skip('should modify first email of first contact', async () => { + it('should modify first email of first contact', async () => { const { getByTestId } = render(); await waitFor(() => { expect(getByTestId('textfield-testid-0')).toBeInTheDocument(); }); - const firstInput = getByTestId('textfield-testid-0') as HTMLInputElement; + const firstInput = getByTestId('textfield-testid-0'); - expect(firstInput.value).toBe('email1@gmail.com'); + expect(firstInput).toHaveValue('email1@gmail.com'); userEvent.type(firstInput, '123'); - expect(firstInput.value).toBe('email1@gmail.com123'); + expect(firstInput).toHaveValue('email1@gmail.com123'); }); describe('setContactFocus()', () => { diff --git a/src/components/Tool/FixEmailAddresses/FixEmailAddresses.tsx b/src/components/Tool/FixEmailAddresses/FixEmailAddresses.tsx index 00fb2288f..34516aeb6 100644 --- a/src/components/Tool/FixEmailAddresses/FixEmailAddresses.tsx +++ b/src/components/Tool/FixEmailAddresses/FixEmailAddresses.tsx @@ -104,7 +104,7 @@ export interface EmailAddressData { destroy?: boolean; } -interface PersonEmailAddresses { +export interface PersonEmailAddresses { emailAddresses: EmailAddressData[]; toDelete: PersonEmailAddressInput[]; } @@ -279,6 +279,7 @@ export const FixEmailAddresses: React.FC = ({ name={`${person.firstName} ${person.lastName}`} key={person.id} personId={person.id} + dataState={dataState} emailAddresses={dataState[person.id]?.emailAddresses} toDelete={dataState[person.id]?.toDelete} contactId={person.contactId} From 599149d25a8b45ee70100f5e4bd67097ebf1a61d Mon Sep 17 00:00:00 2001 From: Bill Randall Date: Fri, 26 Jul 2024 12:00:42 -0400 Subject: [PATCH 4/5] Fix copy/paste error --- .../Tool/FixEmailAddresses/FixEmailAddresses.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx b/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx index 63f4ab966..03a33cb0e 100644 --- a/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx +++ b/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx @@ -60,7 +60,7 @@ const Components = ({ ); -describe('FixPhoneNumbers-Home', () => { +describe('FixEmailAddresses-Home', () => { it('default with test data', async () => { const { getByText, getByTestId, queryByTestId } = render(); From 5d876788c3ea8bd4d5e02c1526a6e805b50a10a6 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Thu, 1 Aug 2024 18:03:07 -0400 Subject: [PATCH 5/5] Cleaning up code, Adding more testing around adding a new email and improving some UX with snackBar. --- .../FixEmailAddresses/EmailValidationForm.tsx | 36 ++-- .../FixEmailAddresses.test.tsx | 174 +++++++++++------- 2 files changed, 134 insertions(+), 76 deletions(-) diff --git a/src/components/Tool/FixEmailAddresses/EmailValidationForm.tsx b/src/components/Tool/FixEmailAddresses/EmailValidationForm.tsx index 2487ee12a..98636c499 100644 --- a/src/components/Tool/FixEmailAddresses/EmailValidationForm.tsx +++ b/src/components/Tool/FixEmailAddresses/EmailValidationForm.tsx @@ -1,10 +1,12 @@ import { Grid, IconButton, TextField } from '@mui/material'; import { styled } from '@mui/material/styles'; import { Form, Formik } from 'formik'; +import { useSnackbar } from 'notistack'; import { useTranslation } from 'react-i18next'; -import * as Yup from 'yup'; +import * as yup from 'yup'; import { AddIcon } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/StyledComponents'; import { useAccountListId } from 'src/hooks/useAccountListId'; +import i18n from 'src/lib/i18n'; import { useEmailAddressesMutation } from './AddEmailAddress.generated'; import { RowWrapper } from './FixEmailAddressPerson'; import { @@ -32,10 +34,23 @@ interface EmailValidationFormProps { personId: string; } +const validationSchema = yup.object({ + email: yup + .string() + .email(i18n.t('Invalid Email Address Format')) + .required(i18n.t('Please enter a valid email address')), + isPrimary: yup.bool().default(false), + updatedAt: yup.string(), + source: yup.string(), + personId: yup.string(), + isValid: yup.bool().default(false), +}); + const EmailValidationForm = ({ personId }: EmailValidationFormProps) => { const { t } = useTranslation(); const accountListId = useAccountListId(); const [emailAddressesMutation] = useEmailAddressesMutation(); + const { enqueueSnackbar } = useSnackbar(); const initialEmail = { email: '', @@ -46,21 +61,11 @@ const EmailValidationForm = ({ personId }: EmailValidationFormProps) => { isValid: false, } as EmailValidationFormEmail; - const validationSchema = Yup.object({ - email: Yup.string() - .email(t('Invalid Email Address Format')) - .required('Please enter a valid email address'), - isPrimary: Yup.bool().default(false), - updatedAt: Yup.string(), - source: Yup.string(), - personId: Yup.string(), - isValid: Yup.bool().default(false), - }); - const onSubmit = (values, actions) => { emailAddressesMutation({ variables: { input: { + accountListId: accountListId || '', attributes: { id: personId, emailAddresses: [ @@ -69,7 +74,6 @@ const EmailValidationForm = ({ personId }: EmailValidationFormProps) => { }, ], }, - accountListId: accountListId || '', }, }, update: (cache, { data: addEmailAddressData }) => { @@ -114,6 +118,12 @@ const EmailValidationForm = ({ personId }: EmailValidationFormProps) => { }); } }, + onCompleted: () => { + enqueueSnackbar(t('Added email address'), { variant: 'success' }); + }, + onError: () => { + enqueueSnackbar(t('Failed to add email address'), { variant: 'error' }); + }, }); }; diff --git a/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx b/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx index 03a33cb0e..33668ed05 100644 --- a/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx +++ b/src/components/Tool/FixEmailAddresses/FixEmailAddresses.test.tsx @@ -4,8 +4,8 @@ import { ThemeProvider } from '@mui/material/styles'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { ApolloErgonoMockMap, ErgonoMockShape } from 'graphql-ergonomock'; +import { SnackbarProvider } from 'notistack'; import TestRouter from '__tests__/util/TestRouter'; -import TestWrapper from '__tests__/util/TestWrapper'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; import { GetInvalidEmailAddressesQuery } from 'src/components/Tool/FixEmailAddresses/FixEmailAddresses.generated'; import theme from '../../../theme'; @@ -21,43 +21,58 @@ import { newEmail, } from './FixEmailAddressesMocks'; -const accountListId = 'test121'; +const accountListId = 'accountListId'; const router = { query: { accountListId }, isReady: true, }; const setContactFocus = jest.fn(); +const mutationSpy = jest.fn(); +const mockEnqueue = jest.fn(); + +jest.mock('notistack', () => ({ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + ...jest.requireActual('notistack'), + useSnackbar: () => { + return { + enqueueSnackbar: mockEnqueue, + }; + }, +})); -const Components = ({ - mocks = { - GetInvalidEmailAddresses: { - people: { nodes: mockInvalidEmailAddressesResponse }, - }, +const defaultGraphQLMock = { + GetInvalidEmailAddresses: { + people: { nodes: mockInvalidEmailAddressesResponse }, }, - cache, -}: { +}; + +interface ComponentsProps { mocks?: ApolloErgonoMockMap; cache?: ApolloCache; -}) => ( - - - +} + +const Components = ({ mocks = defaultGraphQLMock, cache }: ComponentsProps) => ( + + + mocks={mocks} cache={cache} + onCall={mutationSpy} > - - - + + + ); describe('FixEmailAddresses-Home', () => { @@ -92,6 +107,39 @@ describe('FixEmailAddresses-Home', () => { expect(getByTestId('starOutlineIcon-testid-0')).toBeInTheDocument(); }); + it('should add an new email address, firing a GraphQL mutation and resetting the form', async () => { + const { getByTestId, getAllByLabelText } = render(); + await waitFor(() => { + expect(getByTestId('textfield-testid-0')).toBeInTheDocument(); + }); + const textFieldNew = getAllByLabelText('New Email Address')[0]; + userEvent.type(textFieldNew, newEmail.email); + const addButton = getByTestId('addButton-testid'); + expect(textFieldNew).toHaveValue(newEmail.email); + + userEvent.click(addButton); + + await waitFor(() => + expect(mockEnqueue).toHaveBeenCalledWith('Added email address', { + variant: 'success', + }), + ); + + expect(mutationSpy.mock.calls[1][0].operation.operationName).toEqual( + 'EmailAddresses', + ); + expect(mutationSpy.mock.calls[1][0].operation.variables).toEqual({ + input: { + accountListId: accountListId, + attributes: { + id: 'testid', + emailAddresses: [{ email: newEmail.email }], + }, + }, + }); + expect(textFieldNew).toHaveValue(''); + }); + //TODO: Fix during MPDX-7936 it.skip('delete third email from first person', async () => { const { getByTestId, queryByTestId } = render(); @@ -124,7 +172,53 @@ describe('FixEmailAddresses-Home', () => { expect(getByTestId('starIcon-testid2-0')).toBeInTheDocument(); }); - describe('add email address', () => { + it('should render no contacts with no data', async () => { + const { getByText, getByTestId } = render( + , + ); + await waitFor(() => + expect(getByTestId('fixEmailAddresses-null-state')).toBeInTheDocument(), + ); + expect( + getByText('No people with email addresses need attention'), + ).toBeInTheDocument(); + }); + + it('should modify first email of first contact', async () => { + const { getByTestId } = render(); + await waitFor(() => { + expect(getByTestId('textfield-testid-0')).toBeInTheDocument(); + }); + const firstInput = getByTestId('textfield-testid-0'); + + expect(firstInput).toHaveValue('email1@gmail.com'); + userEvent.type(firstInput, '123'); + expect(firstInput).toHaveValue('email1@gmail.com123'); + }); + + describe('setContactFocus()', () => { + it('should open up contact details', async () => { + const { getByText, queryByTestId } = render(); + await waitFor(() => + expect(queryByTestId('loading')).not.toBeInTheDocument(), + ); + expect(setContactFocus).not.toHaveBeenCalled(); + + const contactName = getByText('Test Contact'); + + expect(contactName).toBeInTheDocument(); + userEvent.click(contactName); + expect(setContactFocus).toHaveBeenCalledWith(contactId); + }); + }); + + describe('Add email address - Testing cache', () => { interface AddEmailAddressProps { postSaveResponse: object; emailAddressNodes: object[]; @@ -254,50 +348,4 @@ describe('FixEmailAddresses-Home', () => { }); }); }); - - it('should render no contacts with no data', async () => { - const { getByText, getByTestId } = render( - , - ); - await waitFor(() => - expect(getByTestId('fixEmailAddresses-null-state')).toBeInTheDocument(), - ); - expect( - getByText('No people with email addresses need attention'), - ).toBeInTheDocument(); - }); - - it('should modify first email of first contact', async () => { - const { getByTestId } = render(); - await waitFor(() => { - expect(getByTestId('textfield-testid-0')).toBeInTheDocument(); - }); - const firstInput = getByTestId('textfield-testid-0'); - - expect(firstInput).toHaveValue('email1@gmail.com'); - userEvent.type(firstInput, '123'); - expect(firstInput).toHaveValue('email1@gmail.com123'); - }); - - describe('setContactFocus()', () => { - it('should open up contact details', async () => { - const { getByText, queryByTestId } = render(); - await waitFor(() => - expect(queryByTestId('loading')).not.toBeInTheDocument(), - ); - expect(setContactFocus).not.toHaveBeenCalled(); - - const contactName = getByText('Test Contact'); - - expect(contactName).toBeInTheDocument(); - userEvent.click(contactName); - expect(setContactFocus).toHaveBeenCalledWith(contactId); - }); - }); });