From c0cd54bd82e74c8d8acf7f4c38759fb6bc29c1c2 Mon Sep 17 00:00:00 2001 From: Jon Mulhern <81927768+curlyfriesplease@users.noreply.github.com> Date: Wed, 15 Oct 2025 11:26:30 +0100 Subject: [PATCH 1/5] fix: Postcode validation within MarketingPrefs: Replace simple regex with more robust methods from the postcode library --- package.json | 1 + .../MarketingPreferencesDS/_MarketingPrefsConfig.js | 8 +++++++- yarn.lock | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ce9eb7062..d01e6b2dc 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "lazysizes": "^5.3.2", "lodash": "^4.17.11", "moment": "^2.29.4", + "postcode": "^5.1.0", "prop-types": "^15.8.1", "pure-react-carousel": "1.30.1", "react": "^17.0.2", diff --git a/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js b/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js index dc9ec76bd..761c5bd39 100644 --- a/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js +++ b/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js @@ -1,5 +1,6 @@ import * as yup from 'yup'; import { merge } from 'lodash'; +import { fix as fixPostcode, isValid } from 'postcode'; const setInitialValues = overrideValues => { const defaultValues = { @@ -63,6 +64,11 @@ const buildValidationSchema = overrideOptions => { const phoneRegex = /^(((((\+44)|(0044))\s?\d{4}|\(?0\d{4}\)?)\s?\d{3}\s?\d{3})|((((\+44)|(0044))\s?\d{3}|\(?0\d{3}\)?)\s?\d{3}\s?\d{4})|((((\+44)|(0044))\s?\d{2}|\(?0\d{2}\)?)\s?\d{4}\s?\d{4}))(\s?\\#(\d{4}|\d{3}))?$/; + const validatePostcode = (postcode) => { + const fixed = fixPostcode(postcode || ''); + return isValid(fixed) && fixed; + }; + const mpValidationFields = { mp_email: yup.string() .when('mp_permissionEmail', { @@ -108,7 +114,7 @@ const buildValidationSchema = overrideOptions => { mp_postcode: yup.string().when('mp_permissionPost', { is: val => (!(mpValidationOptions.mp_permissionPost.disableOption) && mpValidationOptions.mp_permissionPost[val]), - then: schema => schema.required('Please enter your postcode').matches(/^[a-zA-Z]{1,2}\d[a-zA-Z\d]?\s*\d[a-zA-Z]{2}$/, 'Please enter a valid postcode') + then: schema => schema.required('Please enter your postcode').test('postcode-valid', 'Please enter a valid postcode', validatePostcode) }), mp_country: yup.string().when('mp_permissionPost', { diff --git a/yarn.lock b/yarn.lock index 04251e575..ef711fa08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11095,6 +11095,11 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== +postcode@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/postcode/-/postcode-5.1.0.tgz#ef30a2a4028fd20255fb0aceccf4c70baab8df5b" + integrity sha512-rjwIdlQ8UvdOnUVZRCZQA54PmQJeoMBDQb4RsW5z3MVp/u7Gcet1vsjVy/p0+YX+R7cmgU9DEJf0zSx5mWqxAA== + postcss-attribute-case-insensitive@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880" From 01a623edbb6ebe27d2aa006f319d22c7b6886846 Mon Sep 17 00:00:00 2001 From: Jon Mulhern <81927768+curlyfriesplease@users.noreply.github.com> Date: Wed, 15 Oct 2025 11:45:06 +0100 Subject: [PATCH 2/5] Lint --- .../Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js b/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js index 761c5bd39..0f5b7ad37 100644 --- a/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js +++ b/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js @@ -64,7 +64,7 @@ const buildValidationSchema = overrideOptions => { const phoneRegex = /^(((((\+44)|(0044))\s?\d{4}|\(?0\d{4}\)?)\s?\d{3}\s?\d{3})|((((\+44)|(0044))\s?\d{3}|\(?0\d{3}\)?)\s?\d{3}\s?\d{4})|((((\+44)|(0044))\s?\d{2}|\(?0\d{2}\)?)\s?\d{4}\s?\d{4}))(\s?\\#(\d{4}|\d{3}))?$/; - const validatePostcode = (postcode) => { + const validatePostcode = postcode => { const fixed = fixPostcode(postcode || ''); return isValid(fixed) && fixed; }; From afa9b17c4d527a491ba64cd7e06a04e66f59b8c7 Mon Sep 17 00:00:00 2001 From: Jon Mulhern <81927768+curlyfriesplease@users.noreply.github.com> Date: Wed, 15 Oct 2025 11:45:41 +0100 Subject: [PATCH 3/5] Add postcode validation tests --- .../organisms/marketingPreferences.spec.js | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/playwright/components/organisms/marketingPreferences.spec.js b/playwright/components/organisms/marketingPreferences.spec.js index eaf9eca94..99b4fb29e 100644 --- a/playwright/components/organisms/marketingPreferences.spec.js +++ b/playwright/components/organisms/marketingPreferences.spec.js @@ -142,6 +142,42 @@ test.describe('marketing preferences component', () => { await page.locator('div#marketing-preferences--default input#mp_postcode').fill('E1 8QS'); // clear the email field await expect(page.locator('div#marketing-preferences--default div.field-post > div > label[for="mp_postcode"] > span > span')).toBeHidden(''); + // Test postcode fix functionality - case correction + await page.locator('div#marketing-preferences--default input#mp_postcode').fill(''); // clear the field + await page.locator('div#marketing-preferences--default input#mp_postcode').type('sw1a 2aa'); + await page.locator('div#marketing-preferences--default input#mp_address2').click(); + await expect(page.locator('div#marketing-preferences--default div.field-post > div > label[for="mp_postcode"] > span > span')).toBeHidden(''); + + // Test postcode fix functionality - I to 1 correction + await page.locator('div#marketing-preferences--default input#mp_postcode').fill(''); // clear the field + await page.locator('div#marketing-preferences--default input#mp_postcode').type('SWIA 2AA'); + await page.locator('div#marketing-preferences--default input#mp_address2').click(); + await expect(page.locator('div#marketing-preferences--default div.field-post > div > label[for="mp_postcode"] > span > span')).toBeHidden(''); + + // Test postcode fix functionality - O to 0 correction + await page.locator('div#marketing-preferences--default input#mp_postcode').fill(''); // clear the field + await page.locator('div#marketing-preferences--default input#mp_postcode').type('SW1A OAA'); + await page.locator('div#marketing-preferences--default input#mp_address2').click(); + await expect(page.locator('div#marketing-preferences--default div.field-post > div > label[for="mp_postcode"] > span > span')).toBeHidden(''); + + // Test postcode fix functionality - 1 to I correction + await page.locator('div#marketing-preferences--default input#mp_postcode').fill(''); // clear the field + await page.locator('div#marketing-preferences--default input#mp_postcode').type('SW1A 21A'); + await page.locator('div#marketing-preferences--default input#mp_address2').click(); + await expect(page.locator('div#marketing-preferences--default div.field-post > div > label[for="mp_postcode"] > span > span')).toBeHidden(''); + + // Test postcode fix functionality - 0 to O correction + await page.locator('div#marketing-preferences--default input#mp_postcode').fill(''); // clear the field + await page.locator('div#marketing-preferences--default input#mp_postcode').type('SW1A 20A'); + await page.locator('div#marketing-preferences--default input#mp_address2').click(); + await expect(page.locator('div#marketing-preferences--default div.field-post > div > label[for="mp_postcode"] > span > span')).toBeHidden(''); + + // Test invalid postcode that cannot be fixed + await page.locator('div#marketing-preferences--default input#mp_postcode').fill(''); // clear the field + await page.locator('div#marketing-preferences--default input#mp_postcode').type('INVALID123'); + await page.locator('div#marketing-preferences--default input#mp_address2').click(); + await expect(page.locator('div#marketing-preferences--default div.field-post > div > label[for="mp_postcode"] > span > span')).toContainText('Please enter a valid postcode'); + // country field should show error message when value is not entered await page.locator('div#marketing-preferences--default input#mp_country').type('United Kingdom'); await page.locator('div#marketing-preferences--default input#mp_country').fill(''); // clear the email field From 347531237eab58505c81ba468001221a03c251b4 Mon Sep 17 00:00:00 2001 From: Jon Mulhern <81927768+curlyfriesplease@users.noreply.github.com> Date: Wed, 15 Oct 2025 15:42:03 +0100 Subject: [PATCH 4/5] refactor: Enhance postcode validation by transforming input before validation --- .../_MarketingPrefsConfig.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js b/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js index 0f5b7ad37..fb6e500c9 100644 --- a/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js +++ b/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js @@ -64,9 +64,15 @@ const buildValidationSchema = overrideOptions => { const phoneRegex = /^(((((\+44)|(0044))\s?\d{4}|\(?0\d{4}\)?)\s?\d{3}\s?\d{3})|((((\+44)|(0044))\s?\d{3}|\(?0\d{3}\)?)\s?\d{3}\s?\d{4})|((((\+44)|(0044))\s?\d{2}|\(?0\d{2}\)?)\s?\d{4}\s?\d{4}))(\s?\\#(\d{4}|\d{3}))?$/; - const validatePostcode = postcode => { - const fixed = fixPostcode(postcode || ''); - return isValid(fixed) && fixed; + const transformPostcode = (postcode) => { + if (typeof postcode === 'string') { + return fixPostcode(postcode); + } + return postcode; + }; + + const validatePostcode = (postcode) => { + return isValid(postcode); }; const mpValidationFields = { @@ -114,7 +120,7 @@ const buildValidationSchema = overrideOptions => { mp_postcode: yup.string().when('mp_permissionPost', { is: val => (!(mpValidationOptions.mp_permissionPost.disableOption) && mpValidationOptions.mp_permissionPost[val]), - then: schema => schema.required('Please enter your postcode').test('postcode-valid', 'Please enter a valid postcode', validatePostcode) + then: schema => schema.required('Please enter your postcode').transform(transformPostcode).test('postcode-valid', 'Please enter a valid postcode', validatePostcode) }), mp_country: yup.string().when('mp_permissionPost', { From 6351e551c9bdf90f6474501684272642ae74fca3 Mon Sep 17 00:00:00 2001 From: Jon Mulhern <81927768+curlyfriesplease@users.noreply.github.com> Date: Wed, 15 Oct 2025 15:53:39 +0100 Subject: [PATCH 5/5] Lint --- .../MarketingPreferencesDS/_MarketingPrefsConfig.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js b/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js index fb6e500c9..a766a88f1 100644 --- a/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js +++ b/src/components/Organisms/MarketingPreferencesDS/_MarketingPrefsConfig.js @@ -64,16 +64,14 @@ const buildValidationSchema = overrideOptions => { const phoneRegex = /^(((((\+44)|(0044))\s?\d{4}|\(?0\d{4}\)?)\s?\d{3}\s?\d{3})|((((\+44)|(0044))\s?\d{3}|\(?0\d{3}\)?)\s?\d{3}\s?\d{4})|((((\+44)|(0044))\s?\d{2}|\(?0\d{2}\)?)\s?\d{4}\s?\d{4}))(\s?\\#(\d{4}|\d{3}))?$/; - const transformPostcode = (postcode) => { + const transformPostcode = postcode => { if (typeof postcode === 'string') { return fixPostcode(postcode); } return postcode; }; - const validatePostcode = (postcode) => { - return isValid(postcode); - }; + const validatePostcode = postcode => isValid(postcode); const mpValidationFields = { mp_email: yup.string()