card-validator 10.0.2 → 10.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,47 @@
1
+ name: Bug Report
2
+ description: File a bug report.
3
+ title: "[Bug]: "
4
+ labels: ["bug", "triage"]
5
+ projects: []
6
+ body:
7
+ - type: markdown
8
+ attributes:
9
+ value: |
10
+ Thanks for taking the time to fill out this bug report!
11
+ - type: textarea
12
+ id: what-happened
13
+ attributes:
14
+ label: What happened?
15
+ description: Detail the bad behavior and the steps required to duplicate it
16
+ placeholder: Tell us what you see!
17
+ value: "A bug happened!"
18
+ validations:
19
+ required: true
20
+ - type: textarea
21
+ id: expected-behavior
22
+ attributes:
23
+ label: What did you expect to happen?
24
+ description: What did you expect to happen when performing the action?
25
+ placeholder: Expected behavior
26
+ value: "Expected behavior"
27
+ validations:
28
+ required: true
29
+ - type: input
30
+ id: version
31
+ attributes:
32
+ label: Version
33
+ description: What version of this library are you running?
34
+ placeholder: "v0.0.0"
35
+ value:
36
+ validations:
37
+ required: true
38
+ - type: textarea
39
+ id: browsers
40
+ attributes:
41
+ label: What browsers are you seeing the problem on?
42
+ description: |
43
+ List browsers where you see the problem reported. For example:
44
+ * Mac OS 15.3.5, Chrome 130.0.1234.987
45
+ * Android 14, Firefox 133.0
46
+ * iPadOS 17.1, Safari
47
+ placeholder: "For each browser specify 1) operating system and version 2) browser name and version"
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Contact Developer Support
4
+ url: https://developer.paypal.com/braintree/help
5
+ about: If you need help troubleshooting your integration, reach out to Braintree Support. Only open a GitHub issue if you've found an issue with our SDK.
@@ -0,0 +1,30 @@
1
+ name: Feature Request
2
+ description: Request a new feature.
3
+ title: "[FR]: "
4
+ labels: ["feature", "request", "enhancement", "triage"]
5
+ projects: []
6
+ body:
7
+ - type: markdown
8
+ attributes:
9
+ value: |
10
+ Thanks for taking the time to fill out this feature request!
11
+ - type: textarea
12
+ id: feat-summary
13
+ attributes:
14
+ label: Feature Summary
15
+ description: In a few sentences or less, provide a concise description of the feature being requested.
16
+ placeholder: "New feature that does ___; it can be used when ..."
17
+ validations:
18
+ required: true
19
+ - type: textarea
20
+ id: detailed-description
21
+ attributes:
22
+ label: Detailed description of new feature.
23
+ description: |
24
+ Please provide a detailed description of proposed feature.
25
+ - What problem would this new feature solve?
26
+ - How will this change affect users?
27
+ - Is it a modification to existing functionality? Are you requesting a new/modified function input or output? Please provide an example of what the change might look like?
28
+ placeholder: "New feature details."
29
+ validations:
30
+ required: true
@@ -0,0 +1,24 @@
1
+ name: Security
2
+
3
+ permissions:
4
+ contents: write # Needed by both CodeQL and dependency review
5
+ pull-requests: write # Needed by dependency review
6
+ statuses: write # Needed by dependency review (to post checks)
7
+ security-events: write # Needed by CodeQL to upload SARIF
8
+ packages: read # Needed by CodeQL for private/internal packs
9
+ actions: read # Needed by CodeQL to access internal actions
10
+
11
+ on:
12
+ pull_request:
13
+ branches: [main]
14
+ push:
15
+ branches: [main]
16
+ workflow_dispatch:
17
+
18
+ jobs:
19
+ codeql-javascript:
20
+ uses: braintree/security-workflows/.github/workflows/codeql.yml@main
21
+ with:
22
+ language: javascript-typescript
23
+ dependency-review:
24
+ uses: braintree/security-workflows/.github/workflows/dependency-review.yml@main
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.cardNumber = void 0;
3
+ exports.cardNumber = cardNumber;
4
4
  var luhn10 = require("./luhn-10");
