diff --git a/lerna.json b/lerna.json index 72425889..66d15516 100644 --- a/lerna.json +++ b/lerna.json @@ -7,8 +7,9 @@ "src/interfaces", "src/mixins", "src/modules/*", + "src/services", "src/types", - "src/services" + "src/validators" ], "command": { "publish": { diff --git a/src/validators/index.ts b/src/validators/index.ts new file mode 100644 index 00000000..cdcdae5d --- /dev/null +++ b/src/validators/index.ts @@ -0,0 +1 @@ +export * from './validate-postal-code' diff --git a/src/validators/package.json b/src/validators/package.json new file mode 100644 index 00000000..e982e8c5 --- /dev/null +++ b/src/validators/package.json @@ -0,0 +1,12 @@ +{ + "name": "@bcrs-shared-components/validators", + "version": "0.0.0", + "publishConfig": { + "access": "public" + }, + "dependencies": { + }, + "devDependencies": { + }, + "gitHead": "31c1b1c7fa82101f90ccd7842e436608bdfca752" +} diff --git a/src/validators/validate-postal-code.ts b/src/validators/validate-postal-code.ts new file mode 100644 index 00000000..347a5754 --- /dev/null +++ b/src/validators/validate-postal-code.ts @@ -0,0 +1,14 @@ +/** + * Canadian postal code regex (eg, accepts A1A 1A1 or A1A1A1). + * Ref: https://en.wikipedia.org/wiki/Postal_codes_in_Canada + */ +const CanadaPostalCodeRegex = /^[ABCEGHJ-NPRSTVXY][0-9][ABCEGHJ-NPRSTV-Z][ ]?[0-9][ABCEGHJ-NPRSTV-Z][0-9]$/i + +/** Custom validator for postal codes. */ +export function validatePostalCode (value: string, parentVm: any): boolean { + // if Canada, validate postal code format + // empty value is considered valid -- "required" validation is handled separately + if (parentVm.addressCountry === 'CA') return !value || CanadaPostalCodeRegex.test(value) + // otherwise, no validation + return true +} diff --git a/tests/unit/validate-postal-code.spec.ts b/tests/unit/validate-postal-code.spec.ts new file mode 100644 index 00000000..4d964186 --- /dev/null +++ b/tests/unit/validate-postal-code.spec.ts @@ -0,0 +1,57 @@ +import Vue from 'vue' +import { shallowMount } from '@vue/test-utils' +import { validatePostalCode } from '@/validators' + +const Dummy = Vue.component('DummyComponent', { template: '
' }) + +describe('Validate Postal Code', () => { + let vm: any + + beforeAll(async () => { + // mount the component and wait for everything to stabilize + // (this can be any component since we are not really using it) + const wrapper = shallowMount(Dummy) + vm = wrapper.vm + await Vue.nextTick() + }) + + it.each([ + '', // empty value is valid + 'A1A 1A1', + 'A1A1A1', + 'a1a 1a1', + 'a1a1a1' + ])('accepts valid postal code "%s" for Canada', (postalCode) => { + expect(validatePostalCode(postalCode, { addressCountry: 'CA' })).toBe(true) + }) + + it.each([ + '123456', + 'A1A-1A1', + 'AA1 1A1', + 'A11 1A1', + 'A1 A1A1', + 'A1A 1AA', + 'A1A 11A', + ' A1A 1A1', + 'A1A 1A1', + 'A1A 1A1 ', + 'D1D 1D1', // D is not allowed in Canadian postal codes + 'W1W 1W1', // W is not allowed in Canadian postal codes + 'Z1Z 1Z1' // Z is not allowed in Canadian postal codes + ])('rejects invalid postal code "%s" for Canada', (postalCode) => { + expect(validatePostalCode(postalCode, { addressCountry: 'CA' })).toBe(false) + }) + + it.each([ + '', + 'invalid', + 'A1A 1A1', + 'A1A-1A1', + 'D1D 1D1', + 'W1W 1W1', + 'Z1Z 1Z1' + ])('accepts any postal code "%s" for non-Canada', (postalCode) => { + expect(validatePostalCode(postalCode, { addressCountry: 'XX' })).toBe(true) + }) +})