diff --git a/cypress/e2e/donor/what-is-vouching.cy.js b/cypress/e2e/donor/what-is-vouching.cy.js new file mode 100644 index 0000000000..24970ba526 --- /dev/null +++ b/cypress/e2e/donor/what-is-vouching.cy.js @@ -0,0 +1,34 @@ +describe('what is vouching', () => { + beforeEach(() => { + cy.visit('/fixtures?redirect=/what-is-vouching&progress=payForTheLpa') + cy.url().should('contain', '/what-is-vouching') + cy.checkA11yApp() + }) + + it('can confirm has a voucher', () => { + cy.get('input[name="yes-no"]').check('yes', { force: true }); + cy.contains('button', 'Save and continue').click(); + + cy.url().should('contain', '/enter-voucher') + }) + + it('can confirm has not got a voucher', () => { + cy.get('input[name="yes-no"]').check('no', { force: true }); + cy.contains('button', 'Save and continue').click(); + + cy.url().should('contain', '/task-list') + }) + + it('errors when option not selected', () => { + cy.contains('button', 'Save and continue').click(); + + cy.url().should('contain', '/what-is-vouching') + cy.checkA11yApp() + + cy.get('.govuk-error-summary').within(() => { + cy.contains('Select yes if you know someone and they have agreed to vouch for you'); + }); + + cy.contains('.govuk-error-message', 'Select yes if you know someone and they have agreed to vouch for you'); + }) +}) diff --git a/internal/actor/donor_provided.go b/internal/actor/donor_provided.go index 2d4e5f04d4..38e42edb91 100644 --- a/internal/actor/donor_provided.go +++ b/internal/actor/donor_provided.go @@ -121,6 +121,8 @@ type DonorProvidedDetails struct { // Version is the number of times the LPA has been updated (auto-incremented on PUT) Version int `hash:"-"` + // HasAVoucher indicates if the donor knows someone who can vouch for them + HasAVoucher form.YesNo // Voucher is a person the donor has nominated to vouch for their identity Voucher Voucher diff --git a/internal/actor/donor_provided_test.go b/internal/actor/donor_provided_test.go index 76ea5ffd12..c57c1c09d1 100644 --- a/internal/actor/donor_provided_test.go +++ b/internal/actor/donor_provided_test.go @@ -34,14 +34,14 @@ func TestGenerateHash(t *testing.T) { } // DO change this value to match the updates - const modified uint64 = 0x1544481895e4a269 + const modified uint64 = 0x489c8d6c0bf0060 // DO NOT change these initial hash values. If a field has been added/removed // you will need to handle the version gracefully by modifying // (*DonorProvidedDetails).HashInclude and adding another testcase for the new // version. testcases := map[uint8]uint64{ - 0: 0xfc629cb12d4374fb, + 0: 0x9ff13a8f95ca2df8, } for version, initial := range testcases { @@ -91,13 +91,13 @@ func TestGenerateCheckedHash(t *testing.T) { } // DO change this value to match the updates - const modified uint64 = 0xb4f40b404256ad9 + const modified uint64 = 0x4cbba4134d45f904 // DO NOT change these initial hash values. If a field has been added/removed // you will need to handle the version gracefully by modifying // toCheck.HashInclude and adding another testcase for the new version. testcases := map[uint8]uint64{ - 0: 0xf9b8a8058a8f224a, + 0: 0xadda078438d084b2, } for version, initial := range testcases { diff --git a/internal/page/donor/register.go b/internal/page/donor/register.go index b1f441260e..688de07082 100644 --- a/internal/page/donor/register.go +++ b/internal/page/donor/register.go @@ -362,7 +362,7 @@ func Register( handleWithDonor(page.Paths.UnableToConfirmIdentity, page.None, Guidance(tmpls.Get("unable_to_confirm_identity.gohtml"))) handleWithDonor(page.Paths.WhatIsVouching, page.CanGoBack, - Guidance(tmpls.Get("unable_to_confirm_identity.gohtml"))) + WhatIsVouching(tmpls.Get("what_is_vouching.gohtml"), donorStore)) handleWithDonor(page.Paths.EnterVoucher, page.CanGoBack, EnterVoucher(tmpls.Get("enter_voucher.gohtml"), donorStore)) handleWithDonor(page.Paths.ConfirmPersonAllowedToVouch, page.CanGoBack, diff --git a/internal/page/donor/what_is_vouching.go b/internal/page/donor/what_is_vouching.go new file mode 100644 index 0000000000..979e6d690e --- /dev/null +++ b/internal/page/donor/what_is_vouching.go @@ -0,0 +1,47 @@ +package donor + +import ( + "net/http" + + "github.com/ministryofjustice/opg-go-common/template" + "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" + "github.com/ministryofjustice/opg-modernising-lpa/internal/form" + "github.com/ministryofjustice/opg-modernising-lpa/internal/page" + "github.com/ministryofjustice/opg-modernising-lpa/internal/validation" +) + +type whatIsVouchingData struct { + App page.AppData + Errors validation.List + Form *form.YesNoForm +} + +func WhatIsVouching(tmpl template.Template, donorStore DonorStore) Handler { + return func(appData page.AppData, w http.ResponseWriter, r *http.Request, donor *actor.DonorProvidedDetails) error { + data := &whatIsVouchingData{ + App: appData, + Form: form.NewYesNoForm(donor.HasAVoucher), + } + + if r.Method == http.MethodPost { + f := form.ReadYesNoForm(r, "yesIfHaveSomeoneCanVouchForYou") + data.Errors = f.Validate() + + if data.Errors.None() { + donor.HasAVoucher = f.YesNo + if err := donorStore.Put(r.Context(), donor); err != nil { + return err + } + + if donor.HasAVoucher.IsYes() { + return page.Paths.EnterVoucher.Redirect(w, r, appData, donor) + } else { + // temp until no flow built + return page.Paths.TaskList.Redirect(w, r, appData, donor) + } + } + } + + return tmpl(w, data) + } +} diff --git a/internal/page/donor/what_is_vouching_test.go b/internal/page/donor/what_is_vouching_test.go new file mode 100644 index 0000000000..df180678a7 --- /dev/null +++ b/internal/page/donor/what_is_vouching_test.go @@ -0,0 +1,102 @@ +package donor + +import ( + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + + "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" + "github.com/ministryofjustice/opg-modernising-lpa/internal/form" + "github.com/ministryofjustice/opg-modernising-lpa/internal/page" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestGetWhatIsVouching(t *testing.T) { + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "/", nil) + + template := newMockTemplate(t) + template.EXPECT(). + Execute(w, &whatIsVouchingData{ + App: testAppData, + Form: &form.YesNoForm{ + YesNo: form.Yes, + FieldName: form.FieldNames.YesNo, + Options: form.YesNoValues, + }, + }). + Return(nil) + + err := WhatIsVouching(template.Execute, nil)(testAppData, w, r, &actor.DonorProvidedDetails{HasAVoucher: form.Yes}) + + assert.Nil(t, err) +} + +func TestGetWhatIsVouchingWhenTemplateError(t *testing.T) { + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "/", nil) + + template := newMockTemplate(t) + template.EXPECT(). + Execute(mock.Anything, mock.Anything). + Return(expectedError) + + err := WhatIsVouching(template.Execute, nil)(testAppData, w, r, &actor.DonorProvidedDetails{}) + + assert.Error(t, err) +} + +func TestPostWhatIsVouching(t *testing.T) { + testcases := map[form.YesNo]string{ + form.Yes: page.Paths.EnterVoucher.Format("lpa-id"), + form.No: page.Paths.TaskList.Format("lpa-id"), + } + + for yesNo, path := range testcases { + t.Run(yesNo.String(), func(t *testing.T) { + f := url.Values{ + "yes-no": {yesNo.String()}, + } + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode())) + r.Header.Add("Content-Type", page.FormUrlEncoded) + + donorStore := newMockDonorStore(t) + donorStore.EXPECT(). + Put(r.Context(), &actor.DonorProvidedDetails{LpaID: "lpa-id", HasAVoucher: yesNo}). + Return(nil) + + err := WhatIsVouching(nil, donorStore)(testAppData, w, r, &actor.DonorProvidedDetails{LpaID: "lpa-id"}) + resp := w.Result() + + assert.Nil(t, err) + assert.Equal(t, http.StatusFound, resp.StatusCode) + assert.Equal(t, path, resp.Header.Get("Location")) + }) + } +} + +func TestPostWhatIsVouchingWhenDonorStoreError(t *testing.T) { + f := url.Values{ + "yes-no": {form.Yes.String()}, + } + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode())) + r.Header.Add("Content-Type", page.FormUrlEncoded) + + donorStore := newMockDonorStore(t) + donorStore.EXPECT(). + Put(mock.Anything, mock.Anything). + Return(expectedError) + + err := WhatIsVouching(nil, donorStore)(testAppData, w, r, &actor.DonorProvidedDetails{LpaID: "lpa-id"}) + resp := w.Result() + + assert.Error(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) +} diff --git a/lang/cy.json b/lang/cy.json index 18947f7634..e4b605a945 100644 --- a/lang/cy.json +++ b/lang/cy.json @@ -1222,5 +1222,11 @@ "payingNoFee": "Welsh", "payingHalfFee": "Welsh", "aRepeatApplicationDiscount": "Welsh", - "aHardshipApplication": "Welsh" + "aHardshipApplication": "Welsh", + "whatIsVouching": "Welsh", + "whatIsVouchingContent": "
Welsh
Welsh:
Welsh:
Welsh
Welsh:
Welsh
", + "isThereSomeoneWhoCanVouchForYou": "Welsh", + "yesIKnowSomeone": "Welsh", + "noIDoNotKnowSomeone": "Welsh", + "yesIfHaveSomeoneCanVouchForYou": "Welsh" } diff --git a/lang/en.json b/lang/en.json index 0934f91f31..ff9cf52b0c 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1154,5 +1154,11 @@ "payingNoFee": "paying no fee (exemption)", "payingHalfFee": "paying a half fee (remission)", "aRepeatApplicationDiscount": "a repeat application discount", - "aHardshipApplication": "a hardship application (fee waiver)" + "aHardshipApplication": "a hardship application (fee waiver)", + "whatIsVouching": "What is vouching?", + "whatIsVouchingContent": "Vouching allows someone you know well to confirm your identity.
The person you ask to vouch for you must:
The person you choose cannot be:
You must ask the person if they are willing to vouch for you first. If they are willing, they will need to confirm their own identity using GOV.UK One Login.
They will need either:
We will then ask them to sign a declaration and confirm your identity.
", + "isThereSomeoneWhoCanVouchForYou": "Is there someone who can vouch for you?", + "yesIKnowSomeone": "Yes, I know someone and they have agreed to vouch for me", + "noIDoNotKnowSomeone": "No, I do not know someone who can vouch for me", + "yesIfHaveSomeoneCanVouchForYou": "yes if you know someone and they have agreed to vouch for you" } diff --git a/web/template/donor/what_is_vouching.gohtml b/web/template/donor/what_is_vouching.gohtml new file mode 100644 index 0000000000..9db2a42b24 --- /dev/null +++ b/web/template/donor/what_is_vouching.gohtml @@ -0,0 +1,32 @@ +{{ template "page" . }} + +{{ define "pageTitle" }}{{ tr .App "whatIsVouching" }}{{ end }} + +{{ define "main" }} +