diff --git a/cypress/e2e/attorney/language-preferences.cy.js b/cypress/e2e/attorney/language-preferences.cy.js index 5032720d8a..7bf3208f03 100644 --- a/cypress/e2e/attorney/language-preferences.cy.js +++ b/cypress/e2e/attorney/language-preferences.cy.js @@ -21,9 +21,9 @@ describe('Your preferred language', () => { cy.checkA11yApp(); cy.get('.govuk-error-summary').within(() => { - cy.contains('Select which language you’d like us to use when we contact you'); + cy.contains('Select which language you would like us to use when we contact you'); }); - cy.contains('.govuk-fieldset .govuk-error-message', 'Select which language you’d like us to use when we contact you'); + cy.contains('.govuk-fieldset .govuk-error-message', 'Select which language you would like us to use when we contact you'); }) }) diff --git a/cypress/e2e/certificate-provider/language-preferences.cy.js b/cypress/e2e/certificate-provider/language-preferences.cy.js index 2b505e749b..247e638392 100644 --- a/cypress/e2e/certificate-provider/language-preferences.cy.js +++ b/cypress/e2e/certificate-provider/language-preferences.cy.js @@ -21,9 +21,9 @@ describe('Your preferred language', () => { cy.checkA11yApp(); cy.get('.govuk-error-summary').within(() => { - cy.contains('Select which language you’d like us to use when we contact you'); + cy.contains('Select which language you would like us to use when we contact you'); }); - cy.contains('.govuk-fieldset .govuk-error-message', 'Select which language you’d like us to use when we contact you'); + cy.contains('.govuk-fieldset .govuk-error-message', 'Select which language you would like us to use when we contact you'); }) }) diff --git a/cypress/e2e/donor/confirm-your-identity-and-sign.cy.js b/cypress/e2e/donor/confirm-your-identity-and-sign.cy.js index 640de83258..f94ada261a 100644 --- a/cypress/e2e/donor/confirm-your-identity-and-sign.cy.js +++ b/cypress/e2e/donor/confirm-your-identity-and-sign.cy.js @@ -40,6 +40,10 @@ describe('Confirm your identity and sign', () => { cy.contains('h3', "Replacement attorney"); cy.contains('a', 'Continue').click(); + cy.url().should('contain', '/your-lpa-language'); + cy.contains('label', 'Continue and register my LPA in English').click(); + cy.contains('button', 'Save and continue').click(); + cy.url().should('contain', '/your-legal-rights-and-responsibilities'); cy.checkA11yApp(); cy.contains('a', 'Continue to signing page').click(); @@ -104,6 +108,8 @@ describe('Confirm your identity and sign', () => { cy.contains('button', 'Continue').click(); cy.contains('a', 'Continue').click(); + cy.contains('label', 'Continue and register my LPA in English').click(); + cy.contains('button', 'Save and continue').click(); cy.contains('a', 'Continue to signing page').click(); cy.contains('label', 'I want to sign this LPA as a deed').click(); cy.contains('label', 'I want to apply to register this LPA').click(); @@ -144,6 +150,10 @@ describe('Confirm your identity and sign', () => { cy.contains('h3', "Replacement attorney"); cy.contains('a', 'Continue').click(); + cy.url().should('contain', '/your-lpa-language'); + cy.contains('label', 'Continue and register my LPA in English').click(); + cy.contains('button', 'Save and continue').click(); + cy.url().should('contain', '/your-legal-rights-and-responsibilities'); cy.checkA11yApp(); cy.contains('a', 'Continue to signing page').click(); @@ -287,9 +297,9 @@ describe('Confirm your identity and sign', () => { it('can update LPA details', () => { cy.visit('/fixtures?redirect=/task-list&progress=payForTheLpa'); cy.contains('li', "Confirm your identity and sign") - .should('contain', 'Not started') - .find('a') - .click(); + .should('contain', 'Not started') + .find('a') + .click(); cy.url().should('contain', '/how-to-confirm-your-identity-and-sign'); cy.checkA11yApp(); @@ -326,9 +336,9 @@ describe('Confirm your identity and sign', () => { it('can withdraw LPA', () => { cy.visit('/fixtures?redirect=/task-list&progress=payForTheLpa'); cy.contains('li', "Confirm your identity and sign") - .should('contain', 'Not started') - .find('a') - .click(); + .should('contain', 'Not started') + .find('a') + .click(); cy.url().should('contain', '/how-to-confirm-your-identity-and-sign'); cy.checkA11yApp(); @@ -360,9 +370,9 @@ describe('Confirm your identity and sign', () => { it('errors when option not selected', () => { cy.visit('/fixtures?redirect=/task-list&progress=payForTheLpa'); cy.contains('li', "Confirm your identity and sign") - .should('contain', 'Not started') - .find('a') - .click(); + .should('contain', 'Not started') + .find('a') + .click(); cy.url().should('contain', '/how-to-confirm-your-identity-and-sign'); cy.checkA11yApp(); diff --git a/cypress/e2e/donor/language-preferences.cy.js b/cypress/e2e/donor/language-preferences.cy.js index 5c7d9feba9..c74266ef48 100644 --- a/cypress/e2e/donor/language-preferences.cy.js +++ b/cypress/e2e/donor/language-preferences.cy.js @@ -4,8 +4,9 @@ describe('Your preferred language', () => { cy.url().should('contain', '/your-preferred-language') }); - it('can choose a language contact preference', () => { - cy.get('[name="language-preference"]').check('en', { force: true }) + it('can choose language preferences', () => { + cy.get('[name="contact-language"]').check('en', { force: true }) + cy.get('[name="lpa-language"]').check('en', { force: true }) cy.checkA11yApp(); @@ -21,9 +22,11 @@ describe('Your preferred language', () => { cy.checkA11yApp(); cy.get('.govuk-error-summary').within(() => { - cy.contains('Select which language you’d like us to use when we contact you'); + cy.contains('Select which language you would like us to use when we contact you'); + cy.contains('Select the language in which you would like your LPA registered'); }); - cy.contains('.govuk-fieldset .govuk-error-message', 'Select which language you’d like us to use when we contact you'); + cy.contains('.govuk-fieldset .govuk-error-message', 'Select which language you would like us to use when we contact you'); + cy.contains('.govuk-fieldset .govuk-error-message', 'Select the language in which you would like your LPA registered'); }) }) diff --git a/cypress/e2e/donor/provide-your-details.cy.js b/cypress/e2e/donor/provide-your-details.cy.js index b4c8367611..6557e10e0f 100644 --- a/cypress/e2e/donor/provide-your-details.cy.js +++ b/cypress/e2e/donor/provide-your-details.cy.js @@ -18,7 +18,8 @@ describe('Provide your details', () => { AddressFormAssertions.assertCanAddAddressFromSelect(); - cy.get('[name="language-preference"]').check('en', { force: true }); + cy.get('[name="contact-language"]').check('en', { force: true }); + cy.get('[name="lpa-language"]').check('en', { force: true }); cy.contains('button', 'Save and continue').click(); cy.get('#f-lpa-type').check('property-and-affairs', { force: true }); diff --git a/cypress/e2e/donor/signing-on-behalf.cy.js b/cypress/e2e/donor/signing-on-behalf.cy.js index dc0232e97d..3b98e8450c 100644 --- a/cypress/e2e/donor/signing-on-behalf.cy.js +++ b/cypress/e2e/donor/signing-on-behalf.cy.js @@ -14,6 +14,10 @@ describe('Signing on behalf of the donor', () => { cy.url().should('contain', '/read-your-lpa'); cy.contains('a', 'Continue').click(); + cy.url().should('contain', '/your-lpa-language'); + cy.contains('label', 'Continue and register my LPA in English').click(); + cy.contains('button', 'Save and continue').click(); + cy.url().should('contain', '/your-legal-rights-and-responsibilities'); cy.contains('a', 'Continue to signing page').click(); }); diff --git a/cypress/e2e/supporter/dashboard.cy.js b/cypress/e2e/supporter/dashboard.cy.js index 30540362d3..de23b61b2c 100644 --- a/cypress/e2e/supporter/dashboard.cy.js +++ b/cypress/e2e/supporter/dashboard.cy.js @@ -41,7 +41,8 @@ describe('Dashboard', () => { AddressFormAssertions.assertCanAddAddressFromSelect() - cy.get('[name="language-preference"]').check('en', { force: true }) + cy.get('[name="contact-language"]').check('en', { force: true }) + cy.get('[name="lpa-language"]').check('en', { force: true }) cy.contains('button', 'Save and continue').click() cy.get('#f-lpa-type').check('property-and-affairs'); diff --git a/internal/actor/donor.go b/internal/actor/donor.go index 17a561be6b..85ae955b14 100644 --- a/internal/actor/donor.go +++ b/internal/actor/donor.go @@ -35,6 +35,8 @@ type Donor struct { Channel Channel // ContactLanguagePreference is the language the donor prefers to receive notifications in ContactLanguagePreference localize.Lang + // LpaLanguagePreference is the language the donor prefers to receive the registered LPA in + LpaLanguagePreference localize.Lang } func (d Donor) FullName() string { diff --git a/internal/actor/donor_provided_test.go b/internal/actor/donor_provided_test.go index 2ac87a4e21..4a8601637b 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 = 0xe8ee03e19c4313a1 + const modified uint64 = 0x60cf41a52d0f9ed2 // 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: 0x240e672086c6a594, + 0: 0x946ff61139e1f784, } 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 = 0xed6b4d9634dfbb96 + const modified uint64 = 0x4af298e82cc36153 // 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: 0x8146b900161c34e3, + 0: 0x49f8cef460cf416e, } for version, initial := range testcases { diff --git a/internal/app/app.go b/internal/app/app.go index 9a3f17cc2e..6a619d6a87 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -204,7 +204,6 @@ func App( donor.Register( rootMux, logger, - tmpls, donorTmpls, sessionStore, donorStore, diff --git a/internal/page/attorney/your_preferred_language.go b/internal/page/attorney/your_preferred_language.go index e976778dcc..c699df6f8f 100644 --- a/internal/page/attorney/your_preferred_language.go +++ b/internal/page/attorney/your_preferred_language.go @@ -39,7 +39,7 @@ func YourPreferredLanguage(tmpl template.Template, attorneyStore AttorneyStore, } if r.Method == http.MethodPost { - data.Form = form.ReadLanguagePreferenceForm(r, "whichLanguageYoudLikeUsToUseWhenWeContactYou") + data.Form = form.ReadLanguagePreferenceForm(r, "whichLanguageYouWouldLikeUsToUseWhenWeContactYou") data.Errors = data.Form.Validate() if data.Errors.None() { diff --git a/internal/page/attorney/your_preferred_language_test.go b/internal/page/attorney/your_preferred_language_test.go index a5ae187785..ae82fe24f0 100644 --- a/internal/page/attorney/your_preferred_language_test.go +++ b/internal/page/attorney/your_preferred_language_test.go @@ -162,11 +162,11 @@ func TestPostYourPreferredLanguageWhenInvalidData(t *testing.T) { App: testAppData, Form: &form.LanguagePreferenceForm{ Error: errors.New("invalid Lang 'not-a-lang'"), - ErrorLabel: "whichLanguageYoudLikeUsToUseWhenWeContactYou", + ErrorLabel: "whichLanguageYouWouldLikeUsToUseWhenWeContactYou", }, Options: localize.LangValues, FieldName: form.FieldNames.LanguagePreference, - Errors: validation.With(form.FieldNames.LanguagePreference, validation.SelectError{Label: "whichLanguageYoudLikeUsToUseWhenWeContactYou"}), + Errors: validation.With(form.FieldNames.LanguagePreference, validation.SelectError{Label: "whichLanguageYouWouldLikeUsToUseWhenWeContactYou"}), Lpa: &lpastore.Lpa{}, }). Return(nil) diff --git a/internal/page/certificateprovider/your_preferred_language.go b/internal/page/certificateprovider/your_preferred_language.go index a603c8aca4..a752391ab3 100644 --- a/internal/page/certificateprovider/your_preferred_language.go +++ b/internal/page/certificateprovider/your_preferred_language.go @@ -43,7 +43,7 @@ func YourPreferredLanguage(tmpl template.Template, certificateProviderStore Cert } if r.Method == http.MethodPost { - data.Form = form.ReadLanguagePreferenceForm(r, "whichLanguageYoudLikeUsToUseWhenWeContactYou") + data.Form = form.ReadLanguagePreferenceForm(r, "whichLanguageYouWouldLikeUsToUseWhenWeContactYou") data.Errors = data.Form.Validate() if data.Errors.None() { diff --git a/internal/page/certificateprovider/your_preferred_language_test.go b/internal/page/certificateprovider/your_preferred_language_test.go index 7be12704e4..4a0e5acdd8 100644 --- a/internal/page/certificateprovider/your_preferred_language_test.go +++ b/internal/page/certificateprovider/your_preferred_language_test.go @@ -205,11 +205,11 @@ func TestPostYourPreferredLanguageWhenInvalidData(t *testing.T) { App: testAppData, Form: &form.LanguagePreferenceForm{ Error: errors.New("invalid Lang 'not-a-lang'"), - ErrorLabel: "whichLanguageYoudLikeUsToUseWhenWeContactYou", + ErrorLabel: "whichLanguageYouWouldLikeUsToUseWhenWeContactYou", }, Options: localize.LangValues, FieldName: form.FieldNames.LanguagePreference, - Errors: validation.With(form.FieldNames.LanguagePreference, validation.SelectError{Label: "whichLanguageYoudLikeUsToUseWhenWeContactYou"}), + Errors: validation.With(form.FieldNames.LanguagePreference, validation.SelectError{Label: "whichLanguageYouWouldLikeUsToUseWhenWeContactYou"}), Lpa: &lpastore.Lpa{}, }). Return(nil) diff --git a/internal/page/donor/register.go b/internal/page/donor/register.go index 08bbd61a5a..264491062d 100644 --- a/internal/page/donor/register.go +++ b/internal/page/donor/register.go @@ -154,7 +154,7 @@ type ProgressTracker interface { func Register( rootMux *http.ServeMux, logger Logger, - commonTmpls, tmpls template.Templates, + tmpls template.Templates, sessionStore SessionStore, donorStore DonorStore, oneLoginClient OneLoginClient, @@ -206,7 +206,7 @@ func Register( handleWithDonor(page.Paths.WeHaveUpdatedYourDetails, page.None, Guidance(tmpls.Get("we_have_updated_your_details.gohtml"))) handleWithDonor(page.Paths.YourPreferredLanguage, page.None, - YourPreferredLanguage(commonTmpls.Get("your_preferred_language.gohtml"), donorStore)) + YourPreferredLanguage(tmpls.Get("your_preferred_language.gohtml"), donorStore)) handleWithDonor(page.Paths.LpaType, page.None, LpaType(tmpls.Get("lpa_type.gohtml"), donorStore, eventClient)) handleWithDonor(page.Paths.CheckYouCanSign, page.None, @@ -384,6 +384,8 @@ func Register( handleWithDonor(page.Paths.ReadYourLpa, page.None, Guidance(tmpls.Get("read_your_lpa.gohtml"))) + handleWithDonor(page.Paths.YourLpaLanguage, page.CanGoBack, + YourLpaLanguage(tmpls.Get("your_lpa_language.gohtml"), donorStore)) handleWithDonor(page.Paths.LpaYourLegalRightsAndResponsibilities, page.CanGoBack, Guidance(tmpls.Get("your_legal_rights_and_responsibilities.gohtml"))) handleWithDonor(page.Paths.SignYourLpa, page.CanGoBack, diff --git a/internal/page/donor/register_test.go b/internal/page/donor/register_test.go index 5c33ab09c4..eeecc08407 100644 --- a/internal/page/donor/register_test.go +++ b/internal/page/donor/register_test.go @@ -22,7 +22,7 @@ import ( func TestRegister(t *testing.T) { mux := http.NewServeMux() - Register(mux, &slog.Logger{}, template.Templates{}, template.Templates{}, &mockSessionStore{}, &mockDonorStore{}, &onelogin.Client{}, &place.Client{}, "http://example.org", &pay.Client{}, &mockShareCodeSender{}, &mockWitnessCodeSender{}, nil, &mockCertificateProviderStore{}, &mockNotifyClient{}, &mockEvidenceReceivedStore{}, &mockDocumentStore{}, &mockEventClient{}, &mockDashboardStore{}, &mockLpaStoreClient{}, &mockShareCodeStore{}, &mockProgressTracker{}, &lpastore.ResolvingService{}) + Register(mux, &slog.Logger{}, template.Templates{}, &mockSessionStore{}, &mockDonorStore{}, &onelogin.Client{}, &place.Client{}, "http://example.org", &pay.Client{}, &mockShareCodeSender{}, &mockWitnessCodeSender{}, nil, &mockCertificateProviderStore{}, &mockNotifyClient{}, &mockEvidenceReceivedStore{}, &mockDocumentStore{}, &mockEventClient{}, &mockDashboardStore{}, &mockLpaStoreClient{}, &mockShareCodeStore{}, &mockProgressTracker{}, &lpastore.ResolvingService{}) assert.Implements(t, (*http.Handler)(nil), mux) } diff --git a/internal/page/donor/your_lpa_language.go b/internal/page/donor/your_lpa_language.go new file mode 100644 index 0000000000..d22d7dd61d --- /dev/null +++ b/internal/page/donor/your_lpa_language.go @@ -0,0 +1,55 @@ +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/localize" + "github.com/ministryofjustice/opg-modernising-lpa/internal/page" + "github.com/ministryofjustice/opg-modernising-lpa/internal/validation" +) + +type yourLpaLanguageData struct { + App page.AppData + Errors validation.List + Form *form.YesNoForm + SelectedLanguage localize.Lang + UnselectedLanguage localize.Lang +} + +func YourLpaLanguage(tmpl template.Template, donorStore DonorStore) Handler { + return func(appData page.AppData, w http.ResponseWriter, r *http.Request, donor *actor.DonorProvidedDetails) error { + data := &yourLpaLanguageData{ + App: appData, + Form: form.NewYesNoForm(form.YesNoUnknown), + SelectedLanguage: donor.Donor.LpaLanguagePreference, + } + + if data.SelectedLanguage.IsEn() { + data.UnselectedLanguage = localize.Cy + } else { + data.UnselectedLanguage = localize.En + } + + if r.Method == http.MethodPost { + f := form.ReadYesNoForm(r, "whatYouWouldLikeToDo") + data.Errors = f.Validate() + + if data.Errors.None() { + if f.YesNo.IsNo() { + donor.Donor.LpaLanguagePreference = data.UnselectedLanguage + + if err := donorStore.Put(r.Context(), donor); err != nil { + return err + } + } + + return page.Paths.LpaYourLegalRightsAndResponsibilities.Redirect(w, r, appData, donor) + } + } + + return tmpl(w, data) + } +} diff --git a/internal/page/donor/your_lpa_language_test.go b/internal/page/donor/your_lpa_language_test.go new file mode 100644 index 0000000000..4344613d24 --- /dev/null +++ b/internal/page/donor/your_lpa_language_test.go @@ -0,0 +1,164 @@ +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/localize" + "github.com/ministryofjustice/opg-modernising-lpa/internal/page" + "github.com/ministryofjustice/opg-modernising-lpa/internal/validation" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestGetYourLpaLanguage(t *testing.T) { + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodGet, "/", nil) + + template := newMockTemplate(t) + template.EXPECT(). + Execute(w, &yourLpaLanguageData{ + App: testAppData, + Form: form.NewYesNoForm(form.YesNoUnknown), + SelectedLanguage: localize.En, + UnselectedLanguage: localize.Cy, + }). + Return(nil) + + err := YourLpaLanguage(template.Execute, nil)(testAppData, w, r, &actor.DonorProvidedDetails{ + Donor: actor.Donor{LpaLanguagePreference: localize.En}, + }) + resp := w.Result() + + assert.Nil(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) +} + +func TestGetYourLpaLanguageWhenTemplateErrors(t *testing.T) { + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodGet, "/", nil) + + template := newMockTemplate(t) + template.EXPECT(). + Execute(w, mock.Anything). + Return(expectedError) + + err := YourLpaLanguage(template.Execute, nil)(testAppData, w, r, &actor.DonorProvidedDetails{}) + resp := w.Result() + + assert.Equal(t, expectedError, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) +} + +func TestPostYourLpaLanguageWhenContinue(t *testing.T) { + for _, lang := range []localize.Lang{localize.En, localize.Cy} { + t.Run(lang.String(), func(t *testing.T) { + f := url.Values{ + form.FieldNames.YesNo: {form.Yes.String()}, + } + + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode())) + r.Header.Add("Content-Type", page.FormUrlEncoded) + + err := YourLpaLanguage(nil, nil)(testAppData, w, r, &actor.DonorProvidedDetails{ + LpaID: "lpa-id", + Donor: actor.Donor{LpaLanguagePreference: lang}, + }) + resp := w.Result() + + assert.Nil(t, err) + assert.Equal(t, http.StatusFound, resp.StatusCode) + assert.Equal(t, page.Paths.LpaYourLegalRightsAndResponsibilities.Format("lpa-id"), resp.Header.Get("Location")) + }) + } +} + +func TestPostYourLpaLanguageWhenSwitch(t *testing.T) { + testCases := map[string]struct { + selectedLanguage localize.Lang + expectedLanguage localize.Lang + }{ + "cy": { + selectedLanguage: localize.En, + expectedLanguage: localize.Cy, + }, + "en": { + selectedLanguage: localize.Cy, + expectedLanguage: localize.En, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + f := url.Values{ + form.FieldNames.YesNo: {form.No.String()}, + } + + w := httptest.NewRecorder() + r, _ := http.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", + Donor: actor.Donor{LpaLanguagePreference: tc.expectedLanguage}, + }). + Return(nil) + + err := YourLpaLanguage(nil, donorStore)(testAppData, w, r, &actor.DonorProvidedDetails{ + LpaID: "lpa-id", + Donor: actor.Donor{LpaLanguagePreference: tc.selectedLanguage}, + }) + resp := w.Result() + + assert.Nil(t, err) + assert.Equal(t, http.StatusFound, resp.StatusCode) + assert.Equal(t, page.Paths.LpaYourLegalRightsAndResponsibilities.Format("lpa-id"), resp.Header.Get("Location")) + }) + } +} + +func TestPostYourLpaLanguageWhenStoreErrors(t *testing.T) { + f := url.Values{ + form.FieldNames.YesNo: {form.No.String()}, + } + + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(f.Encode())) + r.Header.Add("Content-Type", page.FormUrlEncoded) + + donorStore := newMockDonorStore(t) + donorStore.EXPECT(). + Put(r.Context(), mock.Anything). + Return(expectedError) + + err := YourLpaLanguage(nil, donorStore)(testAppData, w, r, &actor.DonorProvidedDetails{}) + + assert.Equal(t, expectedError, err) +} + +func TestPostYourLpaLanguageWhenValidationErrors(t *testing.T) { + w := httptest.NewRecorder() + r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader("")) + r.Header.Add("Content-Type", page.FormUrlEncoded) + + template := newMockTemplate(t) + template.EXPECT(). + Execute(w, mock.MatchedBy(func(data *yourLpaLanguageData) bool { + return assert.Equal(t, validation.With(form.FieldNames.YesNo, validation.SelectError{Label: "whatYouWouldLikeToDo"}), data.Errors) + })). + Return(nil) + + err := YourLpaLanguage(template.Execute, nil)(testAppData, w, r, &actor.DonorProvidedDetails{}) + resp := w.Result() + + assert.Nil(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) +} diff --git a/internal/page/donor/your_preferred_language.go b/internal/page/donor/your_preferred_language.go index 0a76717ac8..b93f37122a 100644 --- a/internal/page/donor/your_preferred_language.go +++ b/internal/page/donor/your_preferred_language.go @@ -12,30 +12,31 @@ import ( ) type yourPreferredLanguageData struct { - App page.AppData - Errors validation.List - Form *form.LanguagePreferenceForm - Options localize.LangOptions - FieldName string + App page.AppData + Errors validation.List + Form *yourPreferredLanguageForm + Options localize.LangOptions } func YourPreferredLanguage(tmpl template.Template, donorStore DonorStore) Handler { return func(appData page.AppData, w http.ResponseWriter, r *http.Request, donor *actor.DonorProvidedDetails) error { data := &yourPreferredLanguageData{ App: appData, - Form: &form.LanguagePreferenceForm{ - Preference: donor.Donor.ContactLanguagePreference, + Form: &yourPreferredLanguageForm{ + Contact: donor.Donor.ContactLanguagePreference, + Lpa: donor.Donor.LpaLanguagePreference, }, - Options: localize.LangValues, - FieldName: form.FieldNames.LanguagePreference, + Options: localize.LangValues, } if r.Method == http.MethodPost { - data.Form = form.ReadLanguagePreferenceForm(r, "whichLanguageYoudLikeUsToUseWhenWeContactYou") + data.Form = readYourPreferredLanguageForm(r) data.Errors = data.Form.Validate() if data.Errors.None() { - donor.Donor.ContactLanguagePreference = data.Form.Preference + donor.Donor.ContactLanguagePreference = data.Form.Contact + donor.Donor.LpaLanguagePreference = data.Form.Lpa + if err := donorStore.Put(r.Context(), donor); err != nil { return err } @@ -47,3 +48,33 @@ func YourPreferredLanguage(tmpl template.Template, donorStore DonorStore) Handle return tmpl(w, data) } } + +type yourPreferredLanguageForm struct { + Contact localize.Lang + ContactError error + Lpa localize.Lang + LpaError error +} + +func readYourPreferredLanguageForm(r *http.Request) *yourPreferredLanguageForm { + contact, contactErr := localize.ParseLang(form.PostFormString(r, "contact-language")) + lpa, lpaErr := localize.ParseLang(form.PostFormString(r, "lpa-language")) + + return &yourPreferredLanguageForm{ + Contact: contact, + ContactError: contactErr, + Lpa: lpa, + LpaError: lpaErr, + } +} + +func (f *yourPreferredLanguageForm) Validate() validation.List { + var errors validation.List + + errors.Error("contact-language", "whichLanguageYouWouldLikeUsToUseWhenWeContactYou", f.ContactError, + validation.Selected()) + errors.Error("lpa-language", "theLanguageInWhichYouWouldLikeYourLpaRegistered", f.LpaError, + validation.Selected()) + + return errors +} diff --git a/internal/page/donor/your_preferred_language_test.go b/internal/page/donor/your_preferred_language_test.go index c2ce465a82..a349be0a90 100644 --- a/internal/page/donor/your_preferred_language_test.go +++ b/internal/page/donor/your_preferred_language_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" - "github.com/ministryofjustice/opg-modernising-lpa/internal/form" "github.com/ministryofjustice/opg-modernising-lpa/internal/localize" "github.com/ministryofjustice/opg-modernising-lpa/internal/page" "github.com/ministryofjustice/opg-modernising-lpa/internal/validation" @@ -25,11 +24,10 @@ func TestGetYourPreferredLanguage(t *testing.T) { template.EXPECT(). Execute(w, &yourPreferredLanguageData{ App: testAppData, - Form: &form.LanguagePreferenceForm{ - Preference: localize.Cy, + Form: &yourPreferredLanguageForm{ + Contact: localize.Cy, }, - Options: localize.LangValues, - FieldName: form.FieldNames.LanguagePreference, + Options: localize.LangValues, }). Return(nil) @@ -63,7 +61,7 @@ func TestPostYourPreferredLanguage(t *testing.T) { for _, lang := range testCases { t.Run(lang.String(), func(t *testing.T) { - formValues := url.Values{form.FieldNames.LanguagePreference: {lang.String()}} + formValues := url.Values{"contact-language": {lang.String()}, "lpa-language": {lang.String()}} w := httptest.NewRecorder() r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(formValues.Encode())) @@ -71,7 +69,10 @@ func TestPostYourPreferredLanguage(t *testing.T) { donorStore := newMockDonorStore(t) donorStore.EXPECT(). - Put(r.Context(), &actor.DonorProvidedDetails{LpaID: "lpa-id", Donor: actor.Donor{ContactLanguagePreference: lang}}). + Put(r.Context(), &actor.DonorProvidedDetails{ + LpaID: "lpa-id", + Donor: actor.Donor{ContactLanguagePreference: lang, LpaLanguagePreference: lang}, + }). Return(nil) err := YourPreferredLanguage(nil, donorStore)(testAppData, w, r, &actor.DonorProvidedDetails{LpaID: "lpa-id"}) @@ -85,8 +86,8 @@ func TestPostYourPreferredLanguage(t *testing.T) { } } -func TestPostYourPreferredLanguageWhenAttorneyStoreError(t *testing.T) { - formValues := url.Values{form.FieldNames.LanguagePreference: {localize.En.String()}} +func TestPostYourPreferredLanguageWhenDonorStoreError(t *testing.T) { + formValues := url.Values{"contact-language": {localize.En.String()}, "lpa-language": {localize.En.String()}} w := httptest.NewRecorder() r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(formValues.Encode())) @@ -106,7 +107,7 @@ func TestPostYourPreferredLanguageWhenAttorneyStoreError(t *testing.T) { } func TestPostYourPreferredLanguageWhenInvalidData(t *testing.T) { - formValues := url.Values{form.FieldNames.LanguagePreference: {"not-a-lang"}} + formValues := url.Values{"contact-language": {"not-a-lang"}, "lpa-language": {localize.En.String()}} w := httptest.NewRecorder() r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(formValues.Encode())) @@ -116,13 +117,12 @@ func TestPostYourPreferredLanguageWhenInvalidData(t *testing.T) { template.EXPECT(). Execute(w, &yourPreferredLanguageData{ App: testAppData, - Form: &form.LanguagePreferenceForm{ - Error: errors.New("invalid Lang 'not-a-lang'"), - ErrorLabel: "whichLanguageYoudLikeUsToUseWhenWeContactYou", + Form: &yourPreferredLanguageForm{ + Lpa: localize.En, + ContactError: errors.New("invalid Lang 'not-a-lang'"), }, - Options: localize.LangValues, - FieldName: form.FieldNames.LanguagePreference, - Errors: validation.With(form.FieldNames.LanguagePreference, validation.SelectError{Label: "whichLanguageYoudLikeUsToUseWhenWeContactYou"}), + Options: localize.LangValues, + Errors: validation.With("contact-language", validation.SelectError{Label: "whichLanguageYouWouldLikeUsToUseWhenWeContactYou"}), }). Return(nil) @@ -133,3 +133,33 @@ func TestPostYourPreferredLanguageWhenInvalidData(t *testing.T) { assert.Nil(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) } + +func TestReadYourPreferredLanguageForm(t *testing.T) { + form := url.Values{"contact-language": {localize.En.String()}, "lpa-language": {localize.Cy.String()}} + r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(form.Encode())) + r.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + assert.Equal(t, &yourPreferredLanguageForm{Contact: localize.En, Lpa: localize.Cy}, readYourPreferredLanguageForm(r)) +} + +func TestLanguagePreferenceFormValidate(t *testing.T) { + testcases := map[string]struct { + form *yourPreferredLanguageForm + errors validation.List + }{ + "valid": { + form: &yourPreferredLanguageForm{}, + }, + "invalid": { + form: &yourPreferredLanguageForm{ContactError: errors.New("err"), LpaError: errors.New("arr")}, + errors: validation.With("contact-language", validation.SelectError{Label: "whichLanguageYouWouldLikeUsToUseWhenWeContactYou"}). + With("lpa-language", validation.SelectError{Label: "theLanguageInWhichYouWouldLikeYourLpaRegistered"}), + }, + } + + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + assert.Equal(t, tc.errors, tc.form.Validate()) + }) + } +} diff --git a/internal/page/fixtures/fixtures.go b/internal/page/fixtures/fixtures.go index 5cfa2f80fb..215dcc2400 100644 --- a/internal/page/fixtures/fixtures.go +++ b/internal/page/fixtures/fixtures.go @@ -136,6 +136,7 @@ func makeDonor(email string) actor.Donor { ThinksCanSign: actor.Yes, CanSign: form.Yes, ContactLanguagePreference: localize.En, + LpaLanguagePreference: localize.En, } } diff --git a/internal/page/paths.go b/internal/page/paths.go index 6f6515601f..fcc772c40c 100644 --- a/internal/page/paths.go +++ b/internal/page/paths.go @@ -425,8 +425,8 @@ type AppPaths struct { WeHaveReceivedVoucherDetails LpaPath WeHaveUpdatedYourDetails LpaPath WhatACertificateProviderDoes LpaPath - WhatHappensNextRegisteringWithCourtOfProtection LpaPath WhatHappensNextPostEvidence LpaPath + WhatHappensNextRegisteringWithCourtOfProtection LpaPath WhatIsVouching LpaPath WhatYouCanDoNow LpaPath WhenCanTheLpaBeUsed LpaPath @@ -445,6 +445,7 @@ type AppPaths struct { YourIndependentWitness LpaPath YourIndependentWitnessAddress LpaPath YourIndependentWitnessMobile LpaPath + YourLpaLanguage LpaPath YourName LpaPath YourPreferredLanguage LpaPath } @@ -628,8 +629,8 @@ var Paths = AppPaths{ WeHaveReceivedVoucherDetails: "/we-have-received-voucher-details", WeHaveUpdatedYourDetails: "/we-have-updated-your-details", WhatACertificateProviderDoes: "/what-a-certificate-provider-does", - WhatHappensNextRegisteringWithCourtOfProtection: "/what-happens-next-registering-with-court-of-protection", WhatHappensNextPostEvidence: "/what-happens-next-post-evidence", + WhatHappensNextRegisteringWithCourtOfProtection: "/what-happens-next-registering-with-court-of-protection", WhatIsVouching: "/what-is-vouching", WhatYouCanDoNow: "/what-you-can-do-now", WhenCanTheLpaBeUsed: "/when-can-the-lpa-be-used", @@ -649,6 +650,7 @@ var Paths = AppPaths{ YourIndependentWitnessAddress: "/your-independent-witness-address", YourIndependentWitnessMobile: "/your-independent-witness-mobile", YourLegalRightsAndResponsibilities: "/your-legal-rights-and-responsibilities", + YourLpaLanguage: "/your-lpa-language", YourName: "/your-name", YourPreferredLanguage: "/your-preferred-language", } diff --git a/lang/cy.json b/lang/cy.json index fee497e6cc..e93a9dbd26 100644 --- a/lang/cy.json +++ b/lang/cy.json @@ -968,7 +968,7 @@ "whichLanguageWouldYouPreferWhenWeContactYou": "Pa iaith fyddai orau gennych i ni ei defnyddio pan fyddwn yn cysylltu â chi?", "en": "Saesneg", "cy": "Cymraeg", - "whichLanguageYoudLikeUsToUseWhenWeContactYou": "Welsh", + "whichLanguageYouWouldLikeUsToUseWhenWeContactYou": "Welsh", "preferredContactLanguage": "Dewis iaith gyswllt", "addAnotherLpaIfInvited:donor": "Welsh", "addAnotherLpaIfInvited:attorney": "Ychwanegwch LPA arall os anfonwyd e-bost atoch yn eich gwahodd i fod yn atwrnai.", @@ -1272,5 +1272,17 @@ "iUnderstandThisWillWithdrawLPAHint": "Welsh", "youCanOnlyContinueIfDetailsMatchWarning": "Welsh", "yesIfWouldLikeToUpdateDetails": "Welsh", - "wouldYouLikeToUpdateDetailsToMatchIdentityDetails": "Welsh" + "wouldYouLikeToUpdateDetailsToMatchIdentityDetails": "Welsh", + "yourRegisteredLpaLanguageContent": "

Welsh

", + "youCanOnlyChangeLanguageWarning": "Welsh", + "youCanReturnToThisPageFromProvideDetails": "Welsh", + "inWhichLanguageWouldYouLikeYourLpaRegistered": "Welsh", + "usingThisService": "Welsh", + "theLanguageInWhichYouWouldLikeYourLpaRegistered": "Welsh", + "yourLpaLanguage": "Welsh", + "yourLpaLanguageContent": "

Welsh {{.Lang}} {{.Lang}}

", + "anyRestrictionsWillNotBeTranslatedWarning": "Welsh {{.Lang}}", + "continueAndRegisterMyLpaIn": "Welsh {{.Lang}}", + "changeToRegisterMyLpaIn": "Welsh {{.Lang}}", + "iUnderstandThatRestrictionsWillNotBeTranslated": "Welsh" } diff --git a/lang/en.json b/lang/en.json index 3445956589..7fe2f50254 100644 --- a/lang/en.json +++ b/lang/en.json @@ -915,7 +915,7 @@ "whichLanguageWouldYouPreferWhenWeContactYou": "Which language would you prefer us to use when we contact you?", "en": "English", "cy": "Welsh", - "whichLanguageYoudLikeUsToUseWhenWeContactYou": "which language you’d like us to use when we contact you", + "whichLanguageYouWouldLikeUsToUseWhenWeContactYou": "which language you would like us to use when we contact you", "preferredContactLanguage": "Preferred contact language", "addAnotherLpaIfInvited:donor": "Add your LPA that you’ve been invited to access by email.", "addAnotherLpaIfInvited:attorney": "Add another LPA if you have been sent an email inviting you to be an attorney.", @@ -1201,5 +1201,17 @@ "iUnderstandThisWillWithdrawLPAHint": "I understand that this will withdraw 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", "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?" + "wouldYouLikeToUpdateDetailsToMatchIdentityDetails": "Would you like to update the details on your LPA to match your confirmed identity details?", + "yourRegisteredLpaLanguageContent": "

Your registered LPA

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.

", + "youCanOnlyChangeLanguageWarning": "You can only change the language of your LPA until you register it. Once you register, it will only be available in the language you selected.", + "youCanReturnToThisPageFromProvideDetails": "You can return to this page from the ‘Provide your details’ section of your task list until you register your LPA.", + "inWhichLanguageWouldYouLikeYourLpaRegistered": "In which language would you like your LPA registered?", + "usingThisService": "Using this service", + "theLanguageInWhichYouWouldLikeYourLpaRegistered": "the language in which you would like your LPA registered", + "yourLpaLanguage": "Your LPA language", + "yourLpaLanguageContent": "

You have selected to register your LPA in {{.Lang}}.

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 {{.Lang}}.

You cannot change this or get a copy in another language once you register.

", + "anyRestrictionsWillNotBeTranslatedWarning": "Any restrictions and conditions you have written will not be translated if you change to register your LPA in {{.Lang}}", + "continueAndRegisterMyLpaIn": "Continue and register my LPA in {{.Lang}}", + "changeToRegisterMyLpaIn": "Change to register my LPA in {{.Lang}}", + "iUnderstandThatRestrictionsWillNotBeTranslated": "I understand that any restrictions and conditions I have written will not be translated." } diff --git a/web/template/donor/read_your_lpa.gohtml b/web/template/donor/read_your_lpa.gohtml index 7aafc39b33..2abcc6a90a 100644 --- a/web/template/donor/read_your_lpa.gohtml +++ b/web/template/donor/read_your_lpa.gohtml @@ -23,7 +23,7 @@ {{ template "people-named-on-lpa" (lpaDecisions .App .Donor (not .Donor.Tasks.ConfirmYourIdentityAndSign.IsCompleted)) }} - {{ template "buttons" (button .App "continue" "link" (global.Paths.LpaYourLegalRightsAndResponsibilities.Format .App.LpaID)) }} + {{ template "buttons" (button .App "continue" "link" (global.Paths.YourLpaLanguage.Format .App.LpaID)) }} {{ end }} diff --git a/web/template/donor/your_lpa_language.gohtml b/web/template/donor/your_lpa_language.gohtml new file mode 100644 index 0000000000..732c146b0c --- /dev/null +++ b/web/template/donor/your_lpa_language.gohtml @@ -0,0 +1,38 @@ +{{ template "page" . }} + +{{ define "pageTitle" }}{{ tr .App "yourLpaLanguage" }}{{ end }} + +{{ define "main" }} + {{ $selected := tr .App .SelectedLanguage.String }} + {{ $unselected := tr .App .UnselectedLanguage.String }} + +
+
+

{{ tr .App "yourLpaLanguage" }}

+ + {{ trFormatHtml .App "yourLpaLanguageContent" "Lang" $selected }} + + {{ template "warning" (content .App (trFormat .App "anyRestrictionsWillNotBeTranslatedWarning" "Lang" $unselected)) }} + +
+
+
+ + {{ tr .App "whatWouldYouLikeToDo" }} + + + {{ template "error-message" (errorMessage . .Form.FieldName) }} + + {{ template "radios" (items . .Form.FieldName .Form.YesNo.String + (item .Form.Options.Yes.String (trFormat .App "continueAndRegisterMyLpaIn" "Lang" $selected)) + (item .Form.Options.No.String (trFormat .App "changeToRegisterMyLpaIn" "Lang" $unselected) "hint" "iUnderstandThatRestrictionsWillNotBeTranslated") + ) }} +
+
+ + {{ template "buttons" (button .App "saveAndContinue") }} + {{ template "csrf-field" . }} +
+
+
+{{ end }} diff --git a/web/template/donor/your_preferred_language.gohtml b/web/template/donor/your_preferred_language.gohtml new file mode 100644 index 0000000000..8020d81589 --- /dev/null +++ b/web/template/donor/your_preferred_language.gohtml @@ -0,0 +1,62 @@ +{{ template "page" . }} + +{{ define "pageTitle" }}{{ tr .App "yourPreferredLanguage" }}{{ end }} + +{{ define "main" }} +
+
+

{{ tr .App "yourPreferredLanguage" }}

+ +
+

{{ tr .App "usingThisService" }}

+ + {{ trHtml .App "yourPreferredLanguageIntroContent" }} + + {{ template "details" (details . "howToChangeLanguageToWelsh" "howToChangeLanguageToWelshContent" false) }} + + {{ trHtml .App "gettingInTouchWithYouContentIsDonor" }} + +
+
+ + {{ tr .App "whichLanguageWouldYouPreferWhenWeContactYou" }} + + + {{ template "error-message" (errorMessage . "contact-language") }} + + {{ template "radios" (items . "contact-language" .Form.Contact.String + (item .Options.En.String .Options.En.String) + (item .Options.Cy.String .Options.Cy.String) + ) }} +
+
+ + {{ trHtml .App "yourRegisteredLpaLanguageContent" }} + + {{ template "warning" (content .App "youCanOnlyChangeLanguageWarning") }} + +
+ {{ tr .App "youCanReturnToThisPageFromProvideDetails" }} +
+ +
+
+ + {{ tr .App "inWhichLanguageWouldYouLikeYourLpaRegistered" }} + + + {{ template "error-message" (errorMessage . "lpa-language") }} + + {{ template "radios" (items . "lpa-language" .Form.Lpa.String + (item .Options.En.String .Options.En.String) + (item .Options.Cy.String .Options.Cy.String) + ) }} +
+
+ + + {{ template "csrf-field" . }} +
+
+
+{{ end }}