Skip to content

Commit

Permalink
No jira:
Browse files Browse the repository at this point in the history
- Fix validate display name when text to be validated longer than 27 char with space at the end
- Add allowUnicode param on ValidateDisplayName to support multi language character
- strictSpecialCharacters to allow or disable DisplayName can contain (',. -) at the mid of the text
  • Loading branch information
accelbyte-taufik committed Feb 26, 2020
1 parent 6be8b44 commit c8527aa
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 11 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Added validateChannelName
- Changed validateChannelName separator from "_" to "-"
- Added validateBadWords with multiple localization support
- Fix validate display name when text to be validated longer than 27 char with space at the end
- Add allowUnicode param on ValidateDisplayName to support multi language character
- Add strictSpecialCharacters param on ValidateDisplayName to allow or disable DisplayName can contain (',. -) at the mid of the text
19 changes: 11 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "justice-js-common-utils",
"version": "0.13.1",
"version": "0.14.0",
"description": "AccelByte's Justice Common Javascript Utilities",
"main": "build/main/index.js",
"typings": "build/main/index.d.ts",
Expand Down Expand Up @@ -45,34 +45,31 @@
"peerDependencies": {
"flat": "^4.1.0",
"i18next": "^17.0.7",
"naughty-words": "^1.1.0",
"react": "^16.8.6",
"react-i18next": "^10.11.4",
"validator": "^11.1.0"
},
"devDependencies": {
"flat": "^4.1.0",
"i18next": "^17.0.7",
"react": "^16.8.6",
"react-i18next": "^10.11.4",
"validator": "^11.1.0",
"@bitjson/npm-scripts-info": "^1.0.0",
"@types/flat": "^0.0.28",
"@types/jest": "^24.0.15",
"@types/node": "^12.6.8",
"@types/react": "^16.8.24",
"@types/validator": "^10.11.2",
"flat": "^4.1.0",
"husky": "^3.0.1",
"i18next": "^17.0.7",
"i18next-scanner": "^2.10.2",
"jest": "^24.8.0",
"jest-cli": "^24.8.0",
"json5": "^2.1.0",
"naughty-words": "^1.1.0",
"npm-run-all": "^4.1.5",
"object-assign": "^4.1.1",
"open-cli": "^5.0.0",
"prettier": "^1.18.2",
"raf": "^3.4.1",
"react": "^16.8.6",
"react-i18next": "^10.11.4",
"semver-parser": "^3.0.1",
"standard-version": "^6.0.1",
"trash-cli": "^3.0.0",
Expand All @@ -81,11 +78,17 @@
"tslint-config-prettier": "^1.18.0",
"typedoc": "^0.15.0",
"typescript": "^3.5.3",
"validator": "^11.1.0",
"whatwg-fetch": "^3.0.0"
},
"husky": {
"hooks": {
"pre-commit": "yarn run precommit"
}
},
"dependencies": {
"@types/xregexp": "^3.0.30",
"naughty-words": "^1.1.0",
"xregexp": "^4.3.0"
}
}
16 changes: 16 additions & 0 deletions src/lib/input-validation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,19 @@ class Component extends React.Component {
}
}
```

### Third Party Packages

#### XRegExp
XRegExp provides augmented (and extensible) JavaScript regular expressions. You get modern syntax and flags beyond what browsers support natively. XRegExp is also a regex utility belt with tools to make your grepping and parsing easier

This package used in ValidateDisplayName to validate display and filter special character name in various type of language alphabet (ex: 日本語, русский, 中文, عربى. etc)

Example
```
// Test the Unicode category L (Letter)
const unicodeWord = XRegExp('^\\pL+$');
unicodeWord.test('Русский'); // -> true
unicodeWord.test('日本語'); // -> true
unicodeWord.test('العربية'); // -> true
```
56 changes: 56 additions & 0 deletions src/lib/input-validation/validateDisplayName.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,42 @@ afterEach(mockValidateDisplayName.mockClear);
afterAll(mockValidateDisplayName.mockRestore);

describe("validateDisplayName returns correct output", () => {
it("returns null when using japanese 'ジョンドー' text", () => {
mockValidateDisplayName("ジョンドー", { allowUnicode: true });
expect(mockValidateDisplayName).toHaveBeenCalledTimes(1);
expect(mockValidateDisplayName).toHaveReturnedWith(null);
});

it("returns invalidFormat when using japanese 'ジョンドー' text with no allowUnicode param", () => {
mockValidateDisplayName("ジョンドー");
expect(mockValidateDisplayName).toHaveBeenCalledTimes(1);
expect(mockValidateDisplayName).toHaveReturnedWith(ValidateDisplayNameErrorType.invalidFormat);
});

it("returns invalidFormat when using JP 'ジョン・ドウ' (with middle point (・)) text", () => {
mockValidateDisplayName("ジョン・ドウ", { allowUnicode: true });
expect(mockValidateDisplayName).toHaveBeenCalledTimes(1);
expect(mockValidateDisplayName).toHaveReturnedWith(ValidateDisplayNameErrorType.invalidFormat);
});

it("returns null when using RU 'гладиатор' text", () => {
mockValidateDisplayName("гладиатор", { allowUnicode: true });
expect(mockValidateDisplayName).toHaveBeenCalledTimes(1);
expect(mockValidateDisplayName).toHaveReturnedWith(null);
});

it("returns invalidFormat when using RU 'гладиатор' text with no allowUnicode param", () => {
mockValidateDisplayName("гладиатор");
expect(mockValidateDisplayName).toHaveBeenCalledTimes(1);
expect(mockValidateDisplayName).toHaveReturnedWith(ValidateDisplayNameErrorType.invalidFormat);
});

it("returns null when using AR '١المصارع١' text", () => {
mockValidateDisplayName("المصارع", { allowUnicode: true });
expect(mockValidateDisplayName).toHaveBeenCalledTimes(1);
expect(mockValidateDisplayName).toHaveReturnedWith(null);
});

it("returns empty error string when given empty string, but it is not a required field", () => {
mockValidateDisplayName("", { isRequired: false });
expect(mockValidateDisplayName).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -77,6 +113,12 @@ describe("validateDisplayName returns correct output", () => {
expect(mockValidateDisplayName).toHaveReturnedWith(ValidateDisplayNameErrorType.exceedLengthLimit);
});

it("returns error string containing invalidFormat with given long string with space at the end", () => {
mockValidateDisplayName("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvsafdsafsdfgsdfgw ");
expect(mockValidateDisplayName).toHaveBeenCalledTimes(1);
expect(mockValidateDisplayName).toHaveReturnedWith(ValidateDisplayNameErrorType.invalidFormat);
});

it("returns error string 'empty' when given empty string", () => {
mockValidateDisplayName("");
expect(mockValidateDisplayName).toHaveBeenCalledTimes(1);
Expand All @@ -94,4 +136,18 @@ describe("validateDisplayName returns correct output", () => {
expect(mockValidateDisplayName).toHaveBeenCalledTimes(1);
expect(mockValidateDisplayName).toHaveReturnedWith(ValidateDisplayNameErrorType.invalidFormat);
});

// tslint:disable-next-line:max-line-length
it("returns invalidFormat when given EN string with 1 symbol other than `',. -` with strictlyAllowSpecialCharacters", () => {
mockValidateDisplayName("John Doe", { strictlyAllowSpecialCharacters: false });
expect(mockValidateDisplayName).toHaveBeenCalledTimes(1);
expect(mockValidateDisplayName).toHaveReturnedWith(ValidateDisplayNameErrorType.invalidFormat);
});

