Skip to content

Commit

Permalink
Add zip-code-assert
Browse files Browse the repository at this point in the history
  • Loading branch information
Megamind51 committed Jan 17, 2025
1 parent c9b90d9 commit 09286da
Show file tree
Hide file tree
Showing 6 changed files with 1,898 additions and 1,925 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ The following set of extra asserts are provided by this package:
- [UsSubdivision](#ussubdivision)
- [UsZipCode](#uszipcode)
- [Uuid](#uuid)
- [ZipCode](#zipcode) (requires `@uphold/countries` and `@uphold/zip-codes`)

### AbaRoutingNumber
Tests if the value is a valid [ABA Routing Number](http://www.accuity.com/PageFiles/255/ROUTING_NUMBER_POLICY.pdf).
Expand Down Expand Up @@ -259,6 +260,14 @@ Tests if the value is a valid uuid.
#### Arguments
- `version` (optional) - the version to test the uuid for. Supported version are `3`, `4` and `5`. Defaults to test for `all` three if omitted.

### ZipCode
Tests if the value is a valid zip code.
This validator also checks if the country is a valid country from our codebase and validates if this zip code is valid based on country state (only checks countries on [zip-codes repository](https://github.com/uphold/zip-codes)).

#### Arguments
- `country` (required) - the country which zip code is from.
- `state` (optional) - the state which zip code is from.

## Usage
The following is an example for the extra ip assert:

Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@
"validator.js": "^2.0.0"
},
"devDependencies": {
"@uphold/countries": "https://github.com/uphold/countries#v5.1.0",
"@uphold/github-changelog-generator": "^3.4.0",
"@uphold/zip-codes": "https://github.com/uphold/zip-codes#v1.0.0",
"release-it": "^17.0.1",
"abavalidator": "^2.0.1",
"bignumber.js": "^9.0.0",
Expand All @@ -64,6 +66,8 @@
"node": ">=18"
},
"optionalPeerDependencies": {
"@uphold/countries": "https://github.com/uphold/countries#v5.1.0",
"@uphold/zip-codes": "https://github.com/uphold/zip-codes#v1.0.0",
"abavalidator": ">=2 <3",
"bignumber.js": ">=7 <=9.0.0",
"cpf": "^2.0.1",
Expand Down
80 changes: 80 additions & 0 deletions src/asserts/zip-code-assert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
'use strict';

/**
* Module dependencies.
*/

const { Assert, Validator, Violation } = require('validator.js');
const { CaZipCode, UsZipCode } = require('../index.js');
const {
common: { getCountriesAlpha2 }
} = require('@uphold/countries');
const _ = require('lodash');
const zipCodes = require('@uphold/zip-codes');

/**
* Constants.
*/

const countriesAlpha2 = getCountriesAlpha2();

/**
* Instances.
*/

const is = Assert.extend({ CaZipCode, UsZipCode });

/**
* Export `ZipCodeAssert`.
*/

module.exports = function zipCodeAssert(data = {}) {
/**
* Class name.
*/

this.__class__ = 'ZipCode';
this.country = data && data.country;
this.state = data && data.state;

/**
* Validation algorithm.
*/

this.validate = function (value) {
if (value !== null && !_.isString(value)) {
throw new Violation(this, value, { value: 'must_be_null_or_a_string' });
}

if (!_.isString(this.country)) {
throw new Violation(this, value, { country: Validator.errorCode.must_be_a_string });
}

if (!countriesAlpha2.includes(this.country)) {
throw new Violation(this, value, { reason: 'invalid-country' });
}

if (this.country === 'US' && is.usZipCode().check(value) !== true) {
throw new Violation(this, value, { reason: 'invalid-zip-code' });
}

if (this.country === 'CA' && is.caZipCode().check(value) !== true) {
throw new Violation(this, value, { reason: 'invalid-zip-code' });
}

const state = this.state && this.state.split('-')[1];

if (
this.country &&
state &&
_.has(zipCodes, `${this.country}.${state}`) &&
!(zipCodes[this.country][state] || []).find(zip => value.toUpperCase().startsWith(zip.toUpperCase()))
) {
throw new Violation(this, value, { reason: 'invalid-state-zip-code' });
}

return true;
};

return this;
};
4 changes: 3 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const Uri = require('./asserts/uri-assert.js');
const UsSubdivision = require('./asserts/us-subdivision-assert.js');
const UsZipCode = require('./asserts/us-zip-code-assert.js');
const Uuid = require('./asserts/uuid-assert.js');
const ZipCode = require('./asserts/zip-code-assert.js');

/**
* Export asserts.
Expand Down Expand Up @@ -91,5 +92,6 @@ module.exports = {
Uri,
UsSubdivision,
UsZipCode,
Uuid
Uuid,
ZipCode
};
144 changes: 144 additions & 0 deletions test/asserts/zip-code-assert.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
'use strict';

/**
* Module dependencies.
*/

const { Assert: BaseAssert, Validator, Violation } = require('validator.js');
const zipCodeAssert = require('../../src/asserts/zip-code-assert');

/**
* Extend `Assert` with `UuidAssert`.
*/

const Assert = BaseAssert.extend({
ZipCode: zipCodeAssert
});

/**
* Test `ZipCodeAssert`.
*/

describe('ZipCodeAssert', () => {
it('should throw an error if the input value is invalid', () => {
const types = [[], {}, 0];

types.forEach(type => {
try {
Assert.zipCode().validate(type);

fail();
} catch (e) {
expect(e).toBeInstanceOf(Violation);
expect(e.show().assert).toBe('ZipCode');
expect(e.violation.value).toBe('must_be_null_or_a_string');
}
});
});

it('should throw an error if the country is invalid', () => {
try {
Assert.zipCode({ country: null }).validate('12345');

fail();
} catch (e) {
expect(e).toBeInstanceOf(Violation);
expect(e.show().assert).toBe('ZipCode');
expect(e.show().violation.country).toBe(Validator.errorCode.must_be_a_string);
}
});

it('should throw an error if the country is unknown', () => {
try {
Assert.zipCode({ country: 'ZZ' }).validate('12345');

fail();
} catch (e) {
expect(e).toBeInstanceOf(Violation);
expect(e.show().assert).toBe('ZipCode');
expect(e.show().violation.reason).toBe('invalid-country');
}
});

it('should throw an error if the zip code is not in a valid CA zip code format and user is from CA', () => {
try {
Assert.zipCode({ country: 'CA' }).validate('1234');

fail();
} catch (e) {
expect(e).toBeInstanceOf(Violation);
expect(e.show().assert).toBe('ZipCode');
expect(e.show().violation.reason).toBe('invalid-zip-code');
}
});

it('should throw an error if the zip code is not in a valid US zip code format and user if from US', () => {
try {
Assert.zipCode({ country: 'US' }).validate('1234');

fail();
} catch (e) {
expect(e).toBeInstanceOf(Violation);
expect(e.show().assert).toBe('ZipCode');
expect(e.show().violation.reason).toBe('invalid-zip-code');
}
});

it('should throw an error if the zip code is not valid for the given CA state', () => {
try {
Assert.zipCode({ country: 'CA', state: 'CA-AB' }).validate('A1A1A2');

fail();
} catch (e) {
expect(e).toBeInstanceOf(Violation);
expect(e.show().assert).toBe('ZipCode');
expect(e.show().violation.reason).toBe('invalid-state-zip-code');
}
});

it('should throw an error if the zip code is not valid for the given US state', () => {
try {
Assert.zipCode({ country: 'US', state: 'US-CA' }).validate('12345');

fail();
} catch (e) {
expect(e).toBeInstanceOf(Violation);
expect(e.show().assert).toBe('ZipCode');
expect(e.show().violation.reason).toBe('invalid-state-zip-code');
}
});

it('should accept valid CA zip codes', () => {
['A1A1A1', 'C5D-1B3', 'G0A 5B6'].forEach(code => Assert.zipCode({ country: 'CA' }).validate(code));
});

it('should accept valid US zip codes', () => {
['12345', '12345-1234', '12345 1234', '123456789'].forEach(code =>
Assert.zipCode({ country: 'US' }).validate(code)
);
});

it('should accept valid US territory zip codes', () => {
['12345', '12345-1234', '12345 1234', '123456789'].forEach(code =>
Assert.zipCode({ country: 'US', state: 'US-GU' }).validate(code)
);
});

it('should accept valid CA state zip codes', () => {
['t0A-1a1', 'T0b5Z0', 'T0C 9M9'].forEach(code => Assert.zipCode({ country: 'CA', state: 'CA-AB' }).validate(code));
});

it('should accept valid US state zip codes', () => {
['94501', '95988-1234', '95559 1234', '900011234'].forEach(code =>
Assert.zipCode({ country: 'US', state: 'US-CA' }).validate(code)
);
});

it('should accept a valid non-CA/US zip code', () => {
Assert.zipCode({ country: 'PT' }).validate('foobar');
});

it('should accept a valid non-CA/US state zip code', () => {
Assert.zipCode({ country: 'PT', state: 'PT-01' }).validate('foobar');
});
});
Loading

0 comments on commit 09286da

Please sign in to comment.