diff --git a/cypress/e2e/donor/confirm-your-identity.cy.js b/cypress/e2e/donor/confirm-your-identity.cy.js index 95b57ec77..159acbba0 100644 --- a/cypress/e2e/donor/confirm-your-identity.cy.js +++ b/cypress/e2e/donor/confirm-your-identity.cy.js @@ -164,8 +164,7 @@ describe('Confirm your identity', () => { cy.get('main').should('not.contain', '2 January 2000'); }) - - it('can withdraw LPA', () => { + it('can get OPG to check', () => { cy.visit('/fixtures?redirect=/task-list&progress=payForTheLpa'); cy.contains('li', "Confirm your identity") .should('contain', 'Not started') @@ -191,8 +190,14 @@ describe('Confirm your identity', () => { cy.contains('label', 'No').click(); cy.contains('button', 'Continue').click(); - cy.url().should('contain', '/withdraw-this-lpa'); + cy.url().should('contain', '/identity-details'); cy.checkA11yApp(); + + cy.contains('The Office of the Public Guardian (OPG) is checking your mismatched identity details'); + cy.contains('a', 'Return to task list').click(); + + cy.contains('li', "Confirm your identity") + .should('contain', 'Pending'); }) it('errors when option not selected', () => { diff --git a/cypress/e2e/donor/progress.cy.js b/cypress/e2e/donor/progress.cy.js index d33079bb4..fee5b64fe 100644 --- a/cypress/e2e/donor/progress.cy.js +++ b/cypress/e2e/donor/progress.cy.js @@ -236,5 +236,27 @@ describe('Progress', () => { cy.contains('Your LPA is awaiting registration'); cy.contains('at the end of our statutory 4-week waiting period'); }); + + it('when continue with identity mismatch', () => { + cy.visit(`/fixtures?redirect=/task-list&progress=payForTheLpa`); + + cy.contains('a', 'Confirm your identity').click(); + cy.contains('button', 'Continue').click(); + cy.origin('http://localhost:7012', () => { + cy.contains('label', 'Charlie Cooper').click(); + cy.contains('button', 'Continue').click(); + }); + + cy.contains('label', 'No').click(); + cy.contains('button', 'Continue').click(); + + cy.visitLpa('/progress'); + + cy.checkA11yApp(); + cy.contains('Important: 1 notification from OPG'); + + cy.contains('Confirmation of identity pending'); + cy.contains('You do not need to take any action'); + }); }); }); diff --git a/internal/donor/donordata/provided.go b/internal/donor/donordata/provided.go index 2d2180eff..9e36a6ba2 100644 --- a/internal/donor/donordata/provided.go +++ b/internal/donor/donordata/provided.go @@ -129,6 +129,9 @@ type Provided struct { // RegisteringWithCourtOfProtection is set when the donor wishes to take the // Lpa to the Court of Protection for registration. RegisteringWithCourtOfProtection bool `checkhash:"-"` + // ContinueWithMismatchedIdentity is set when the donor wishes to continue + // their application with mismatched identity details + ContinueWithMismatchedIdentity bool `checkhash:"-"` // Version is the number of times the LPA has been updated (auto-incremented // on PUT) Version int `hash:"-" checkhash:"-"` diff --git a/internal/donor/donordata/provided_test.go b/internal/donor/donordata/provided_test.go index a22021d8a..0ea49fc81 100644 --- a/internal/donor/donordata/provided_test.go +++ b/internal/donor/donordata/provided_test.go @@ -77,14 +77,14 @@ func TestGenerateHash(t *testing.T) { } // DO change this value to match the updates - const modified uint64 = 0xba6539282f5874e4 + const modified uint64 = 0xe3bd213ba882ed4f // DO NOT change these initial hash values. If a field has been added/removed // you will need to handle the version gracefully by modifying // (*Provided).HashInclude and adding another testcase for the new // version. testcases := map[uint8]uint64{ - 0: 0xd49c9f60fe98b302, + 0: 0x3730007cdf45a8cd, } for version, initial := range testcases { diff --git a/internal/donor/donorpage/identity_details.go b/internal/donor/donorpage/identity_details.go index 15d641885..2a5d72714 100644 --- a/internal/donor/donorpage/identity_details.go +++ b/internal/donor/donorpage/identity_details.go @@ -64,7 +64,14 @@ func IdentityDetails(tmpl template.Template, donorStore DonorStore) Handler { return donor.PathIdentityDetails.RedirectQuery(w, r, appData, provided, url.Values{"detailsUpdated": {"1"}}) } else { - return donor.PathWithdrawThisLpa.Redirect(w, r, appData, provided) + provided.ContinueWithMismatchedIdentity = true + provided.Tasks.ConfirmYourIdentity = task.IdentityStatePending + + if err := donorStore.Put(r.Context(), provided); err != nil { + return err + } + + return donor.PathIdentityDetails.Redirect(w, r, appData, provided) } } } diff --git a/internal/donor/donorpage/identity_details_test.go b/internal/donor/donorpage/identity_details_test.go index 5405323b1..e754961d0 100644 --- a/internal/donor/donorpage/identity_details_test.go +++ b/internal/donor/donorpage/identity_details_test.go @@ -19,7 +19,7 @@ import ( "github.com/stretchr/testify/mock" ) -func TestOneLoginIdentityDetailsDataDetailsMatch(t *testing.T) { +func TestIdentityDetailsDataDetailsMatch(t *testing.T) { assert.True(t, identityDetailsData{ FirstNamesMatch: true, LastNameMatch: true, @@ -30,7 +30,7 @@ func TestOneLoginIdentityDetailsDataDetailsMatch(t *testing.T) { assert.False(t, identityDetailsData{}.DetailsMatch()) } -func TestGetOneLoginIdentityDetails(t *testing.T) { +func TestGetIdentityDetails(t *testing.T) { dob := date.New("1", "2", "3") testcases := map[string]struct { @@ -98,13 +98,7 @@ func TestGetOneLoginIdentityDetails(t *testing.T) { } } -func TestPostOneLoginIdentityDetailsWhenYes(t *testing.T) { - f := url.Values{form.FieldNames.YesNo: {form.Yes.String()}} - - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode())) - r.Header.Set("Content-Type", "application/x-www-form-urlencoded") - +func TestPostIdentityDetails(t *testing.T) { existingDob := date.New("1", "2", "3") identityDob := date.New("4", "5", "6") @@ -116,58 +110,77 @@ func TestPostOneLoginIdentityDetailsWhenYes(t *testing.T) { } updated.UpdateCheckedHash() - donorStore := newMockDonorStore(t) - donorStore.EXPECT(). - Put(r.Context(), updated). - Return(nil) - - err := IdentityDetails(nil, donorStore)(testAppData, w, r, &donordata.Provided{ - LpaID: "lpa-id", - Donor: donordata.Donor{FirstNames: "b", LastName: "b", DateOfBirth: existingDob, Address: testAddress}, - IdentityUserData: identity.UserData{FirstNames: "B", LastName: "B", DateOfBirth: identityDob, CurrentAddress: place.Address{Line1: "a"}}, - }) - resp := w.Result() + testcases := map[form.YesNo]struct { + provided *donordata.Provided + redirect string + }{ + form.Yes: { + provided: updated, + redirect: donor.PathIdentityDetails.Format("lpa-id") + "?detailsUpdated=1", + }, + form.No: { + provided: &donordata.Provided{ + LpaID: "lpa-id", + Donor: donordata.Donor{FirstNames: "b", LastName: "b", DateOfBirth: existingDob, Address: testAddress}, + IdentityUserData: identity.UserData{FirstNames: "B", LastName: "B", DateOfBirth: identityDob, CurrentAddress: place.Address{Line1: "a"}}, + Tasks: donordata.Tasks{ConfirmYourIdentity: task.IdentityStatePending}, + ContinueWithMismatchedIdentity: true, + }, + redirect: donor.PathIdentityDetails.Format("lpa-id"), + }, + } - assert.Nil(t, err) - assert.Equal(t, http.StatusFound, resp.StatusCode) - assert.Equal(t, donor.PathIdentityDetails.Format("lpa-id")+"?detailsUpdated=1", resp.Header.Get("Location")) -} + for yesNo, tc := range testcases { + t.Run(yesNo.String(), func(t *testing.T) { + f := url.Values{form.FieldNames.YesNo: {yesNo.String()}} -func TestPostOneLoginIdentityDetailsWhenNo(t *testing.T) { - f := url.Values{form.FieldNames.YesNo: {form.No.String()}} + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode())) + r.Header.Set("Content-Type", "application/x-www-form-urlencoded") - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode())) - r.Header.Set("Content-Type", "application/x-www-form-urlencoded") + donorStore := newMockDonorStore(t) + donorStore.EXPECT(). + Put(r.Context(), tc.provided). + Return(nil) - err := IdentityDetails(nil, nil)(testAppData, w, r, &donordata.Provided{LpaID: "lpa-id"}) - resp := w.Result() + err := IdentityDetails(nil, donorStore)(testAppData, w, r, &donordata.Provided{ + LpaID: "lpa-id", + Donor: donordata.Donor{FirstNames: "b", LastName: "b", DateOfBirth: existingDob, Address: testAddress}, + IdentityUserData: identity.UserData{FirstNames: "B", LastName: "B", DateOfBirth: identityDob, CurrentAddress: place.Address{Line1: "a"}}, + }) + resp := w.Result() - assert.Nil(t, err) - assert.Equal(t, http.StatusFound, resp.StatusCode) - assert.Equal(t, donor.PathWithdrawThisLpa.Format("lpa-id"), resp.Header.Get("Location")) + assert.Nil(t, err) + assert.Equal(t, http.StatusFound, resp.StatusCode) + assert.Equal(t, tc.redirect, resp.Header.Get("Location")) + }) + } } -func TestPostOneLoginIdentityDetailsWhenDonorStoreError(t *testing.T) { - f := url.Values{form.FieldNames.YesNo: {form.Yes.String()}} +func TestPostIdentityDetailsWhenDonorStoreError(t *testing.T) { + for _, yesNo := range []form.YesNo{form.Yes, form.No} { + t.Run(yesNo.String(), func(t *testing.T) { + f := url.Values{form.FieldNames.YesNo: {yesNo.String()}} - w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode())) - r.Header.Set("Content-Type", "application/x-www-form-urlencoded") + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode())) + r.Header.Set("Content-Type", "application/x-www-form-urlencoded") - donorStore := newMockDonorStore(t) - donorStore.EXPECT(). - Put(r.Context(), mock.Anything). - Return(expectedError) + donorStore := newMockDonorStore(t) + donorStore.EXPECT(). + Put(r.Context(), mock.Anything). + Return(expectedError) - err := IdentityDetails(nil, donorStore)(testAppData, w, r, &donordata.Provided{}) - resp := w.Result() + err := IdentityDetails(nil, donorStore)(testAppData, w, r, &donordata.Provided{}) + resp := w.Result() - assert.Equal(t, expectedError, err) - assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, expectedError, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + }) + } } -func TestPostOneLoginIdentityDetailsWhenValidationError(t *testing.T) { +func TestPostIdentityDetailsWhenValidationError(t *testing.T) { f := url.Values{form.FieldNames.YesNo: {""}} w := httptest.NewRecorder() diff --git a/internal/donor/donorpage/progress.go b/internal/donor/donorpage/progress.go index 4dc09f1a1..6493672e2 100644 --- a/internal/donor/donorpage/progress.go +++ b/internal/donor/donorpage/progress.go @@ -247,6 +247,13 @@ func Progress(tmpl template.Template, lpaStoreResolvingService LpaStoreResolving }) } + if donor.Tasks.ConfirmYourIdentity.IsPending() && donor.ContinueWithMismatchedIdentity { + data.InfoNotifications = append(data.InfoNotifications, progressNotification{ + Heading: appData.Localizer.T("confirmationOfIdentityPending"), + Body: appData.Localizer.T("youDoNotNeedToTakeAnyAction"), + }) + } + return tmpl(w, data) } } diff --git a/internal/donor/donorpage/progress_test.go b/internal/donor/donorpage/progress_test.go index e801304b2..cd04d5292 100644 --- a/internal/donor/donorpage/progress_test.go +++ b/internal/donor/donorpage/progress_test.go @@ -635,6 +635,26 @@ func TestGetProgress(t *testing.T) { return l }, }, + "identity mismatch pending": { + donor: &donordata.Provided{ + ContinueWithMismatchedIdentity: true, + IdentityUserData: identity.UserData{Status: identity.StatusConfirmed}, + Tasks: donordata.Tasks{ + ConfirmYourIdentity: task.IdentityStatePending, + }, + }, + lpa: &lpadata.Lpa{}, + setupCertificateProviderStore: certificateProviderStoreNotFound, + infoNotifications: []progressNotification{ + {Heading: "H", Body: "B"}, + }, + setupLocalizer: func(t *testing.T) *mockLocalizer { + l := newMockLocalizer(t) + l.EXPECT().T("confirmationOfIdentityPending").Return("H") + l.EXPECT().T("youDoNotNeedToTakeAnyAction").Return("B") + return l + }, + }, } for name, tc := range testCases { diff --git a/lang/cy.json b/lang/cy.json index a57abe0cd..e6d1b3d29 100644 --- a/lang/cy.json +++ b/lang/cy.json @@ -1551,5 +1551,9 @@ "voucherHasSuccessfullyConfirmedYourIdentity": "
", "yourLpaIsAwaitingRegistration": "Welsh", "theOpgWillRegisterYourLpaAtEndOfWaitingPeriod": "Welsh", - "viewYourLpa": "Welsh" + "viewYourLpa": "Welsh", + "youCanUpdateTheDetailsOnYourLpaToMatchContent": "Welsh
", + "theOpgIsCheckingYourMismatchedIdentityDetails": "Welsh", + "someOfTheDetailsDoNotMatchYouAskedOpgToCheck": "Welsh
", + "confirmationOfIdentityPending": "Welsh" } diff --git a/lang/en.json b/lang/en.json index 219356612..8c08ca566 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1160,9 +1160,9 @@ "someDetailsDoNotMatchIdentityDetailsWarning": "Some of the details on your LPA do not match your confirmed identity details", "yourLPADetailsHaveBeenUpdatedToMatchIdentitySuccess": "Your LPA details have been updated to match your confirmed identity", "doesNotMatch": "Does not match", - "updateMyLPADetailsToMatchIdentityHint": "Update my LPA details to match my confirmed identity details.", - "iUnderstandThisWillWithdrawLPAHint": "I understand that this will revoke my LPA and I will no longer be able to access it.", - "youCanOnlyContinueIfDetailsMatchWarning": "You can only continue with this LPA if the details match your confirmed identity details", + "updateMyLPADetailsToMatchIdentityHint": "Update your details and continue to sign your LPA", + "iUnderstandThisWillWithdrawLPAHint": "You can continue to sign your LPA, but if you do not update your details and the mismatch is serious, OPG will not be able to register it", + "youCanOnlyContinueIfDetailsMatchWarning": "Once you apply to register your LPA through the Court of Protection, you cannot change your mind. If you decide to confirm your identity another way, you’ll need to create a new LPA.", "yesIfWouldLikeToUpdateDetails": "yes if you would like to update your details", "wouldYouLikeToUpdateDetailsToMatchIdentityDetails": "Would you like to update the details on your LPA to match your confirmed identity details?", "yourRegisteredLpaLanguageContent": "You can register your LPA in either English or Welsh. It is not possible to have copies in both languages.
It’s best to write your LPA in just one language to avoid confusion.
You may need to give organisations access to your LPA when you choose for your attorneys to use it. Organisations will also see it in your preferred language.
", @@ -1447,5 +1447,9 @@ "voucherHasSuccessfullyConfirmedYourIdentity": " ", "yourLpaIsAwaitingRegistration": "Your LPA is awaiting registration", "theOpgWillRegisterYourLpaAtEndOfWaitingPeriod": "The Office of the Public Guardian will register your LPA at the end of our statutory 4-week waiting period, if no objections are raised.", - "viewYourLpa": "View your LPA" + "viewYourLpa": "View your LPA", + "youCanUpdateTheDetailsOnYourLpaToMatchContent": "You can update the details on your LPA to match your confirmed identity details and continue to sign your LPA.
Or, you can ask the Office of the Public Guardian (OPG) to check the mismatch in your identity details.
If the mismatch is serious, OPG will not be able to register your LPA. We’ll contact you to ask if you want to apply to the Court of Protection to register your LPA.
It costs £408 to register your LPA through the Court of Protection and will take several months.
To delete this LPA, return to ‘Manage your LPAs’.
", + "theOpgIsCheckingYourMismatchedIdentityDetails": "The Office of the Public Guardian (OPG) is checking your mismatched identity details", + "someOfTheDetailsDoNotMatchYouAskedOpgToCheck": "Some of the details on your LPA do not match your confirmed identity details. You have asked OPG to check this mismatch.
We will contact you if you need to take further action.
", + "confirmationOfIdentityPending": "Confirmation of identity pending" } diff --git a/web/template/donor/identity_details.gohtml b/web/template/donor/identity_details.gohtml index 44570e847..b1a96815c 100644 --- a/web/template/donor/identity_details.gohtml +++ b/web/template/donor/identity_details.gohtml @@ -7,9 +7,10 @@ {{ define "main" }}