// tslint:disable-next-line:max-line-length
it("returns invalidFormat when given RU string with 1 symbol other than `',. -` with strictlyAllowSpecialCharacters", () => {
mockValidateDisplayName("глади атор", { allowUnicode: true, strictlyAllowSpecialCharacters: false });
expect(mockValidateDisplayName).toHaveBeenCalledTimes(1);
expect(mockValidateDisplayName).toHaveReturnedWith(ValidateDisplayNameErrorType.invalidFormat);
});
});
36 changes: 33 additions & 3 deletions src/lib/input-validation/validateDisplayName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import { isEmpty, matches } from "validator";
import XRegExp from "xregexp";
import { Enum, ExtendEnum } from "../../types/types";
import { CommonValidationErrorType } from "./constant/errorType";
import { MAX_DISPLAY_NAME_LENGTH } from "./constant/numbers";
Expand All @@ -17,20 +18,49 @@ export const ValidateDisplayNameErrorType = ExtendEnum(
export type ValidateDisplayNameErrorType = Enum<typeof ValidateDisplayNameErrorType>;

export interface ValidateDisplayNameOptions {
allowUnicode?: boolean;
isRequired?: boolean;
strictlyAllowSpecialCharacters?: boolean;
}

export const validateDisplayName = (value: string, { isRequired = true }: ValidateDisplayNameOptions = {}) => {
const REGEX = "^[a-zA-Z0-9]+(([',. -][a-zA-Z0-9])?[a-zA-Z0-9]*)*$";
/**
*
* @param value
* @param allowUnicode (true: Allow various language character, false: only allow Alpha Numeric character)
* @param isRequired
* @param strictlyAllowSpecialCharacters (true: allow (',. -) in the mid of value, false: Only allow Alpha Numeric)
*
* @default allowUnicode false
* @default isRequired true
* @default strictlyAllowSpecialCharacters true
*/
export const validateDisplayName = (
value: string,
{ allowUnicode = false, isRequired = true, strictlyAllowSpecialCharacters = true }: ValidateDisplayNameOptions = {}
) => {
if (isEmpty(value)) {
if (!isRequired) {
return null;
}
return ValidateDisplayNameErrorType.empty;
}
if (!matches(value, REGEX)) {

if (allowUnicode && !strictlyAllowSpecialCharacters && !XRegExp("^[\\pL\\pN]*$").test(value)) {
return ValidateDisplayNameErrorType.invalidFormat;
}

if (allowUnicode && strictlyAllowSpecialCharacters && !XRegExp("^[\\pL\\pN]+([',. -][\\pL\\pN]+)*$").test(value)) {
return ValidateDisplayNameErrorType.invalidFormat;
}

if (!allowUnicode && !strictlyAllowSpecialCharacters && !matches(value, /^\w*$/g)) {
return ValidateDisplayNameErrorType.invalidFormat;
}

if (!allowUnicode && strictlyAllowSpecialCharacters && !matches(value, "^\\w+([',. -]\\w+)*$")) {
return ValidateDisplayNameErrorType.invalidFormat;
}

return validateLength(value, {
max: MAX_DISPLAY_NAME_LENGTH,
});
Expand Down
21 changes: 21 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"

"@babel/runtime-corejs3@^7.8.3":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.8.4.tgz#ccc4e042e2fae419c67fa709567e5d2179ed3940"
dependencies:
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.2"

"@babel/runtime@^7.3.1":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
Expand Down Expand Up @@ -385,6 +392,10 @@
version "10.11.2"
resolved "https://registry.yarnpkg.com/@types/validator/-/validator-10.11.2.tgz#48b60ca2cca927081f37a1ad1de3e25d04abc9f0"

"@types/xregexp@^3.0.30":
version "3.0.30"
resolved "https://registry.yarnpkg.com/@types/xregexp/-/xregexp-3.0.30.tgz#333d550467dd27ef989f375629f8f279a97cee39"

"@types/yargs@^12.0.2", "@types/yargs@^12.0.9":
version "12.0.12"
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.12.tgz#45dd1d0638e8c8f153e87d296907659296873916"
Expand Down Expand Up @@ -1161,6 +1172,10 @@ copy-descriptor@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"

core-js-pure@^3.0.0:
version "3.6.4"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a"

[email protected], core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
Expand Down Expand Up @@ -5256,6 +5271,12 @@ xml-name-validator@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"

xregexp@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50"
dependencies:
"@babel/runtime-corejs3" "^7.8.3"

xtend@~4.0.0, xtend@~4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
Expand Down

0 comments on commit c8527aa

Please sign in to comment.