From c4207b012e488766cb79086e831ecacd8593e047 Mon Sep 17 00:00:00 2001 From: Kyle Cardwell Date: Wed, 27 Nov 2024 11:55:20 -0700 Subject: [PATCH 1/4] prevents saving if formattedSignature equals the message body --- .../components/ComposeForm/ComposeForm.jsx | 21 +++++++++++++++++-- .../containers/Compose.jsx | 12 ----------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/applications/mhv-secure-messaging/components/ComposeForm/ComposeForm.jsx b/src/applications/mhv-secure-messaging/components/ComposeForm/ComposeForm.jsx index 227dca687f20..7716b34d950f 100644 --- a/src/applications/mhv-secure-messaging/components/ComposeForm/ComposeForm.jsx +++ b/src/applications/mhv-secure-messaging/components/ComposeForm/ComposeForm.jsx @@ -47,9 +47,10 @@ import { RadioCategories } from '../../util/inputContants'; import { getCategories } from '../../actions/categories'; import ElectronicSignature from './ElectronicSignature'; import RecipientsSelect from './RecipientsSelect'; +import { getPatientSignature } from '../../actions/preferences'; const ComposeForm = props => { - const { pageTitle, headerRef, draft, recipients, signature } = props; + const { pageTitle, headerRef, draft, recipients } = props; const { noAssociations, allTriageGroupsBlocked, @@ -58,11 +59,22 @@ const ComposeForm = props => { const dispatch = useDispatch(); const history = useHistory(); + const signature = useSelector(state => state.sm.preferences?.signature); + const [recipientsList, setRecipientsList] = useState(allowedRecipients); const [selectedRecipientId, setSelectedRecipientId] = useState(null); const [isSignatureRequired, setIsSignatureRequired] = useState(null); const [checkboxMarked, setCheckboxMarked] = useState(false); + useEffect( + () => { + if (!signature) { + dispatch(getPatientSignature()); + } + }, + [signature, dispatch], + ); + useEffect( () => { if (selectedRecipientId) { @@ -346,7 +358,11 @@ const ComposeForm = props => { setSubjectError(ErrorMessages.ComposeForm.SUBJECT_REQUIRED); messageValid = false; } - if (messageBody === '' || messageBody.match(/^[\s]+$/)) { + if ( + messageBody === '' || + messageBody.match(/^[\s]+$/) || + messageBody.trim() === formattedSignature.trim() + ) { setBodyError(ErrorMessages.ComposeForm.BODY_REQUIRED); messageValid = false; } @@ -372,6 +388,7 @@ const ComposeForm = props => { messageBody, category, isSignatureRequired, + formattedSignature, electronicSignature, checkboxMarked, setMessageInvalid, diff --git a/src/applications/mhv-secure-messaging/containers/Compose.jsx b/src/applications/mhv-secure-messaging/containers/Compose.jsx index 5427f82a0269..f86cb0d7a4df 100644 --- a/src/applications/mhv-secure-messaging/containers/Compose.jsx +++ b/src/applications/mhv-secure-messaging/containers/Compose.jsx @@ -8,13 +8,11 @@ import ComposeForm from '../components/ComposeForm/ComposeForm'; import InterstitialPage from './InterstitialPage'; import { closeAlert } from '../actions/alerts'; import { PageTitles, Paths } from '../util/constants'; -import { getPatientSignature } from '../actions/preferences'; const Compose = () => { const dispatch = useDispatch(); const recipients = useSelector(state => state.sm.recipients); const { drafts, saveError } = useSelector(state => state.sm.threadDetails); - const signature = useSelector(state => state.sm.preferences.signature); const draftMessage = drafts?.[0] ?? null; const { draftId } = useParams(); @@ -47,15 +45,6 @@ const Compose = () => { [dispatch, draftId, location.pathname], ); - useEffect( - () => { - if (!signature) { - dispatch(getPatientSignature()); - } - }, - [signature, dispatch], - ); - useEffect( () => { if (draftMessage?.messageId && draftMessage.draftDate === null) { @@ -96,7 +85,6 @@ const Compose = () => { headerRef={header} draft={draftMessage} recipients={!recipients.error && recipients} - signature={signature} /> ); From 75c56b81c8096e9a05a34b7b53800957a7f5a212 Mon Sep 17 00:00:00 2001 From: Kyle Cardwell Date: Mon, 2 Dec 2024 08:33:22 -0700 Subject: [PATCH 2/4] updated unit tests --- .../components/ComposeForm/ComposeForm.jsx | 2 +- .../Compose/ComposeForm.unit.spec.jsx | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/applications/mhv-secure-messaging/components/ComposeForm/ComposeForm.jsx b/src/applications/mhv-secure-messaging/components/ComposeForm/ComposeForm.jsx index 7716b34d950f..1cc3a5df610b 100644 --- a/src/applications/mhv-secure-messaging/components/ComposeForm/ComposeForm.jsx +++ b/src/applications/mhv-secure-messaging/components/ComposeForm/ComposeForm.jsx @@ -361,7 +361,7 @@ const ComposeForm = props => { if ( messageBody === '' || messageBody.match(/^[\s]+$/) || - messageBody.trim() === formattedSignature.trim() + (formattedSignature && messageBody.trim() === formattedSignature.trim()) ) { setBodyError(ErrorMessages.ComposeForm.BODY_REQUIRED); messageValid = false; diff --git a/src/applications/mhv-secure-messaging/tests/components/Compose/ComposeForm.unit.spec.jsx b/src/applications/mhv-secure-messaging/tests/components/Compose/ComposeForm.unit.spec.jsx index eae080f9ee56..fcb271586fbc 100644 --- a/src/applications/mhv-secure-messaging/tests/components/Compose/ComposeForm.unit.spec.jsx +++ b/src/applications/mhv-secure-messaging/tests/components/Compose/ComposeForm.unit.spec.jsx @@ -352,6 +352,31 @@ describe('Compose form component', () => { ); }); + it('displays error states on message body field when send button is clicked and message body only has signature', async () => { + const customState = { + ...initialState, + sm: { + ...initialState.sm, + triageTeams: { triageTeams }, + categories: { categories }, + preferences: signatureReducers.signatureEnabled, + }, + featureToggles: {}, + }; + const screen = setup(customState, Paths.COMPOSE, { + draft: { ...draftMessage, body: '\n\nName\nTitle' }, + }); + + const sendButton = screen.getByTestId('send-button'); + + fireEvent.click(sendButton); + + const messageInput = await screen.getByTestId('message-body-field'); + const messageInputError = messageInput[getProps(messageInput)].error; + + expect(messageInputError).to.equal('Message body cannot be blank.'); + }); + it('displays an error on attempt to save a draft with attachments', async () => { const customProps = { ...draftMessage, From c3716be46a76291d924ff56dbb339180c47f6364 Mon Sep 17 00:00:00 2001 From: Kyle Cardwell Date: Mon, 2 Dec 2024 11:57:13 -0700 Subject: [PATCH 3/4] added signature check to reply drafts --- .../components/ComposeForm/ReplyDraftItem.jsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/applications/mhv-secure-messaging/components/ComposeForm/ReplyDraftItem.jsx b/src/applications/mhv-secure-messaging/components/ComposeForm/ReplyDraftItem.jsx index 30d442e84888..582b55d289eb 100644 --- a/src/applications/mhv-secure-messaging/components/ComposeForm/ReplyDraftItem.jsx +++ b/src/applications/mhv-secure-messaging/components/ComposeForm/ReplyDraftItem.jsx @@ -107,7 +107,7 @@ const ReplyDraftItem = props => { clearTimeout(timeoutId); }; - const formattededSignature = useMemo( + const formattedSignature = useMemo( () => { return messageSignatureFormatter(signature); }, @@ -152,14 +152,18 @@ const ReplyDraftItem = props => { const checkMessageValidity = useCallback( () => { let messageValid = true; - if (messageBody === '' || messageBody.match(/^[\s]+$/)) { + if ( + messageBody === '' || + messageBody.match(/^[\s]+$/) || + (formattedSignature && messageBody.trim() === formattedSignature.trim()) + ) { setBodyError(ErrorMessages.ComposeForm.BODY_REQUIRED); messageValid = false; } setMessageInvalid(!messageValid); return { messageValid }; }, - [messageBody], + [formattedSignature, messageBody], ); const messageBodyHandler = e => { @@ -523,7 +527,7 @@ const ReplyDraftItem = props => { draftSequence ? `-${draftSequence}` : '' }`} onInput={messageBodyHandler} - value={draft?.body || formattededSignature} // populate with the signature, unless there is a saved draft + value={draft?.body || formattedSignature} // populate with the signature, unless there is a saved draft error={bodyError} onFocus={e => { setCaretToPos(e.target.shadowRoot.querySelector('textarea'), 0); From d52b776e50429a6e684a37bd890373dfab5d2cfe Mon Sep 17 00:00:00 2001 From: fazilqa <127263470+fazilqa@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:34:58 -0800 Subject: [PATCH 4/4] add save reply-draft errors tests --- .../tests/e2e/pages/PatientInboxPage.js | 12 ++-- .../e2e/pages/PatientMessageDetailsPage.js | 2 +- ...essaging-save-draft-errors.cypress.spec.js | 2 +- ...ng-save-reply-draft-errors.cypress.spec.js | 70 +++++++++++++++++++ 4 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 src/applications/mhv-secure-messaging/tests/e2e/secure-messaging-save-reply-draft-errors.cypress.spec.js diff --git a/src/applications/mhv-secure-messaging/tests/e2e/pages/PatientInboxPage.js b/src/applications/mhv-secure-messaging/tests/e2e/pages/PatientInboxPage.js index 99c9b3188c09..e564ef85ae54 100644 --- a/src/applications/mhv-secure-messaging/tests/e2e/pages/PatientInboxPage.js +++ b/src/applications/mhv-secure-messaging/tests/e2e/pages/PatientInboxPage.js @@ -347,13 +347,13 @@ class PatientInboxPage { }; navigateToInterstitialPage = () => { - cy.intercept( - 'GET', - Paths.SM_API_EXTENDED + Paths.SIGNATURE, - mockSignature, - ).as('signature'); + // cy.intercept( + // 'GET', + // Paths.SM_API_EXTENDED + Paths.SIGNATURE, + // mockSignature, + // ).as('signature'); cy.get(Locators.LINKS.CREATE_NEW_MESSAGE).click({ force: true }); - cy.wait('@signature'); + // cy.wait('@signature'); }; navigateToComposePageByKeyboard = () => { diff --git a/src/applications/mhv-secure-messaging/tests/e2e/pages/PatientMessageDetailsPage.js b/src/applications/mhv-secure-messaging/tests/e2e/pages/PatientMessageDetailsPage.js index c6abd686d84f..ea8159c4bde3 100644 --- a/src/applications/mhv-secure-messaging/tests/e2e/pages/PatientMessageDetailsPage.js +++ b/src/applications/mhv-secure-messaging/tests/e2e/pages/PatientMessageDetailsPage.js @@ -355,7 +355,7 @@ class PatientMessageDetailsPage { 'GET', `${Paths.SM_API_EXTENDED}/${singleThreadData.data[0].id}/thread*`, singleThreadData, - ).as('replyThread'); + ).as('replyThreadInfo'); cy.get(Locators.BUTTONS.REPLY).click({ force: true }); PatientInterstitialPage.getContinueButton().click(); diff --git a/src/applications/mhv-secure-messaging/tests/e2e/secure-messaging-save-draft-errors.cypress.spec.js b/src/applications/mhv-secure-messaging/tests/e2e/secure-messaging-save-draft-errors.cypress.spec.js index 9e5086de8cc3..b6867181001d 100644 --- a/src/applications/mhv-secure-messaging/tests/e2e/secure-messaging-save-draft-errors.cypress.spec.js +++ b/src/applications/mhv-secure-messaging/tests/e2e/secure-messaging-save-draft-errors.cypress.spec.js @@ -3,7 +3,7 @@ import PatientInboxPage from './pages/PatientInboxPage'; import PatientComposePage from './pages/PatientComposePage'; import { AXE_CONTEXT, Data } from './utils/constants'; -describe('Secure Messaging Compose Errors', () => { +describe('SM COMPOSE ERRORS', () => { beforeEach(() => { SecureMessagingSite.login(); PatientInboxPage.loadInboxMessages(); diff --git a/src/applications/mhv-secure-messaging/tests/e2e/secure-messaging-save-reply-draft-errors.cypress.spec.js b/src/applications/mhv-secure-messaging/tests/e2e/secure-messaging-save-reply-draft-errors.cypress.spec.js new file mode 100644 index 000000000000..2df3dc3edc7e --- /dev/null +++ b/src/applications/mhv-secure-messaging/tests/e2e/secure-messaging-save-reply-draft-errors.cypress.spec.js @@ -0,0 +1,70 @@ +import SecureMessagingSite from './sm_site/SecureMessagingSite'; +import PatientMessageDetailsPage from './pages/PatientMessageDetailsPage'; +import PatientInboxPage from './pages/PatientInboxPage'; +import PatientMessageDraftsPage from './pages/PatientMessageDraftsPage'; +import PatientReplyPage from './pages/PatientReplyPage'; +import mockMessages from './fixtures/messages-response.json'; +import mockSingleThread from './fixtures/thread-response.json'; +import { AXE_CONTEXT, Data, Locators } from './utils/constants'; +import PatientComposePage from './pages/PatientComposePage'; + +describe('SM REPLY ERRORS', () => { + const bodyText = 'Updated body text'; + const singleMessage = { data: mockSingleThread.data[0] }; + singleMessage.data.attributes.body = `${bodyText}\n\n\nName\nTitletest`; + + beforeEach(() => { + SecureMessagingSite.login(); + PatientInboxPage.loadInboxMessages(mockMessages); + PatientInboxPage.loadSingleThread(mockSingleThread); + PatientMessageDetailsPage.clickReplyButton(mockSingleThread); + }); + + it('verify empty message body error', () => { + PatientMessageDraftsPage.saveNewDraftMessage( + mockSingleThread, + singleMessage, + ); + PatientComposePage.verifyErrorText(Data.BODY_CANNOT_BLANK); + PatientComposePage.verifyFocusOnErrorMessage('TEXTAREA'); + + cy.injectAxe(); + cy.axeCheck(AXE_CONTEXT); + }); + + it('verify deleted data in body error', () => { + SecureMessagingSite.login(); + PatientInboxPage.loadInboxMessages(mockMessages); + PatientInboxPage.loadSingleThread(mockSingleThread); + PatientMessageDetailsPage.clickReplyButton(mockSingleThread); + + PatientReplyPage.getMessageBodyField().type(bodyText, { + force: true, + }); + + PatientMessageDraftsPage.saveNewDraftMessage( + mockSingleThread, + singleMessage, + ); + + cy.get(Locators.ALERTS.SAVE_DRAFT).should( + 'include.text', + Data.MESSAGE_WAS_SAVED, + ); + + PatientReplyPage.getMessageBodyField().clear({ + force: true, + }); + + PatientMessageDraftsPage.saveNewDraftMessage( + mockSingleThread, + singleMessage, + ); + + PatientComposePage.verifyErrorText(Data.BODY_CANNOT_BLANK); + PatientComposePage.verifyFocusOnErrorMessage('TEXTAREA'); + + cy.injectAxe(); + cy.axeCheck(AXE_CONTEXT); + }); +});