dce-reactkit 3.4.0-beta.8 → 3.4.0
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.
- package/.eslintrc.js +0 -2
- package/dist/cjs/index.js +190 -36371
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/helpers/abbreviate.test.d.ts +5 -0
- package/dist/cjs/types/helpers/avg.test.d.ts +5 -0
- package/dist/cjs/types/helpers/canReviewLogs.d.ts +5 -0
- package/dist/cjs/types/helpers/canReviewLogs.test.d.ts +5 -0
- package/dist/cjs/types/helpers/ceilToNumDecimals.test.d.ts +5 -0
- package/dist/cjs/types/helpers/compareArraysByProp.test.d.ts +5 -0
- package/dist/cjs/types/helpers/extractProp.test.d.ts +5 -0
- package/dist/cjs/types/helpers/floorToNumDecimals.test.d.ts +5 -0
- package/dist/cjs/types/helpers/forceNumIntoBounds.test.d.ts +5 -0
- package/dist/cjs/types/helpers/genCSV.test.d.ts +5 -0
- package/dist/cjs/types/helpers/genCommaList.d.ts +12 -0
- package/dist/cjs/types/helpers/genCommaList.test.d.ts +1 -0
- package/dist/cjs/types/helpers/getHumanReadableDate.test.d.ts +5 -0
- package/dist/cjs/types/helpers/getLocalTimeInfo.test.d.ts +6 -0
- package/dist/cjs/types/helpers/getMonthName.test.d.ts +5 -0
- package/dist/cjs/types/helpers/getOrdinal.test.d.ts +5 -0
- package/dist/cjs/types/helpers/getPartOfDay.test.d.ts +5 -0
- package/dist/cjs/types/helpers/getTimeInfoInET.test.d.ts +5 -0
- package/dist/cjs/types/helpers/validators/shared/constants/ERROR_MESSAGES.d.ts +10 -0
- package/dist/cjs/types/helpers/validators/shared/helpers/validateRegex.d.ts +19 -0
- package/dist/cjs/types/helpers/validators/shared/types/ValidationResult.d.ts +12 -0
- package/dist/cjs/types/helpers/validators/validateEmail.d.ts +11 -0
- package/dist/cjs/types/helpers/validators/validateEmail.test.d.ts +1 -0
- package/dist/cjs/types/helpers/validators/validatePhoneNumber.d.ts +11 -0
- package/dist/cjs/types/helpers/validators/validatePhoneNumber.test.d.ts +1 -0
- package/dist/cjs/types/helpers/validators/validateString.d.ts +22 -0
- package/dist/cjs/types/helpers/validators/validateString.test.d.ts +1 -0
- package/dist/cjs/types/index.d.ts +1 -6
- package/dist/esm/index.js +419 -36579
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/helpers/abbreviate.test.d.ts +5 -0
- package/dist/esm/types/helpers/avg.test.d.ts +5 -0
- package/dist/esm/types/helpers/canReviewLogs.d.ts +5 -0
- package/dist/esm/types/helpers/canReviewLogs.test.d.ts +5 -0
- package/dist/esm/types/helpers/ceilToNumDecimals.test.d.ts +5 -0
- package/dist/esm/types/helpers/compareArraysByProp.test.d.ts +5 -0
- package/dist/esm/types/helpers/extractProp.test.d.ts +5 -0
- package/dist/esm/types/helpers/floorToNumDecimals.test.d.ts +5 -0
- package/dist/esm/types/helpers/forceNumIntoBounds.test.d.ts +5 -0
- package/dist/esm/types/helpers/genCSV.test.d.ts +5 -0
- package/dist/esm/types/helpers/genCommaList.d.ts +12 -0
- package/dist/esm/types/helpers/genCommaList.test.d.ts +1 -0
- package/dist/esm/types/helpers/getHumanReadableDate.test.d.ts +5 -0
- package/dist/esm/types/helpers/getLocalTimeInfo.test.d.ts +6 -0
- package/dist/esm/types/helpers/getMonthName.test.d.ts +5 -0
- package/dist/esm/types/helpers/getOrdinal.test.d.ts +5 -0
- package/dist/esm/types/helpers/getPartOfDay.test.d.ts +5 -0
- package/dist/esm/types/helpers/getTimeInfoInET.test.d.ts +5 -0
- package/dist/esm/types/helpers/validators/shared/constants/ERROR_MESSAGES.d.ts +10 -0
- package/dist/esm/types/helpers/validators/shared/helpers/validateRegex.d.ts +19 -0
- package/dist/esm/types/helpers/validators/shared/types/ValidationResult.d.ts +12 -0
- package/dist/esm/types/helpers/validators/validateEmail.d.ts +11 -0
- package/dist/esm/types/helpers/validators/validateEmail.test.d.ts +1 -0
- package/dist/esm/types/helpers/validators/validatePhoneNumber.d.ts +11 -0
- package/dist/esm/types/helpers/validators/validatePhoneNumber.test.d.ts +1 -0
- package/dist/esm/types/helpers/validators/validateString.d.ts +22 -0
- package/dist/esm/types/helpers/validators/validateString.test.d.ts +1 -0
- package/dist/esm/types/index.d.ts +1 -6
- package/dist/index.d.ts +35 -166
- package/package.json +13 -17
- package/src/client/initClient.tsx +20 -12
- package/src/components/AppWrapper.tsx +1 -1
- package/src/components/ItemPicker/index.test.tsx +10 -10
- package/src/helpers/abbreviate.test.ts +26 -0
- package/src/helpers/avg.test.ts +24 -0
- package/src/helpers/canReviewLogs.test.ts +67 -0
- package/src/helpers/canReviewLogs.ts +20 -0
- package/src/helpers/ceilToNumDecimals.test.ts +39 -0
- package/src/helpers/compareArraysByProp.test.ts +102 -0
- package/src/helpers/extractProp.test.ts +34 -0
- package/src/helpers/floorToNumDecimals.test.ts +39 -0
- package/src/helpers/forceNumIntoBounds.test.ts +34 -0
- package/src/helpers/genCSV.test.ts +61 -0
- package/src/helpers/genCSV.ts +5 -2
- package/src/helpers/genCommaList.test.ts +57 -0
- package/src/helpers/genCommaList.ts +21 -0
- package/src/helpers/getHumanReadableDate.test.ts +29 -0
- package/src/helpers/getLocalTimeInfo.test.ts +48 -0
- package/src/helpers/getMonthName.test.ts +79 -0
- package/src/helpers/getOrdinal.test.ts +84 -0
- package/src/helpers/{getOrdinal.tsx → getOrdinal.ts} +1 -0
- package/src/helpers/getPartOfDay.test.ts +56 -0
- package/src/helpers/getTimeInfoInET.test.ts +72 -0
- package/src/helpers/validators/shared/constants/ERROR_MESSAGES.ts +18 -0
- package/src/helpers/validators/shared/helpers/validateRegex.ts +47 -0
- package/src/helpers/validators/shared/types/ValidationResult.ts +20 -0
- package/src/helpers/validators/validateEmail.test.ts +110 -0
- package/src/helpers/validators/validateEmail.ts +47 -0
- package/src/helpers/validators/validatePhoneNumber.test.ts +94 -0
- package/src/helpers/validators/validatePhoneNumber.ts +49 -0
- package/src/helpers/validators/validateString.test.ts +175 -0
- package/src/helpers/validators/validateString.ts +171 -0
- package/src/index.ts +10 -12
- package/tsconfig.json +6 -2
- package/dist/cjs/types/components/DBEntryManagerPanel/AddOrEditDBEntry/CreatableMultiselect.d.ts +0 -10
- package/dist/cjs/types/components/DBEntryManagerPanel/AddOrEditDBEntry/index.d.ts +0 -34
- package/dist/cjs/types/components/DBEntryManagerPanel/helpers/generateEndpointPath.d.ts +0 -9
- package/dist/cjs/types/components/DBEntryManagerPanel/index.d.ts +0 -25
- package/dist/cjs/types/components/DBEntryManagerPanel/types/DBEntry.d.ts +0 -8
- package/dist/cjs/types/components/DBEntryManagerPanel/types/DBEntryField.d.ts +0 -49
- package/dist/cjs/types/components/DBEntryManagerPanel/types/DBEntryFieldType.d.ts +0 -12
- package/dist/cjs/types/helpers/addDBEditorEndpoints.d.ts +0 -40
- package/dist/esm/types/components/DBEntryManagerPanel/AddOrEditDBEntry/CreatableMultiselect.d.ts +0 -10
- package/dist/esm/types/components/DBEntryManagerPanel/AddOrEditDBEntry/index.d.ts +0 -34
- package/dist/esm/types/components/DBEntryManagerPanel/helpers/generateEndpointPath.d.ts +0 -9
- package/dist/esm/types/components/DBEntryManagerPanel/index.d.ts +0 -25
- package/dist/esm/types/components/DBEntryManagerPanel/types/DBEntry.d.ts +0 -8
- package/dist/esm/types/components/DBEntryManagerPanel/types/DBEntryField.d.ts +0 -49
- package/dist/esm/types/components/DBEntryManagerPanel/types/DBEntryFieldType.d.ts +0 -12
- package/dist/esm/types/helpers/addDBEditorEndpoints.d.ts +0 -40
- package/src/components/DBEntryManagerPanel/AddOrEditDBEntry/CreatableMultiselect.tsx +0 -286
- package/src/components/DBEntryManagerPanel/AddOrEditDBEntry/index.tsx +0 -722
- package/src/components/DBEntryManagerPanel/helpers/generateEndpointPath.ts +0 -15
- package/src/components/DBEntryManagerPanel/index.tsx +0 -511
- package/src/components/DBEntryManagerPanel/types/DBEntry.ts +0 -7
- package/src/components/DBEntryManagerPanel/types/DBEntryField.ts +0 -94
- package/src/components/DBEntryManagerPanel/types/DBEntryFieldType.ts +0 -18
- package/src/helpers/addDBEditorEndpoints.ts +0 -124
- /package/src/helpers/{abbreviate.tsx → abbreviate.ts} +0 -0
- /package/src/helpers/{avg.tsx → avg.ts} +0 -0
- /package/src/helpers/{ceilToNumDecimals.tsx → ceilToNumDecimals.ts} +0 -0
- /package/src/helpers/{floorToNumDecimals.tsx → floorToNumDecimals.ts} +0 -0
- /package/src/helpers/{forceNumIntoBounds.tsx → forceNumIntoBounds.ts} +0 -0
- /package/src/helpers/{getLocalTimeInfo.tsx → getLocalTimeInfo.ts} +0 -0
- /package/src/helpers/{getTimeInfoInET.tsx → getTimeInfoInET.ts} +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test file for the getOrdinal helper
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Import main function
|
|
7
|
+
import getOrdinal from './getOrdinal';
|
|
8
|
+
|
|
9
|
+
describe('getOrdinal', () => {
|
|
10
|
+
it('should return "th" for 0', () => {
|
|
11
|
+
const result = getOrdinal(0);
|
|
12
|
+
expect(result).toEqual('th');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should return "st" for 1', () => {
|
|
16
|
+
const result = getOrdinal(1);
|
|
17
|
+
expect(result).toEqual('st');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should return "nd" for 2', () => {
|
|
21
|
+
const result = getOrdinal(2);
|
|
22
|
+
expect(result).toEqual('nd');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should return "rd" for 3', () => {
|
|
26
|
+
const result = getOrdinal(3);
|
|
27
|
+
expect(result).toEqual('rd');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should return "th" for 4', () => {
|
|
31
|
+
const result = getOrdinal(4);
|
|
32
|
+
expect(result).toEqual('th');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should return "th" for 11', () => {
|
|
36
|
+
const result = getOrdinal(11);
|
|
37
|
+
expect(result).toEqual('th');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should return "st" for 21', () => {
|
|
41
|
+
const result = getOrdinal(21);
|
|
42
|
+
expect(result).toEqual('st');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should return "nd" for 22', () => {
|
|
46
|
+
const result = getOrdinal(22);
|
|
47
|
+
expect(result).toEqual('nd');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should return "rd" for 23', () => {
|
|
51
|
+
const result = getOrdinal(23);
|
|
52
|
+
expect(result).toEqual('rd');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should return "th" for 24', () => {
|
|
56
|
+
const result = getOrdinal(24);
|
|
57
|
+
expect(result).toEqual('th');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should return "th" for 100', () => {
|
|
61
|
+
const result = getOrdinal(100);
|
|
62
|
+
expect(result).toEqual('th');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should return "st" for 101', () => {
|
|
66
|
+
const result = getOrdinal(101);
|
|
67
|
+
expect(result).toEqual('st');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should return "nd" for 102', () => {
|
|
71
|
+
const result = getOrdinal(102);
|
|
72
|
+
expect(result).toEqual('nd');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should return "rd" for 103', () => {
|
|
76
|
+
const result = getOrdinal(103);
|
|
77
|
+
expect(result).toEqual('rd');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should return "th" for 104', () => {
|
|
81
|
+
const result = getOrdinal(104);
|
|
82
|
+
expect(result).toEqual('th');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test file for the getPartOfDay helper
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import getPartOfDay from './getPartOfDay';
|
|
7
|
+
|
|
8
|
+
describe('getPartOfDay', () => {
|
|
9
|
+
it('should return "morning" if the current hour is less than 12', () => {
|
|
10
|
+
// Mock the current hour to be 9am
|
|
11
|
+
jest.spyOn(Date.prototype, 'getHours').mockReturnValue(9);
|
|
12
|
+
|
|
13
|
+
const result = getPartOfDay();
|
|
14
|
+
expect(result).toEqual('morning');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should return "afternoon" if the current hour is between 12 and 16 (inclusive)', () => {
|
|
18
|
+
// Mock the current hour to be 2pm
|
|
19
|
+
jest.spyOn(Date.prototype, 'getHours').mockReturnValue(14);
|
|
20
|
+
|
|
21
|
+
const result = getPartOfDay();
|
|
22
|
+
expect(result).toEqual('afternoon');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should return "evening" if the current hour is between 17 and 24 (inclusive)', () => {
|
|
26
|
+
// Mock the current hour to be 8pm
|
|
27
|
+
jest.spyOn(Date.prototype, 'getHours').mockReturnValue(20);
|
|
28
|
+
|
|
29
|
+
const result = getPartOfDay();
|
|
30
|
+
expect(result).toEqual('evening');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should return "evening" if the current hour is 17', () => {
|
|
34
|
+
// Mock the current hour to be 5pm
|
|
35
|
+
jest.spyOn(Date.prototype, 'getHours').mockReturnValue(17);
|
|
36
|
+
|
|
37
|
+
const result = getPartOfDay();
|
|
38
|
+
expect(result).toEqual('evening');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should return "evening" if the current hour is 24', () => {
|
|
42
|
+
// Mock the current hour to be midnight
|
|
43
|
+
jest.spyOn(Date.prototype, 'getHours').mockReturnValue(24);
|
|
44
|
+
|
|
45
|
+
const result = getPartOfDay();
|
|
46
|
+
expect(result).toEqual('evening');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should return "morning" if the current hour is 0', () => {
|
|
50
|
+
// Mock the current hour to be midnight
|
|
51
|
+
jest.spyOn(Date.prototype, 'getHours').mockReturnValue(0);
|
|
52
|
+
|
|
53
|
+
const result = getPartOfDay();
|
|
54
|
+
expect(result).toEqual('morning');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test file for the getTimeInfoInET helper
|
|
3
|
+
* @author Gabe Abrams
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Import main function
|
|
7
|
+
import getTimeInfoInET from './getTimeInfoInET';
|
|
8
|
+
|
|
9
|
+
describe('getTimeInfoInET', () => {
|
|
10
|
+
it('should return the current time info in ET if no argument is passed', () => {
|
|
11
|
+
const result = getTimeInfoInET();
|
|
12
|
+
const now = new Date();
|
|
13
|
+
const expectedTimestamp = now.getTime();
|
|
14
|
+
const expectedYear = now.getFullYear();
|
|
15
|
+
const expectedMonth = now.getMonth() + 1;
|
|
16
|
+
const expectedDay = now.getDate();
|
|
17
|
+
const expectedHour = now.getHours();
|
|
18
|
+
const expectedHour12 = expectedHour % 12 || 12;
|
|
19
|
+
const expectedMinute = now.getMinutes();
|
|
20
|
+
const expectedIsPM = expectedHour >= 12;
|
|
21
|
+
expect(result.timestamp).toBeCloseTo(expectedTimestamp, -2);
|
|
22
|
+
expect(result.year).toEqual(expectedYear);
|
|
23
|
+
expect(result.month).toEqual(expectedMonth);
|
|
24
|
+
expect(result.day).toEqual(expectedDay);
|
|
25
|
+
expect(result.hour).toEqual(expectedHour);
|
|
26
|
+
expect(result.hour12).toEqual(expectedHour12);
|
|
27
|
+
expect(result.minute).toEqual(expectedMinute);
|
|
28
|
+
expect(result.isPM).toEqual(expectedIsPM);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should return the time info in ET for a specified date', () => {
|
|
32
|
+
const date = new Date('2022-01-01T00:00:00Z');
|
|
33
|
+
const result = getTimeInfoInET(date);
|
|
34
|
+
const expectedTimestamp = date.getTime();
|
|
35
|
+
const expectedYear = 2021;
|
|
36
|
+
const expectedMonth = 12;
|
|
37
|
+
const expectedDay = 31;
|
|
38
|
+
const expectedHour = 19;
|
|
39
|
+
const expectedHour12 = 7;
|
|
40
|
+
const expectedMinute = 0;
|
|
41
|
+
const expectedIsPM = false;
|
|
42
|
+
expect(result.timestamp).toEqual(expectedTimestamp);
|
|
43
|
+
expect(result.year).toEqual(expectedYear);
|
|
44
|
+
expect(result.month).toEqual(expectedMonth);
|
|
45
|
+
expect(result.day).toEqual(expectedDay);
|
|
46
|
+
expect(result.hour).toEqual(expectedHour);
|
|
47
|
+
expect(result.hour12).toEqual(expectedHour12);
|
|
48
|
+
expect(result.minute).toEqual(expectedMinute);
|
|
49
|
+
expect(result.isPM).toEqual(expectedIsPM);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should return the time info in ET for a specified timestamp', () => {
|
|
53
|
+
const timestamp = 1640995200000; // 2022-01-01T00:00:00Z
|
|
54
|
+
const result = getTimeInfoInET(timestamp);
|
|
55
|
+
const expectedTimestamp = timestamp;
|
|
56
|
+
const expectedYear = 2021;
|
|
57
|
+
const expectedMonth = 12;
|
|
58
|
+
const expectedDay = 31;
|
|
59
|
+
const expectedHour = 19;
|
|
60
|
+
const expectedHour12 = 7;
|
|
61
|
+
const expectedMinute = 0;
|
|
62
|
+
const expectedIsPM = false;
|
|
63
|
+
expect(result.timestamp).toEqual(expectedTimestamp);
|
|
64
|
+
expect(result.year).toEqual(expectedYear);
|
|
65
|
+
expect(result.month).toEqual(expectedMonth);
|
|
66
|
+
expect(result.day).toEqual(expectedDay);
|
|
67
|
+
expect(result.hour).toEqual(expectedHour);
|
|
68
|
+
expect(result.hour12).toEqual(expectedHour12);
|
|
69
|
+
expect(result.minute).toEqual(expectedMinute);
|
|
70
|
+
expect(result.isPM).toEqual(expectedIsPM);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*------------------------------------------------------------------------*/
|
|
2
|
+
/* ------------------------------ Constants ----------------------------- */
|
|
3
|
+
/*------------------------------------------------------------------------*/
|
|
4
|
+
|
|
5
|
+
export const INVALID_REGEX_ERROR = 'input does not follow the requested format';
|
|
6
|
+
export const INVALID_EMAIL_ERROR = 'Please provide a valid email address.';
|
|
7
|
+
export const INVALID_PHONE_ERROR = 'Please provide a valid phone number.';
|
|
8
|
+
export const INVALID_STRING_ERRORS = {
|
|
9
|
+
MIN_LEN: (minLen: number) => {
|
|
10
|
+
return `input must not be under ${minLen} character(s)`;
|
|
11
|
+
},
|
|
12
|
+
MAX_LEN: (maxLen: number) => {
|
|
13
|
+
return `input must not be over ${maxLen} character(s)`;
|
|
14
|
+
},
|
|
15
|
+
LETTERS_ONLY: 'input must only contain letters',
|
|
16
|
+
NUMBERS_ONLY: 'input must only contain numbers',
|
|
17
|
+
MESSAGE_INTRO: 'The following error(s) occurred: ',
|
|
18
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Import types
|
|
2
|
+
import ValidationResult from '../types/ValidationResult';
|
|
3
|
+
|
|
4
|
+
// Import constants
|
|
5
|
+
import { INVALID_REGEX_ERROR } from '../constants/ERROR_MESSAGES';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Determines whether a given input string is considered valid based on
|
|
9
|
+
* the provided regex.
|
|
10
|
+
* @author Austen Money
|
|
11
|
+
* @param opts object containing all args
|
|
12
|
+
* @param opts.input user-provided input to validate
|
|
13
|
+
* @param opts.regex regular expression to check against input
|
|
14
|
+
* @param [opts.regexDescription] description of what regexString is checking
|
|
15
|
+
* for, used to customize error message
|
|
16
|
+
* @returns the unchanged input if valid, or a customized error message if
|
|
17
|
+
* invalid
|
|
18
|
+
*/
|
|
19
|
+
const validateRegex = (
|
|
20
|
+
opts: {
|
|
21
|
+
input: string,
|
|
22
|
+
regex: RegExp,
|
|
23
|
+
regexDescription?: string,
|
|
24
|
+
},
|
|
25
|
+
): ValidationResult<string> => {
|
|
26
|
+
// customize error message in case of invalid input
|
|
27
|
+
const errorMessage = `${INVALID_REGEX_ERROR}${
|
|
28
|
+
opts.regexDescription
|
|
29
|
+
? ': '
|
|
30
|
+
: ''
|
|
31
|
+
}${opts.regexDescription}`;
|
|
32
|
+
|
|
33
|
+
// return error message if test is invalid, or input string if valid
|
|
34
|
+
return (
|
|
35
|
+
opts.regex.test(opts.input)
|
|
36
|
+
? {
|
|
37
|
+
isValid: true,
|
|
38
|
+
cleanedValue: opts.input,
|
|
39
|
+
}
|
|
40
|
+
: {
|
|
41
|
+
isValid: false,
|
|
42
|
+
errorMessage,
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default validateRegex;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of a validation function.
|
|
3
|
+
* @author Austen Money
|
|
4
|
+
*/
|
|
5
|
+
type ValidationResult<CleanedValueType> = (
|
|
6
|
+
| {
|
|
7
|
+
// if true, the validation was successful
|
|
8
|
+
isValid: true,
|
|
9
|
+
// the cleaned version of the value
|
|
10
|
+
cleanedValue: CleanedValueType,
|
|
11
|
+
}
|
|
12
|
+
| {
|
|
13
|
+
// if false, the validation was not successful
|
|
14
|
+
isValid: false,
|
|
15
|
+
// describes why the validation failed
|
|
16
|
+
errorMessage: string,
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export default ValidationResult;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Import types
|
|
2
|
+
import ValidationResult from './shared/types/ValidationResult';
|
|
3
|
+
|
|
4
|
+
// Import constants
|
|
5
|
+
import { INVALID_EMAIL_ERROR } from './shared/constants/ERROR_MESSAGES';
|
|
6
|
+
|
|
7
|
+
// Import function
|
|
8
|
+
import validateEmail from './validateEmail';
|
|
9
|
+
|
|
10
|
+
/*------------------------------------------------------------------------*/
|
|
11
|
+
/* ---------------------------- Valid Tests --------------------------- */
|
|
12
|
+
/*------------------------------------------------------------------------*/
|
|
13
|
+
|
|
14
|
+
const validEmailPairs: { input: string, cleaned: string }[] = [
|
|
15
|
+
{
|
|
16
|
+
input: 'username@gmail.com',
|
|
17
|
+
cleaned: 'username@gmail.com',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
input: 'u@hotmail.org',
|
|
21
|
+
cleaned: 'u@hotmail.org',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
input: ' username@gmail.com',
|
|
25
|
+
cleaned: 'username@gmail.com',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
input: 'username@gmail.com ',
|
|
29
|
+
cleaned: 'username@gmail.com',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
input: ' username@gmail.com ',
|
|
33
|
+
cleaned: 'username@gmail.com',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
input: 'username@harvard.edu',
|
|
37
|
+
cleaned: 'username@harvard.edu',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
input: 'username@harvard.subdomain.edu',
|
|
41
|
+
cleaned: 'username@harvard.subdomain.edu',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
input: '\nusername@harvard.subdomain.edu',
|
|
45
|
+
cleaned: 'username@harvard.subdomain.edu',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
input: '\n\tusername@harvard.subdomain.edu',
|
|
49
|
+
cleaned: 'username@harvard.subdomain.edu',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
input: 'username@harvard.subdomain.edu\n',
|
|
53
|
+
cleaned: 'username@harvard.subdomain.edu',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
input: 'username@harvard.subdomain.edu\n\t',
|
|
57
|
+
cleaned: 'username@harvard.subdomain.edu',
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
test(
|
|
62
|
+
'Returns true for a given valid email and removes any leading or trailing whitespace.',
|
|
63
|
+
async () => {
|
|
64
|
+
validEmailPairs.forEach((pair) => {
|
|
65
|
+
const validResponse: ValidationResult<string> = {
|
|
66
|
+
isValid: true,
|
|
67
|
+
cleanedValue: pair.cleaned,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
expect(validateEmail(pair.input)).toStrictEqual(validResponse);
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
/*------------------------------------------------------------------------*/
|
|
76
|
+
/* ---------------------------- Invalid Tests --------------------------- */
|
|
77
|
+
/*------------------------------------------------------------------------*/
|
|
78
|
+
|
|
79
|
+
const invalidEmails: string[] = [
|
|
80
|
+
'',
|
|
81
|
+
' ',
|
|
82
|
+
'gmail.com',
|
|
83
|
+
'@gmail.com ',
|
|
84
|
+
'username',
|
|
85
|
+
'username@.com',
|
|
86
|
+
'user name@gmail.com',
|
|
87
|
+
'username @gmail.com',
|
|
88
|
+
'username@ gmail.com',
|
|
89
|
+
'username@gmail .com',
|
|
90
|
+
'username\t@gmail.com',
|
|
91
|
+
'username\n@gmail.com',
|
|
92
|
+
'username@gmail\n.com',
|
|
93
|
+
'username@gmail.\tcom',
|
|
94
|
+
'user\nname@gmail.com',
|
|
95
|
+
'user\tname@gmail.com',
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
const invalidResponse: ValidationResult<string> = {
|
|
99
|
+
isValid: false,
|
|
100
|
+
errorMessage: INVALID_EMAIL_ERROR,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
test(
|
|
104
|
+
'Returns false for a given invalid email.',
|
|
105
|
+
async () => {
|
|
106
|
+
invalidEmails.forEach((email) => {
|
|
107
|
+
expect(validateEmail(email)).toStrictEqual(invalidResponse);
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Import helpers
|
|
2
|
+
import validateRegex from './shared/helpers/validateRegex';
|
|
3
|
+
|
|
4
|
+
// Import constants
|
|
5
|
+
import { INVALID_EMAIL_ERROR } from './shared/constants/ERROR_MESSAGES';
|
|
6
|
+
|
|
7
|
+
// Import types
|
|
8
|
+
import ValidationResult from './shared/types/ValidationResult';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Determines whether a given email address is valid.
|
|
12
|
+
* @author Austen Money
|
|
13
|
+
* @param email email address to validate
|
|
14
|
+
* @returns whether email fulfills proper formatting requirements, includes a
|
|
15
|
+
* cleaned version of the address without leading or trailing
|
|
16
|
+
* whitespace if valid or an error message if invalid.
|
|
17
|
+
*/
|
|
18
|
+
const validateEmail = (
|
|
19
|
+
email: string,
|
|
20
|
+
): ValidationResult<string> => {
|
|
21
|
+
// validation regex, sourced from HTML living standard: http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
|
|
22
|
+
// eslint-disable-next-line max-len
|
|
23
|
+
const emailRegex = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
24
|
+
|
|
25
|
+
// remove leading and trailing whitespace
|
|
26
|
+
const cleanedValue: string = email.replace(/^\s+|\s+$/g, '');
|
|
27
|
+
|
|
28
|
+
// validate email with regex, and return error if not valid
|
|
29
|
+
return (
|
|
30
|
+
validateRegex(
|
|
31
|
+
{
|
|
32
|
+
input: cleanedValue,
|
|
33
|
+
regex: emailRegex,
|
|
34
|
+
},
|
|
35
|
+
).isValid
|
|
36
|
+
? {
|
|
37
|
+
isValid: true,
|
|
38
|
+
cleanedValue,
|
|
39
|
+
}
|
|
40
|
+
: {
|
|
41
|
+
isValid: false,
|
|
42
|
+
errorMessage: INVALID_EMAIL_ERROR,
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default validateEmail;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Import types
|
|
2
|
+
import ValidationResult from './shared/types/ValidationResult';
|
|
3
|
+
|
|
4
|
+
// Import constants
|
|
5
|
+
import { INVALID_PHONE_ERROR } from './shared/constants/ERROR_MESSAGES';
|
|
6
|
+
|
|
7
|
+
// Import function
|
|
8
|
+
import validatePhoneNumber from './validatePhoneNumber';
|
|
9
|
+
|
|
10
|
+
/*------------------------------------------------------------------------*/
|
|
11
|
+
/* ---------------------------- Valid Tests --------------------------- */
|
|
12
|
+
/*------------------------------------------------------------------------*/
|
|
13
|
+
|
|
14
|
+
const cleanedNumber = '1234567890';
|
|
15
|
+
const cleanedNumberExt = '11234567890';
|
|
16
|
+
|
|
17
|
+
const validPhoneNumbers: { input: string, cleaned: string }[] = [
|
|
18
|
+
{
|
|
19
|
+
input: '1234567890',
|
|
20
|
+
cleaned: cleanedNumber,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
input: '(123)-456-7890',
|
|
24
|
+
cleaned: cleanedNumber,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
input: '123-456-7890',
|
|
28
|
+
cleaned: cleanedNumber,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
input: '(123)456-7890',
|
|
32
|
+
cleaned: cleanedNumber,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
input: '(123)4567890',
|
|
36
|
+
cleaned: cleanedNumber,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
input: ' 123456-7890 ',
|
|
40
|
+
cleaned: cleanedNumber,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
input: '+1 123 456 7890',
|
|
44
|
+
cleaned: cleanedNumberExt,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
input: '+1123456 7890',
|
|
48
|
+
cleaned: cleanedNumberExt,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
input: '+1(123)456-7890',
|
|
52
|
+
cleaned: cleanedNumberExt,
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
test(
|
|
57
|
+
'Returns true for a given valid phone number and removes any non-digit characters.',
|
|
58
|
+
async () => {
|
|
59
|
+
validPhoneNumbers.forEach((pair) => {
|
|
60
|
+
const validResponse: ValidationResult<string> = {
|
|
61
|
+
isValid: true,
|
|
62
|
+
cleanedValue: pair.cleaned,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
expect(validatePhoneNumber(pair.input)).toStrictEqual(validResponse);
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
/*------------------------------------------------------------------------*/
|
|
71
|
+
/* ---------------------------- Invalid Tests --------------------------- */
|
|
72
|
+
/*------------------------------------------------------------------------*/
|
|
73
|
+
|
|
74
|
+
const invalidPhoneNumbers: string[] = [
|
|
75
|
+
'123456789',
|
|
76
|
+
'1234567894567890',
|
|
77
|
+
'1234-567-890',
|
|
78
|
+
'123456m78',
|
|
79
|
+
'123 4567 890',
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const invalidResponse: ValidationResult<string> = {
|
|
83
|
+
isValid: false,
|
|
84
|
+
errorMessage: INVALID_PHONE_ERROR,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
test(
|
|
88
|
+
'Returns false for a given valid phone number.',
|
|
89
|
+
async () => {
|
|
90
|
+
invalidPhoneNumbers.forEach((phoneNumber) => {
|
|
91
|
+
expect(validatePhoneNumber(phoneNumber)).toStrictEqual(invalidResponse);
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Import helpers
|
|
2
|
+
import validateRegex from './shared/helpers/validateRegex';
|
|
3
|
+
|
|
4
|
+
// Import constants
|
|
5
|
+
import { INVALID_PHONE_ERROR } from './shared/constants/ERROR_MESSAGES';
|
|
6
|
+
|
|
7
|
+
// Import types
|
|
8
|
+
import ValidationResult from './shared/types/ValidationResult';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Determines whether a given phone number is valid.
|
|
12
|
+
* @author Austen Money
|
|
13
|
+
* @param phoneNumber phone number to validate
|
|
14
|
+
* @returns whether phone number is considered valid - if valid, also returns
|
|
15
|
+
* a cleaned version of the number without any formatting. If invalid,
|
|
16
|
+
* returns an error message.
|
|
17
|
+
*/
|
|
18
|
+
const validatePhoneNumber = (
|
|
19
|
+
phoneNumber: string,
|
|
20
|
+
): ValidationResult<string> => {
|
|
21
|
+
// regex to validate phone number
|
|
22
|
+
const validationRegex = /^\s*(?:\+?(\d{1,3}))?[-. (]*(\d{3})[-. )]*(\d{3})[-. ]*(\d{4})(?: *x(\d+))?\s*$/;
|
|
23
|
+
|
|
24
|
+
// validate phone number with regex
|
|
25
|
+
const validationResponse = validateRegex(
|
|
26
|
+
{
|
|
27
|
+
input: phoneNumber,
|
|
28
|
+
regex: validationRegex,
|
|
29
|
+
},
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// remove all non-digits from number
|
|
33
|
+
const cleanedValue: string = phoneNumber.replace(/\D/g, '');
|
|
34
|
+
|
|
35
|
+
// return cleaned value if valid, or error message if invalid
|
|
36
|
+
return (
|
|
37
|
+
validationResponse.isValid
|
|
38
|
+
? {
|
|
39
|
+
isValid: true,
|
|
40
|
+
cleanedValue,
|
|
41
|
+
}
|
|
42
|
+
: {
|
|
43
|
+
isValid: false,
|
|
44
|
+
errorMessage: INVALID_PHONE_ERROR,
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default validatePhoneNumber;
|