5
5
  var getCardTypes = require("credit-card-type");
6
6
  function verification(card, isPotentiallyValid, isValid) {
@@ -51,4 +51,3 @@ function cardNumber(value, options) {
51
51
  }
52
52
  return verification(cardType, testCardValue.length < maxLength, false);
53
53
  }
54
- exports.cardNumber = cardNumber;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.cardholderName = void 0;
3
+ exports.cardholderName = cardholderName;
4
4
  var CARD_NUMBER_REGEX = /^[\d\s-]*$/;
5
5
  var MAX_LENGTH = 255;
6
6
  function verification(isValid, isPotentiallyValid) {
@@ -21,4 +21,3 @@ function cardholderName(value) {
21
21
  }
22
22
  return verification(true, true);
23
23
  }
24
- exports.cardholderName = cardholderName;
package/dist/cvv.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.cvv = void 0;
3
+ exports.cvv = cvv;
4
4
  var DEFAULT_LENGTH = 3;
5
5
  function includes(array, thing) {
6
6
  for (var i = 0; i < array.length; i++) {
@@ -41,4 +41,3 @@ function cvv(value, maxLength) {
41
41
  }
42
42
  return verification(true, true);
43
43
  }
44
- exports.cvv = cvv;
@@ -11,7 +11,7 @@ var __assign = (this && this.__assign) || function () {
11
11
  return __assign.apply(this, arguments);
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.expirationDate = void 0;
14
+ exports.expirationDate = expirationDate;
15
15
  var parse_date_1 = require("./lib/parse-date");
16
16
  var expiration_month_1 = require("./expiration-month");
17
17
  var expiration_year_1 = require("./expiration-year");
@@ -55,4 +55,3 @@ function expirationDate(value, maxElapsedYear) {
55
55
  }
56
56
  return verification(false, false, null, null);
57
57
  }
58
- exports.expirationDate = expirationDate;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.expirationMonth = void 0;
3
+ exports.expirationMonth = expirationMonth;
4
4
  function verification(isValid, isPotentiallyValid, isValidForThisYear) {
5
5
  return {
6
6
  isValid: isValid,
@@ -26,4 +26,3 @@ function expirationMonth(value) {
26
26
  var result = month > 0 && month < 13;
27
27
  return verification(result, result, result && month >= currentMonth);
28
28
  }
29
- exports.expirationMonth = expirationMonth;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.expirationYear = void 0;
3
+ exports.expirationYear = expirationYear;
4
4
  var DEFAULT_VALID_NUMBER_OF_YEARS_IN_THE_FUTURE = 19;
5
5
  function verification(isValid, isPotentiallyValid, isCurrentYear) {
6
6
  return {
@@ -55,4 +55,3 @@ function expirationYear(value, maxElapsedYear) {
55
55
  }
56
56
  return verification(valid, valid, isCurrentYear);
57
57
  }
58
- exports.expirationYear = expirationYear;
package/dist/index.js CHANGED
@@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
25
35
  var creditCardType = __importStar(require("credit-card-type"));
26
36
  var cardholder_name_1 = require("./cardholder-name");
27
37
  var card_number_1 = require("./card-number");
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseDate = void 0;
3
+ exports.parseDate = parseDate;
4
4
  var expiration_year_1 = require("../expiration-year");
5
5
  var is_array_1 = require("./is-array");
6
6
  function getNumberOfMonthDigitsInDateString(dateString) {
@@ -97,4 +97,3 @@ function parseDate(datestring) {
97
97
  year: datestring.substr(month.length),
98
98
  };
99
99
  }
100
- exports.parseDate = parseDate;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.postalCode = void 0;
4
- var DEFAULT_MIN_POSTAL_CODE_LENGTH = 3;
3
+ exports.postalCode = postalCode;
4
+ var DEFAULT_MIN_POSTAL_CODE_LENGTH = 2;
5
5
  var ALPHANUM = new RegExp(/^[a-z0-9]+$/i);
6
6
  function verification(isValid, isPotentiallyValid) {
7
7
  return { isValid: isValid, isPotentiallyValid: isPotentiallyValid };
@@ -20,4 +20,3 @@ function postalCode(value, options) {
20
20
  }
21
21
  return verification(true, true);
22
22
  }
23
- exports.postalCode = postalCode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "card-validator",
3
- "version": "10.0.2",
3
+ "version": "10.0.4",
4
4
  "description": "A library for validating credit card fields",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -20,20 +20,20 @@
20
20
  "author": "Braintree <code@getbraintree.com> (https://www.braintreepayments.com/)",
21
21
  "license": "MIT",
22
22
  "devDependencies": {
23
- "@types/jest": "^29.5.3",
24
- "@types/node": "^20.5.2",
23
+ "@types/jest": "^30.0.0",
24
+ "@types/node": "^25.0.3",
25
25
  "@typescript-eslint/eslint-plugin": "^5.54.1",
26
- "eslint": "^8.47.0",
26
+ "eslint": "^8.57.1",
27
27
  "eslint-config-braintree": "^6.0.0-typescript-prep-rc.2",
28
28
  "eslint-plugin-prettier": "^5.0.0",
29
- "jest": "^29.6.3",
30
- "jest-environment-jsdom": "^29.6.3",
31
- "prettier": "^3.0.2",
32
- "ts-jest": "^29.1.1",
33
- "typescript": "^5.1.6"
29
+ "jest": "^30.2.0",
30
+ "jest-environment-jsdom": "^30.2.0",
31
+ "prettier": "^3.7.4",
32
+ "ts-jest": "^29.4.6",
33
+ "typescript": "^5.9.3"
34
34
  },
35
35
  "dependencies": {
36
- "credit-card-type": "^10.0.2"
36
+ "credit-card-type": "^10.1.0"
37
37
  },
38
38
  "jest": {
39
39
  "testEnvironment": "jsdom",
@@ -1,252 +1,163 @@
1
1
  import { expirationYear, ExpirationYearVerification } from "../expiration-year";
2
2
 
3
- const currentYear = new Date().getFullYear();
3
+ const INVALID: ExpirationYearVerification = {
4
+ isValid: false,
5
+ isPotentiallyValid: false,
6
+ isCurrentYear: false,
7
+ };
4
8
 
5
- function yearsFromNow(fromNow: number, digits?: number): string {
6
- let result = String(currentYear + fromNow);
9
+ const POTENTIALLY_VALID: ExpirationYearVerification = {
10
+ isValid: false,
11
+ isPotentiallyValid: true,
12
+ isCurrentYear: false,
13
+ };
7
14
 
8
- if (digits === 2) {
9
- result = result.substr(2, 2);
10
- }
15
+ const VALID: ExpirationYearVerification = {
16
+ isValid: true,
17
+ isPotentiallyValid: true,
18
+ isCurrentYear: false,
19
+ };
11
20
 
12
- return result;
13
- }
21
+ const CURRENT_YEAR: ExpirationYearVerification = {
22
+ isValid: true,
23
+ isPotentiallyValid: true,
24
+ isCurrentYear: true,
25
+ };
14
26
 
15
27
  describe("expirationYear", () => {
16
- const FALSE_VALIDATION = {
17
- isValid: false,
18
- isPotentiallyValid: false,
19
- isCurrentYear: false,
20
- };
21
-
22
- describe.each([
23
- [
24
- "returns false if not a string",
25
- [
26
- [[], FALSE_VALIDATION],
27
- [{}, FALSE_VALIDATION],
28
- [null, FALSE_VALIDATION],
29
- [undefined, FALSE_VALIDATION], // eslint-disable-line no-undefined
30
- [Infinity, FALSE_VALIDATION],
31
- [0 / 0, FALSE_VALIDATION],
32
- [0, FALSE_VALIDATION],
33
- [1, FALSE_VALIDATION],
34
- [2, FALSE_VALIDATION],
35
- [12, FALSE_VALIDATION],
36
- [-1, FALSE_VALIDATION],
37
- [-12, FALSE_VALIDATION],
38
- ],
39
- ],
40
-
41
- [
42
- "returns false for malformed strings",
43
- [
44
- ["foo", FALSE_VALIDATION],
45
- ["1.2", FALSE_VALIDATION],
46
- ["1/20", FALSE_VALIDATION],
47
- ["1 2", FALSE_VALIDATION],
48
- ["1 ", FALSE_VALIDATION],
49
- [" 1", FALSE_VALIDATION],
50
- ["20015", FALSE_VALIDATION],
51
- ],
52
- ],
53
-
54
- [
55
- "returns the appropriate values for incomplete strings",
56
- [
57
- [
58
- "",
59
- { isValid: false, isPotentiallyValid: true, isCurrentYear: false },
60
- ],
61
- [
62
- "2",
63
- { isValid: false, isPotentiallyValid: true, isCurrentYear: false },
64
- ],
65
- [
66
- "9",
67
- { isValid: false, isPotentiallyValid: true, isCurrentYear: false },
68
- ],
69
- [
70
- "200",
71
- { isValid: false, isPotentiallyValid: true, isCurrentYear: false },
72
- ],
73
- [
74
- "123",
75
- { isValid: false, isPotentiallyValid: false, isCurrentYear: false },
76
- ],
77
- [
78
- "20",
79
- { isValid: false, isPotentiallyValid: true, isCurrentYear: false },
80
- ],
81
- ],
82
- ],
83
-
84
- [
85
- "accepts four-digit years",
86
- [
87
- [
88
- yearsFromNow(0),
89
- { isValid: true, isPotentiallyValid: true, isCurrentYear: true },
90
- ],
91
- [
92
- yearsFromNow(-5),
93
- { isValid: false, isPotentiallyValid: false, isCurrentYear: false },
94
- ],
95
- [
96
- yearsFromNow(5),
97
- { isValid: true, isPotentiallyValid: true, isCurrentYear: false },
98
- ],
99
- [
100
- yearsFromNow(10),
101
- { isValid: true, isPotentiallyValid: true, isCurrentYear: false },
102
- ],
103
- [
104
- yearsFromNow(11),
105
- { isValid: true, isPotentiallyValid: true, isCurrentYear: false },
106
- ],
107
- [
108
- yearsFromNow(12),
109
- { isValid: true, isPotentiallyValid: true, isCurrentYear: false },
110
- ],
111
- [
112
- yearsFromNow(19),
113
- { isValid: true, isPotentiallyValid: true, isCurrentYear: false },
114
- ],
115
- [
116
- yearsFromNow(20),
117
- { isValid: false, isPotentiallyValid: false, isCurrentYear: false },
118
- ],
119
- [
120
- yearsFromNow(25),
121
- { isValid: false, isPotentiallyValid: false, isCurrentYear: false },
122
- ],
123
- [
124
- yearsFromNow(33),
125
- { isValid: false, isPotentiallyValid: false, isCurrentYear: false },
126
- ],
127
- ],
128
- ],
129
-
130
- [
131
- "accepts two-digit years",
132
- [
133
- [
134
- yearsFromNow(0, 2),
135
- { isValid: true, isPotentiallyValid: true, isCurrentYear: true },
136
- ],
137
- [
138
- yearsFromNow(-5, 2),
139
- { isValid: false, isPotentiallyValid: false, isCurrentYear: false },
140
- ],
141
- [
142
- yearsFromNow(5, 2),
143
- { isValid: true, isPotentiallyValid: true, isCurrentYear: false },
144
- ],
145
- [
146
- yearsFromNow(10, 2),
147
- { isValid: true, isPotentiallyValid: true, isCurrentYear: false },
148
- ],
149
- [
150
- yearsFromNow(11, 2),
151
- { isValid: true, isPotentiallyValid: true, isCurrentYear: false },
152
- ],
153
- [
154
- yearsFromNow(12, 2),
155
- { isValid: true, isPotentiallyValid: true, isCurrentYear: false },
156
- ],
157
- [
158
- yearsFromNow(19, 2),
159
- { isValid: true, isPotentiallyValid: true, isCurrentYear: false },
160
- ],
161
- [
162
- yearsFromNow(20, 2),
163
- { isValid: false, isPotentiallyValid: false, isCurrentYear: false },
164
- ],
165
- [
166
- yearsFromNow(25, 2),
167
- { isValid: false, isPotentiallyValid: false, isCurrentYear: false },
168
- ],
169
- [
170
- yearsFromNow(33, 2),
171
- { isValid: false, isPotentiallyValid: false, isCurrentYear: false },
172
- ],
173
- ],
174
- ],
175
-
176
- /*
177
- * This doesn't take 20xx -> 21xx into account, but probably YAGNI
178
- * (with apologies to whoever is possibly looking at this legacy
179
- * code long after we're dead
180
- * */
181
- [
182
- "accepts three-digit years",
183
- [
184
- [
185
- yearsFromNow(-3).slice(0, 3),
186
- { isValid: false, isPotentiallyValid: true, isCurrentYear: false },
187
- ],
188
- [
189
- yearsFromNow(-1).slice(0, 3),
190
- { isValid: false, isPotentiallyValid: true, isCurrentYear: false },
191
- ],
192
- [
193
- yearsFromNow(0).slice(0, 3),
194
- { isValid: false, isPotentiallyValid: true, isCurrentYear: false },
195
- ],
196
- [
197
- yearsFromNow(1).slice(0, 3),
198
- { isValid: false, isPotentiallyValid: true, isCurrentYear: false },
199
- ],
200
- [
201
- yearsFromNow(5).slice(0, 3),
202
- { isValid: false, isPotentiallyValid: true, isCurrentYear: false },
203
- ],
204
- [
205
- yearsFromNow(11).slice(0, 3),
206
- { isValid: false, isPotentiallyValid: true, isCurrentYear: false },
207
- ],
208
- [
209
- yearsFromNow(17).slice(0, 3),
210
- { isValid: false, isPotentiallyValid: true, isCurrentYear: false },
211
- ],
212
- [
213
- yearsFromNow(23).slice(0, 3),
214
- { isValid: false, isPotentiallyValid: true, isCurrentYear: false },
215
- ],
216
- ],
217
- ],
218
- ] as Array<[string, Array<[unknown, ExpirationYearVerification]>]>)(
219
- "%s",
220
- (description, tests) => {
221
- it.each(tests)("parses %s to be %p", (exp, meta) => {
222
- expect(expirationYear(exp)).toEqual(meta);
28
+ // Date picked at random from the past 5 years
29
+ const mockToday = new Date(2021, 5, 9);
30
+ jest.useFakeTimers().setSystemTime(mockToday);
31
+
32
+ describe("given a non-string value", () => {
33
+ test.each([
34
+ [[], INVALID],
35
+ [{}, INVALID],
36
+ [null, INVALID],
37
+ [undefined, INVALID], // eslint-disable-line no-undefined
38
+ [Infinity, INVALID],
39
+ [0 / 0, INVALID],
40
+ [0, INVALID],
41
+ [1, INVALID],
42
+ [2, INVALID],
43
+ [12, INVALID],
44
+ [-1, INVALID],
45
+ [-12, INVALID],
46
+ ])("%p is invalid", (value, output) => {
47
+ expect(expirationYear(value)).toEqual(output);
48
+ });
49
+ });
50
+
51
+ describe("given an empty string", () => {
52
+ it("returns as potentially valid", () => {
53
+ expect(expirationYear("")).toEqual(POTENTIALLY_VALID);
54
+ });
55
+ });
56
+
57
+ describe("given only whitespace", () => {
58
+ it("returns as potentially valid", () => {
59
+ expect(expirationYear(" ")).toEqual(POTENTIALLY_VALID);
60
+ });
61
+ });
62
+
63
+ describe("given a malformed string value", () => {
64
+ test.each([
65
+ ["foo", INVALID],
66
+ ["1.2", INVALID],
67
+ ["1/20", INVALID],
68
+ ["1 2", INVALID],
69
+ ["1 ", INVALID],
70
+ [" 1", INVALID],
71
+ ["20015", INVALID],
72
+ ])("%p is invalid", (value, output) => {
73
+ expect(expirationYear(value)).toEqual(output);
74
+ });
75
+ });
76
+
77
+ describe("given a 1 digit string", () => {
78
+ describe("that's not a number", () => {
79
+ test.each(["a", "#", ";", "\\", "+"])("%p is invalid", (value) => {
80
+ expect(expirationYear(value)).toEqual(INVALID);
223
81
  });
224
- },
225
- );
226
-
227
- it("defaults maxElapsedYear is 19", () => {
228
- expect(expirationYear(yearsFromNow(19))).toEqual({
229
- isValid: true,
230
- isPotentiallyValid: true,
231
- isCurrentYear: false,
232
82
  });
233
- expect(expirationYear(yearsFromNow(20))).toEqual({
234
- isValid: false,
235
- isPotentiallyValid: false,
236
- isCurrentYear: false,
83
+
84
+ describe("that is a number", () => {
85
+ test.each(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"])(
86
+ "%p is potentially valid",
87
+ (value) => {
88
+ expect(expirationYear(value)).toEqual(POTENTIALLY_VALID);
89
+ },
90
+ );
91
+ });
92
+ });
93
+
94
+ describe("given a 2 digit string", () => {
95
+ test.each([
96
+ ["19", INVALID],
97
+ ["20", POTENTIALLY_VALID],
98
+ ["21", CURRENT_YEAR],
99
+ ["22", VALID],
100
+ ["40", VALID],
101
+ ["41", INVALID],
102
+ ])("%p gives expected output", (value, output) => {
103
+ expect(expirationYear(value)).toEqual(output);
104
+ });
105
+ });
106
+
107
+ describe("given a 3 digit string", () => {
108
+ test.each([
109
+ ["000", INVALID],
110
+ ["123", INVALID],
111
+ ["200", POTENTIALLY_VALID],
112
+ ["201", POTENTIALLY_VALID],
113
+ ["202", POTENTIALLY_VALID],
114
+ ["203", POTENTIALLY_VALID],
115
+ ["204", POTENTIALLY_VALID],
116
+ ["205", POTENTIALLY_VALID],
117
+ ["206", POTENTIALLY_VALID],
118
+ ["207", POTENTIALLY_VALID],
119
+ ["208", POTENTIALLY_VALID],
120
+ ["209", POTENTIALLY_VALID],
121
+ ["210", INVALID],
122
+ ["300", INVALID],
123
+ ["999", INVALID],
124
+ ])("%p gives expected output", (value, output) => {
125
+ expect(expirationYear(value)).toEqual(output);
237
126
  });
238
127
  });
239
128
 
240
- it("accepts maxElapsedYear", () => {
241
- expect(expirationYear(yearsFromNow(20), 20)).toEqual({
242
- isValid: true,
243
- isPotentiallyValid: true,
244
- isCurrentYear: false,
129
+ describe("given a 4 digit string", () => {
130
+ test.each([
131
+ ["0000", INVALID],
132
+ ["1234", INVALID],
133
+ ["2020", INVALID],
134
+ ["2021", CURRENT_YEAR],
135
+ ["2022", VALID],
136
+ ["2040", VALID],
137
+ ["2041", INVALID],
138
+ ["3000", INVALID],
139
+ ["9999", INVALID],
140
+ ])("%p gives expected output", (value, output) => {
141
+ expect(expirationYear(value)).toEqual(output);
245
142
  });
246
- expect(expirationYear(yearsFromNow(21), 20)).toEqual({
247
- isValid: false,
248
- isPotentiallyValid: false,
249
- isCurrentYear: false,
143
+ });
144
+
145
+ describe("given a more than 4 digit string", () => {
146
+ test.each(["00000", "12345", "20021", "20202", "20211", "30000", "99999"])(
147
+ "%p is invalid",
148
+ (value) => {
149
+ expect(expirationYear(value)).toEqual(INVALID);
150
+ },
151
+ );
152
+ });
153
+
154
+ describe("given a custom max elapsed year", () => {
155
+ it("uses it correctly", () => {
156
+ expect(expirationYear("2020", 5)).toEqual(INVALID);
157
+ expect(expirationYear("2021", 5)).toEqual(CURRENT_YEAR);
158
+ expect(expirationYear("2022", 5)).toEqual(VALID);
159
+ expect(expirationYear("2026", 5)).toEqual(VALID);
160
+ expect(expirationYear("2027", 5)).toEqual(INVALID);
250
161
  });
251
162
  });
252
163
  });
@@ -32,6 +32,8 @@ describe("postalCode", () => {
32
32
  ["557016", { isValid: true, isPotentiallyValid: true }], // Romania
33
33
  ["110001", { isValid: true, isPotentiallyValid: true }], // India
34
34
  ["SE1 2LN", { isValid: true, isPotentiallyValid: true }], // UK
35
+ ["S9 1DF", { isValid: true, isPotentiallyValid: true }], // UK
36
+ ["AA9A 9AA", { isValid: true, isPotentiallyValid: true }], // UK
35
37
  ["01234567890123456789", { isValid: true, isPotentiallyValid: true }], // some hypothetical country
36
38
  ],
37
39
  ],
@@ -52,11 +54,11 @@ describe("postalCode", () => {
52
54
  ],
53
55
 
54
56
  [
55
- "returns isPotentiallyValid for shorter-than-3 strings",
57
+ "returns isPotentiallyValid for shorter-than-2 strings",
56
58
  [
57
59
  ["", { isValid: false, isPotentiallyValid: true }],
58
60
  ["1", { isValid: false, isPotentiallyValid: true }],
59
- ["12", { isValid: false, isPotentiallyValid: true }],
61
+ ["12", { isValid: true, isPotentiallyValid: true }],
60
62
  ],
61
63
  ],
62
64
  ] as Array<[string, Array<[string, Verification]>]>)(
@@ -79,10 +81,18 @@ describe("postalCode", () => {
79
81
  isPotentiallyValid: true,
80
82
  });
81
83
  expect(postalCode("12")).toEqual({
82
- isValid: false,
84
+ isValid: true,
83
85
  isPotentiallyValid: true,
84
86
  });
85
87
  expect(postalCode("12", {})).toEqual({
88
+ isValid: true,
89
+ isPotentiallyValid: true,
90
+ });
91
+ expect(postalCode("1")).toEqual({
92
+ isValid: false,
93
+ isPotentiallyValid: true,
94
+ });
95
+ expect(postalCode("1", {})).toEqual({
86
96
  isValid: false,
87
97
  isPotentiallyValid: true,
88
98
  });
@@ -4,7 +4,7 @@ type PostalCodeOptions = {
4
4
  minLength?: number;
5
5
  };
6
6
 
7
- const DEFAULT_MIN_POSTAL_CODE_LENGTH = 3;
7
+ const DEFAULT_MIN_POSTAL_CODE_LENGTH = 2;
8
8
  const ALPHANUM = new RegExp(/^[a-z0-9]+$/i);
9
9
 
10
10
  function verification(