Skip to content

Commit

Permalink
Add cardholder name validation (#86)
Browse files Browse the repository at this point in the history
* Add cardholder name validation

addresses #33

* Update src/__tests__/cardholder-name.ts

Co-authored-by: Holly Stotelmyer <[email protected]>

* Update src/__tests__/cardholder-name.ts

* prettier

Co-authored-by: Holly Stotelmyer <[email protected]>
  • Loading branch information
crookedneighbor and hollabaq86 authored Aug 24, 2020
1 parent 4c9266f commit ad2456d
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# unreleased

- Add `cardholderName` verification method

# 8.0.0

_Breaking Changes_
Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,35 @@ A fake session where a user is entering a card number may look like:

---

#### `valid.cardholderName(value: string): object`

The `cardholderName` validation essentially tests for a valid string greater than 0 characters in length that does not look like a card number.

```javascript
{
isPotentiallyValid: true,
isValid: true
}
```

If a cardholder name is comprised of only numbers, hyphens and spaces, the validator considers it to be too card-like to be valid, but may still be potentially valid if a non-numeric character is added. This is to prevent card number values from being sent along as the cardholder name but not make too many assumptions about a person's cardholder name.

```javascript
{
isPotentiallyValid: true,
isValid: false
}
```

If a cardholder name is longer than 255 characters, it is assumed to be invalid.

```javascript
{
isPotentiallyValid: false,
isValid: false
}
```

#### `valid.expirationDate(value: string|object, maxElapsedYear: integer): object`

The `maxElapsedYear` parameter determines how many years in the future a card's expiration date should be considered valid. It has a default value of 19, so cards with an expiration date 20 or more years in the future would not be considered valid. It can be overridden by passing in an `integer` as a second argument.
Expand Down
73 changes: 73 additions & 0 deletions src/__tests__/cardholder-name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { cardholderName } from "../cardholder-name";
import type { Verification } from "../types";

describe("cardholderName", () => {
describe.each([
[
"returns false for non-string types",
[
[0, { isValid: false, isPotentiallyValid: false }],
[0, { isValid: false, isPotentiallyValid: false }],
[123, { isValid: false, isPotentiallyValid: false }],
[1234, { isValid: false, isPotentiallyValid: false }],
[12345, { isValid: false, isPotentiallyValid: false }],
[557016, { isValid: false, isPotentiallyValid: false }],
[-1234, { isValid: false, isPotentiallyValid: false }],
[-10, { isValid: false, isPotentiallyValid: false }],
[0 / 0, { isValid: false, isPotentiallyValid: false }],
[Infinity, { isValid: false, isPotentiallyValid: false }],
[null, { isValid: false, isPotentiallyValid: false }],
[[], { isValid: false, isPotentiallyValid: false }],
[{}, { isValid: false, isPotentiallyValid: false }],
],
],

[
"returns false strings that are longer than 255 characters",
[
[
"this name is 256 chracters aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
{ isValid: false, isPotentiallyValid: false },
],
],
],

[
"accepts valid cardholder names",
[
["name", { isValid: true, isPotentiallyValid: true }],
["given sur", { isValid: true, isPotentiallyValid: true }],
[
"this name is 255 chracters aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
{ isValid: true, isPotentiallyValid: true },
],
["name with many spaces", { isValid: true, isPotentiallyValid: true }],
[
"name with number in it 01234",
{ isValid: true, isPotentiallyValid: true },
],
],
],

[
"returns isPotentiallyValid for shorter-than-1 strings",
[["", { isValid: false, isPotentiallyValid: true }]],
],

[
"returns isPotentiallyValid for strings with only numbers, spaces, and hyphens",
[
["4111", { isValid: false, isPotentiallyValid: true }],
["4111 1111", { isValid: false, isPotentiallyValid: true }],
["4111-1111", { isValid: false, isPotentiallyValid: true }],
],
],
] as Array<[string, Array<[string, Verification]>]>)(
"%s",
(description, tests) => {
it.each(tests)("parses %s to be %p", (parseMe, meta) => {
expect(cardholderName(parseMe)).toEqual(meta);
});
}
);
});
31 changes: 31 additions & 0 deletions src/cardholder-name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Verification } from "./types";

const CARD_NUMBER_REGEX = /^[\d\s-]*$/;
const MAX_LENGTH = 255;

function verification(
isValid: boolean,
isPotentiallyValid: boolean
): Verification {
return { isValid, isPotentiallyValid };
}

export function cardholderName(value: string | unknown): Verification {
if (typeof value !== "string") {
return verification(false, false);
}

if (value.length === 0) {
return verification(false, true);
}

if (value.length > MAX_LENGTH) {
return verification(false, false);
}

if (CARD_NUMBER_REGEX.test(value)) {
return verification(false, true);
}

return verification(true, true);
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as creditCardType from "credit-card-type";

import { cardholderName } from "./cardholder-name";
import { cardNumber as number } from "./card-number";
import { expirationDate } from "./expiration-date";
import { expirationMonth } from "./expiration-month";
Expand All @@ -9,6 +10,7 @@ import { postalCode } from "./postal-code";

const cardValidator = {
creditCardType,
cardholderName,
number,
expirationDate,
expirationMonth,
Expand Down

0 comments on commit ad2456d

Please sign in to comment.