@jdlien/validator 1.0.3 → 1.0.5
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/.prettierrc +8 -0
- package/README.md +46 -3
- package/demo-src.css +106 -0
- package/demo.html +779 -0
- package/index.ts +5 -0
- package/package.json +1 -10
- package/src/Validator.ts +628 -0
- package/src/types.d.ts +23 -0
- package/src/validator-utils.ts +656 -0
- package/tailwind.config.cjs +12 -0
- package/tests/Validator.test.ts +1922 -0
- package/tests/utils.test.ts +1048 -0
- package/tsconfig.json +19 -0
- package/vite.config.js +22 -0
- package/dist/validator.js +0 -1
|
@@ -0,0 +1,1048 @@
|
|
|
1
|
+
import * as utils from '../src/validator-utils'
|
|
2
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
3
|
+
|
|
4
|
+
describe('utils', () => {
|
|
5
|
+
describe('isFormControl', () => {
|
|
6
|
+
it('should return true if the element is an instance of HTMLInputElement', () => {
|
|
7
|
+
const input = document.createElement('input')
|
|
8
|
+
expect(utils.isFormControl(input)).toBe(true)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('should return true if the element is an instance of HTMLSelectElement', () => {
|
|
12
|
+
const select = document.createElement('select')
|
|
13
|
+
expect(utils.isFormControl(select)).toBe(true)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('should return true if the element is an instance of HTMLTextAreaElement', () => {
|
|
17
|
+
const textarea = document.createElement('textarea')
|
|
18
|
+
expect(utils.isFormControl(textarea)).toBe(true)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('should return false if the element is not an instance of HTMLInputElement, HTMLSelectElement or HTMLTextAreaElement', () => {
|
|
22
|
+
const div = document.createElement('div')
|
|
23
|
+
expect(utils.isFormControl(div)).toBe(false)
|
|
24
|
+
})
|
|
25
|
+
}) // end isFormControl
|
|
26
|
+
|
|
27
|
+
describe('isType', () => {
|
|
28
|
+
let inputElement: HTMLInputElement
|
|
29
|
+
let textAreaElement: HTMLTextAreaElement
|
|
30
|
+
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
inputElement = document.createElement('input')
|
|
33
|
+
textAreaElement = document.createElement('textarea')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('should return true for matching data-type attribute', () => {
|
|
37
|
+
inputElement.setAttribute('data-type', 'text')
|
|
38
|
+
expect(utils.isType(inputElement, 'text')).toBe(true)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should return true for matching type attribute', () => {
|
|
42
|
+
inputElement.setAttribute('type', 'text')
|
|
43
|
+
expect(utils.isType(inputElement, 'text')).toBe(true)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should return true for matching type attribute', () => {
|
|
47
|
+
inputElement.setAttribute('type', 'radio')
|
|
48
|
+
expect(utils.isType(inputElement, ['radio', 'checkbox'])).toBe(true)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('should return true for matching data-type or type attribute in an array of types', () => {
|
|
52
|
+
textAreaElement.setAttribute('data-type', 'textarea')
|
|
53
|
+
inputElement.setAttribute('type', 'text')
|
|
54
|
+
expect(utils.isType(textAreaElement, ['text', 'textarea'])).toBe(true)
|
|
55
|
+
expect(utils.isType(inputElement, ['text', 'textarea'])).toBe(true)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('should return false for non-matching data-type and type attribute', () => {
|
|
59
|
+
inputElement.setAttribute('type', 'email')
|
|
60
|
+
inputElement.setAttribute('data-type', 'email')
|
|
61
|
+
expect(utils.isType(inputElement, 'text')).toBe(false)
|
|
62
|
+
expect(utils.isType(textAreaElement, 'text')).toBe(false)
|
|
63
|
+
})
|
|
64
|
+
}) // end isType
|
|
65
|
+
|
|
66
|
+
describe('momentToFPFormat', () => {
|
|
67
|
+
it('should correctly convert YYYY to Y', () => {
|
|
68
|
+
expect(utils.momentToFPFormat('YYYY-MM-DD')).toEqual('Y-m-d')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('should correctly convert YY to y', () => {
|
|
72
|
+
expect(utils.momentToFPFormat('YY-MM-DD')).toEqual('y-m-d')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should correctly convert MMMM to F', () => {
|
|
76
|
+
expect(utils.momentToFPFormat('YYYY-MMMM-DD')).toEqual('Y-F-d')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('should correctly convert MMM to M', () => {
|
|
80
|
+
expect(utils.momentToFPFormat('YYYY-MMM-DD')).toEqual('Y-M-d')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should correctly convert MM to m', () => {
|
|
84
|
+
expect(utils.momentToFPFormat('YYYY-MM-DD')).toEqual('Y-m-d')
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('should correctly convert M to n', () => {
|
|
88
|
+
expect(utils.momentToFPFormat('YYYY-M-DD')).toEqual('Y-n-d')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('should correctly convert DD to d', () => {
|
|
92
|
+
expect(utils.momentToFPFormat('YYYY-MM-DD')).toEqual('Y-m-d')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('should correctly convert D to j', () => {
|
|
96
|
+
expect(utils.momentToFPFormat('YYYY-MM-D')).toEqual('Y-m-j')
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('should correctly convert dddd to l', () => {
|
|
100
|
+
expect(utils.momentToFPFormat('dddd, MMMM DD YYYY')).toEqual('l, F d Y')
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('should correctly convert ddd to D', () => {
|
|
104
|
+
expect(utils.momentToFPFormat('ddd, MMM DD YYYY')).toEqual('D, M d Y')
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('should correctly convert dd to D', () => {
|
|
108
|
+
expect(utils.momentToFPFormat('dd, MMM DD YYYY')).toEqual('D, M d Y')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('should correctly convert d to w', () => {
|
|
112
|
+
expect(utils.momentToFPFormat('d, MMM DD YYYY')).toEqual('w, M d Y')
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('should correctly convert HH to H', () => {
|
|
116
|
+
expect(utils.momentToFPFormat('HH:mm:ss')).toEqual('H:i:S')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('should correctly convert H to G', () => {
|
|
120
|
+
expect(utils.momentToFPFormat('H:mm:ss')).toEqual('G:i:S')
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('should correctly convert 12hr hh to h unpadded', () => {
|
|
124
|
+
expect(utils.momentToFPFormat('h:m:s')).toEqual('h:i:s')
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('should correctly convert 12hr hh to h padded', () => {
|
|
128
|
+
expect(utils.momentToFPFormat('hh:mm:ss')).toEqual('h:i:S')
|
|
129
|
+
})
|
|
130
|
+
}) // momentToFPFormat
|
|
131
|
+
|
|
132
|
+
describe('monthToNumber', () => {
|
|
133
|
+
it('returns the correct zero-based month number for numeric input', () => {
|
|
134
|
+
expect(utils.monthToNumber(1)).toEqual(0)
|
|
135
|
+
expect(utils.monthToNumber('2')).toEqual(1)
|
|
136
|
+
expect(utils.monthToNumber('12')).toEqual(11)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('returns the correct zero-based month number for string input', () => {
|
|
140
|
+
expect(utils.monthToNumber('January')).toEqual(0)
|
|
141
|
+
expect(utils.monthToNumber('Ja')).toEqual(0) // two digits is enough if unambiguous
|
|
142
|
+
expect(utils.monthToNumber('feb')).toEqual(1)
|
|
143
|
+
expect(utils.monthToNumber('Aug')).toEqual(7)
|
|
144
|
+
expect(utils.monthToNumber('september')).toEqual(8)
|
|
145
|
+
expect(utils.monthToNumber('nov')).toEqual(10)
|
|
146
|
+
expect(utils.monthToNumber('décembre')).toEqual(11)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('throws an error for invalid input', () => {
|
|
150
|
+
expect(() => utils.monthToNumber('foo')).toThrowError('Invalid month name: foo')
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
describe('yearToFull', () => {
|
|
155
|
+
describe('yearToFull', () => {
|
|
156
|
+
it('converts a two-digit year to a four-digit year in the 20th century', () => {
|
|
157
|
+
expect(utils.yearToFull(87)).toBe(1987)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('converts a two-digit year to a four-digit year in the 21st century', () => {
|
|
161
|
+
expect(utils.yearToFull(22)).toBe(2022)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('returns a four-digit year unchanged', () => {
|
|
165
|
+
expect(utils.yearToFull(2000)).toBe(2000)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('returns a four-digit year unchanged if year is greater than 99', () => {
|
|
169
|
+
expect(utils.yearToFull(2099)).toBe(2099)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('handles string input with non-numeric characters', () => {
|
|
173
|
+
expect(utils.yearToFull('a22')).toBe(2022)
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('handles string input with extra spaces', () => {
|
|
177
|
+
expect(utils.yearToFull(' 87 ')).toBe(1987)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('handles years in the past as long as they are over three digits', () => {
|
|
181
|
+
expect(utils.yearToFull('203')).toBe(203)
|
|
182
|
+
expect(utils.yearToFull('1901')).toBe(1901)
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
describe('parseDate', function () {
|
|
188
|
+
let today: Date
|
|
189
|
+
let yesterday: Date
|
|
190
|
+
let tomorrow: Date
|
|
191
|
+
|
|
192
|
+
beforeEach(() => {
|
|
193
|
+
today = new Date()
|
|
194
|
+
yesterday = new Date()
|
|
195
|
+
tomorrow = new Date()
|
|
196
|
+
|
|
197
|
+
today.setHours(0)
|
|
198
|
+
today.setMinutes(0)
|
|
199
|
+
today.setSeconds(0)
|
|
200
|
+
today.setMilliseconds(0)
|
|
201
|
+
|
|
202
|
+
yesterday.setHours(0)
|
|
203
|
+
yesterday.setMinutes(0)
|
|
204
|
+
yesterday.setSeconds(0)
|
|
205
|
+
yesterday.setMilliseconds(0)
|
|
206
|
+
yesterday.setDate(today.getDate() - 1)
|
|
207
|
+
|
|
208
|
+
tomorrow.setHours(0)
|
|
209
|
+
tomorrow.setMinutes(0)
|
|
210
|
+
tomorrow.setSeconds(0)
|
|
211
|
+
tomorrow.setMilliseconds(0)
|
|
212
|
+
tomorrow.setDate(today.getDate() + 1)
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('should return a date object', function () {
|
|
216
|
+
expect(utils.parseDate('2019-01-01')).toEqual(new Date('2019-01-01 00:00:00'))
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('should return a date object with a time if only a time is set', function () {
|
|
220
|
+
const today1300 = new Date()
|
|
221
|
+
today1300.setHours(13)
|
|
222
|
+
today1300.setMinutes(0)
|
|
223
|
+
today1300.setSeconds(0)
|
|
224
|
+
today1300.setMilliseconds(0)
|
|
225
|
+
|
|
226
|
+
expect(utils.parseDate('13:00')).toEqual(today1300)
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('returns correct date', () => {
|
|
230
|
+
expect(utils.parseDate('1/2/3')).toEqual(new Date('2003/01/02 00:00:00'))
|
|
231
|
+
expect(utils.parseDate('2000-2-1')).toEqual(new Date('2000/02/01 00:00:00'))
|
|
232
|
+
expect(utils.parseDate('Jan 5 2000')).toEqual(new Date('2000/01/05 00:00:00'))
|
|
233
|
+
expect(utils.parseDate('5 Jan 99')).toEqual(new Date('1999/01/05 00:00:00'))
|
|
234
|
+
|
|
235
|
+
// Numbers over 12 will either be date or year
|
|
236
|
+
// m/d/y
|
|
237
|
+
expect(utils.parseDate('3 30 05')).toEqual(new Date('2005/03/30 00:00:00'))
|
|
238
|
+
|
|
239
|
+
// m/d/y
|
|
240
|
+
expect(utils.parseDate('3 05 30')).toEqual(new Date('2030/03/05 00:00:00'))
|
|
241
|
+
|
|
242
|
+
// Anything alphabetic should be interpreted as a month.
|
|
243
|
+
// Also, if the month comes first, and remaining is ambiguous,
|
|
244
|
+
// the second token should be interpreted as the day
|
|
245
|
+
expect(utils.parseDate('jan/5/30')).toEqual(new Date('2030/01/05 00:00:00'))
|
|
246
|
+
|
|
247
|
+
// If the month and year are unambiguous, the remaining token is the day
|
|
248
|
+
// Unsupported weird formats
|
|
249
|
+
expect(utils.parseDate('jan/2005/10')).toEqual(new Date('2005/01/10 00:00:00'))
|
|
250
|
+
expect(utils.parseDate('30 2001 Jan')).toEqual(new Date('2001/01/30 00:00:00'))
|
|
251
|
+
|
|
252
|
+
expect(utils.parseDate('Jan 5, 30')).toEqual(new Date('2030/01/05 00:00:00'))
|
|
253
|
+
expect(utils.parseDate('Jan 30, 2001')).toEqual(new Date('2001/01/30 00:00:00'))
|
|
254
|
+
expect(utils.parseDate('30 Jan 2001')).toEqual(new Date('2001/01/30 00:00:00'))
|
|
255
|
+
|
|
256
|
+
// unsupported
|
|
257
|
+
expect(utils.parseDate('010203')).toEqual(new Date('2001/02/03'))
|
|
258
|
+
|
|
259
|
+
// 8-digit number is interpreted as YYYYMMDD
|
|
260
|
+
expect(utils.parseDate('20010203')).toEqual(new Date('2001/02/03 00:00:00'))
|
|
261
|
+
|
|
262
|
+
// Apostrophes are ignored
|
|
263
|
+
expect(utils.parseDate("1 2 '02")).toEqual(new Date('2002/01/02 00:00:00'))
|
|
264
|
+
// This will be interpreted as 2001-Feb-03
|
|
265
|
+
expect(utils.parseDate("'01 2 03")).toEqual(new Date('2001/02/03 00:00:00'))
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
it('adds the current year if no year was given', () => {
|
|
269
|
+
expect(utils.parseDate('02-03')).toEqual(new Date(today.getFullYear() + '/02/03'))
|
|
270
|
+
expect(utils.parseDate('Jan-06')).toEqual(new Date(today.getFullYear() + '/01/06'))
|
|
271
|
+
expect(utils.parseDate('12-30')).toEqual(new Date(today.getFullYear() + '/12/30'))
|
|
272
|
+
expect(utils.parseDate('1 3')).toEqual(new Date(today.getFullYear() + '/1/3'))
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
it('ignores any weekdays', () => {
|
|
276
|
+
expect(utils.parseDate('Saturday 30 Jan 2001')).toEqual(new Date('2001/01/30 00:00:00'))
|
|
277
|
+
expect(utils.parseDate('30 Jan 2001 Mon')).toEqual(new Date('2001/01/30 00:00:00'))
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
// In apps-date this would be interpreted as 1999-02-01
|
|
281
|
+
it('returns invalid date when passed date that does not exist', () => {
|
|
282
|
+
expect(utils.parseDate('1999-02-29')).toEqual(new Date('1999/02/29 00:00:00'))
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
it("returns now as today's date", () => {
|
|
286
|
+
expect(utils.parseDate('now')).toEqual(today)
|
|
287
|
+
expect(utils.parseDate('today')).toEqual(today)
|
|
288
|
+
// Only support English for now
|
|
289
|
+
// expect(utils.parseDate('ahora')).toEqual(today)
|
|
290
|
+
// expect(utils.parseDate("aujourd'hui")).toEqual(today)
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
// Removed support for yesterday
|
|
294
|
+
// it('returns yesterday as yesterday\'s date', () => {
|
|
295
|
+
// expect(utils.parseDate('yesterday')).toEqual(yesterday)
|
|
296
|
+
// expect(utils.parseDate('ayer')).toEqual(yesterday)
|
|
297
|
+
// expect(utils.parseDate('hier')).toEqual(yesterday)
|
|
298
|
+
// })
|
|
299
|
+
|
|
300
|
+
it("returns 'tomorrow' as tomorrow's date", () => {
|
|
301
|
+
expect(utils.parseDate('tomorrow')).toEqual(tomorrow)
|
|
302
|
+
// expect(utils.parseDate('manana')).toEqual(tomorrow)
|
|
303
|
+
// expect(utils.parseDate('demain')).toEqual(tomorrow)
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
it('returns correct time when a time is passed', () => {
|
|
307
|
+
expect(utils.parseDate('2001-04-20 4:00p')).toEqual(new Date(2001, 3, 20, 16, 0, 0, 0))
|
|
308
|
+
// // expect(utils.parseDate('manana')).toEqual(tomorrow)
|
|
309
|
+
// // expect(utils.parseDate('demain')).toEqual(tomorrow)
|
|
310
|
+
})
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
describe('parseTime', function () {
|
|
314
|
+
it('should return an object with hour, minute, and second properties', function () {
|
|
315
|
+
const time = '12:34:56'
|
|
316
|
+
const result = utils.parseTime(time)
|
|
317
|
+
expect(result).toMatchObject({
|
|
318
|
+
hour: 12,
|
|
319
|
+
minute: 34,
|
|
320
|
+
second: 56,
|
|
321
|
+
})
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
it('should handle leading zeros', function () {
|
|
325
|
+
const time = '01:02:03'
|
|
326
|
+
const result = utils.parseTime(time)
|
|
327
|
+
expect(result).toMatchObject({
|
|
328
|
+
hour: 1,
|
|
329
|
+
minute: 2,
|
|
330
|
+
second: 3,
|
|
331
|
+
})
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
it('should handle times with only hours and minutes', function () {
|
|
335
|
+
const time = '12:34'
|
|
336
|
+
const result = utils.parseTime(time)
|
|
337
|
+
expect(result).toMatchObject({
|
|
338
|
+
hour: 12,
|
|
339
|
+
minute: 34,
|
|
340
|
+
second: 0,
|
|
341
|
+
})
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
it('should handle times with only hours', function () {
|
|
345
|
+
const time = '12'
|
|
346
|
+
const result = utils.parseTime(time)
|
|
347
|
+
expect(result).toMatchObject({
|
|
348
|
+
hour: 12,
|
|
349
|
+
minute: 0,
|
|
350
|
+
second: 0,
|
|
351
|
+
})
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
it('should handle 24h time', function () {
|
|
355
|
+
const time = '23:59:59'
|
|
356
|
+
const result = utils.parseTime(time)
|
|
357
|
+
expect(result).toMatchObject({
|
|
358
|
+
hour: 23,
|
|
359
|
+
minute: 59,
|
|
360
|
+
second: 59,
|
|
361
|
+
})
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
it('should handle 12h times with meridiem', function () {
|
|
365
|
+
const time = '12a'
|
|
366
|
+
const result = utils.parseTime(time)
|
|
367
|
+
expect(result).toMatchObject({
|
|
368
|
+
hour: 0,
|
|
369
|
+
minute: 0,
|
|
370
|
+
second: 0,
|
|
371
|
+
})
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
it('should handle 12h times with meridiem', function () {
|
|
375
|
+
const time = '1p'
|
|
376
|
+
const result = utils.parseTime(time)
|
|
377
|
+
expect(result).toMatchObject({
|
|
378
|
+
hour: 13,
|
|
379
|
+
minute: 0,
|
|
380
|
+
second: 0,
|
|
381
|
+
})
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
it('should handle 12h times with meridiem', function () {
|
|
385
|
+
const time = '2 p'
|
|
386
|
+
const result = utils.parseTime(time)
|
|
387
|
+
expect(result).toMatchObject({
|
|
388
|
+
hour: 14,
|
|
389
|
+
minute: 0,
|
|
390
|
+
second: 0,
|
|
391
|
+
})
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
it('should handle 12h times with meridiem', function () {
|
|
395
|
+
const time = '2:31 p'
|
|
396
|
+
const result = utils.parseTime(time)
|
|
397
|
+
expect(result).toMatchObject({
|
|
398
|
+
hour: 14,
|
|
399
|
+
minute: 31,
|
|
400
|
+
second: 0,
|
|
401
|
+
})
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
it('should handle 12h times with meridiem', function () {
|
|
405
|
+
const time = '11:31:29 p'
|
|
406
|
+
const result = utils.parseTime(time)
|
|
407
|
+
expect(result).toMatchObject({
|
|
408
|
+
hour: 23,
|
|
409
|
+
minute: 31,
|
|
410
|
+
second: 29,
|
|
411
|
+
})
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
it('should handle invalid input', function () {
|
|
415
|
+
const time = 'abc'
|
|
416
|
+
const result = utils.parseTime(time)
|
|
417
|
+
expect(result).toBe(null)
|
|
418
|
+
})
|
|
419
|
+
}) // end parseTime
|
|
420
|
+
|
|
421
|
+
describe('parseTimeToString', function () {
|
|
422
|
+
it('should return a formatted string for a valid time', function () {
|
|
423
|
+
const value = '12:34:56'
|
|
424
|
+
const result = utils.parseTimeToString(value)
|
|
425
|
+
expect(result).toEqual('12:34 PM')
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
it('should handle leading zeros', function () {
|
|
429
|
+
const value = '01:02:03'
|
|
430
|
+
const result = utils.parseTimeToString(value)
|
|
431
|
+
expect(result).toEqual('1:02 AM')
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
it('should handle times with only hours and minutes', function () {
|
|
435
|
+
const value = '12:34'
|
|
436
|
+
const result = utils.parseTimeToString(value)
|
|
437
|
+
expect(result).toEqual('12:34 PM')
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
it('should handle times with only hours', function () {
|
|
441
|
+
const value = '12'
|
|
442
|
+
const result = utils.parseTimeToString(value)
|
|
443
|
+
expect(result).toEqual('12:00 PM')
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
it('should handle military time', function () {
|
|
447
|
+
const value = '23:59:59'
|
|
448
|
+
const result = utils.parseTimeToString(value)
|
|
449
|
+
expect(result).toEqual('11:59 PM')
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
it('should handle invalid input', function () {
|
|
453
|
+
const value = 'abc'
|
|
454
|
+
const result = utils.parseTimeToString(value)
|
|
455
|
+
expect(result).toBe('')
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
it('should handle custom format', function () {
|
|
459
|
+
const value = '12:34:56'
|
|
460
|
+
const format = 'hh:mm:ss'
|
|
461
|
+
const result = utils.parseTimeToString(value, format)
|
|
462
|
+
expect(result).toEqual('12:34:56')
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
it('should handle HH:mm:ss format', function () {
|
|
466
|
+
const value = '14:34:56'
|
|
467
|
+
const format = 'HH:mm:ss'
|
|
468
|
+
const result = utils.parseTimeToString(value, format)
|
|
469
|
+
expect(result).toEqual('14:34:56')
|
|
470
|
+
})
|
|
471
|
+
}) // end parseTimeToString
|
|
472
|
+
|
|
473
|
+
describe('formatDateTime', function () {
|
|
474
|
+
it('should return a formatted string for a given date and format', function () {
|
|
475
|
+
const date = new Date(2022, 0, 1, 12, 34, 56)
|
|
476
|
+
const format = 'YYYY-MM-DD HH:mm:ss'
|
|
477
|
+
const result = utils.formatDateTime(date, format)
|
|
478
|
+
expect(result).toEqual('2022-01-01 12:34:56')
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
it('should handle custom format with day of week and full month', function () {
|
|
482
|
+
// Hmm, I don't support ordinal dates yet
|
|
483
|
+
const date = new Date(2022, 0, 1, 12, 34, 56)
|
|
484
|
+
const format = 'dddd, MMMM D YYYY, h:mm:ss a'
|
|
485
|
+
const result = utils.formatDateTime(date, format)
|
|
486
|
+
expect(result).toEqual('Saturday, January 1 2022, 12:34:56 pm')
|
|
487
|
+
})
|
|
488
|
+
|
|
489
|
+
// TODO: No timezone support yet
|
|
490
|
+
// it('should handle format with timezone', function() {
|
|
491
|
+
// const date = new Date(2022, 0, 1, 12, 34, 56)
|
|
492
|
+
// const format = 'YYYY-MM-DD HH:mm:ss ZZ'
|
|
493
|
+
// const result = utils.formatDateTime(date, format)
|
|
494
|
+
// expect(result).toEqual('2022-01-01 12:34:56 +00:00')
|
|
495
|
+
// })
|
|
496
|
+
|
|
497
|
+
it('should handle format with only time', function () {
|
|
498
|
+
const date = new Date(2022, 0, 1, 12, 34, 56)
|
|
499
|
+
const format = 'HH:mm:ss'
|
|
500
|
+
const result = utils.formatDateTime(date, format)
|
|
501
|
+
expect(result).toEqual('12:34:56')
|
|
502
|
+
})
|
|
503
|
+
|
|
504
|
+
it('should handle format with only date', function () {
|
|
505
|
+
const date = new Date(2022, 0, 1, 12, 34, 56)
|
|
506
|
+
const format = 'YYYY-MM-DD'
|
|
507
|
+
const result = utils.formatDateTime(date, format)
|
|
508
|
+
expect(result).toEqual('2022-01-01')
|
|
509
|
+
})
|
|
510
|
+
|
|
511
|
+
it('should handle format with only time', function () {
|
|
512
|
+
const date = new Date(2022, 0, 1, 12, 34, 56)
|
|
513
|
+
const format = 'HH:mm:ss'
|
|
514
|
+
const result = utils.formatDateTime(date, format)
|
|
515
|
+
expect(result).toEqual('12:34:56')
|
|
516
|
+
})
|
|
517
|
+
}) // end formatDateTime
|
|
518
|
+
|
|
519
|
+
describe('parseDateToString', function () {
|
|
520
|
+
it('should return a formatted string for a valid date', function () {
|
|
521
|
+
const value = '2022-01-31'
|
|
522
|
+
const result = utils.parseDateToString(value)
|
|
523
|
+
expect(result).toEqual('2022-Jan-31')
|
|
524
|
+
})
|
|
525
|
+
|
|
526
|
+
it('should handle invalid input', function () {
|
|
527
|
+
const value = 'abc'
|
|
528
|
+
const result = utils.parseDateToString(value)
|
|
529
|
+
expect(result).toBe('')
|
|
530
|
+
})
|
|
531
|
+
|
|
532
|
+
it('should handle custom format', function () {
|
|
533
|
+
const value = '2022-01-01'
|
|
534
|
+
const format = 'YYYY/MM-DD'
|
|
535
|
+
const result = utils.parseDateToString(value, format)
|
|
536
|
+
expect(result).toEqual('2022/01-01')
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
it('should handle date input', function () {
|
|
540
|
+
const value = new Date(2022, 0, 1)
|
|
541
|
+
const result = utils.parseDateToString(value)
|
|
542
|
+
expect(result).toEqual('2022-Jan-01')
|
|
543
|
+
})
|
|
544
|
+
})
|
|
545
|
+
|
|
546
|
+
describe('isDateInRange', function () {
|
|
547
|
+
const today = new Date()
|
|
548
|
+
const tomorrow = new Date(today)
|
|
549
|
+
tomorrow.setDate(today.getDate() + 1)
|
|
550
|
+
const yesterday = new Date(today)
|
|
551
|
+
yesterday.setDate(today.getDate() - 1)
|
|
552
|
+
|
|
553
|
+
it('should return true if date is in range', function () {
|
|
554
|
+
expect(utils.isDateInRange(today, 'future')).toBe(true)
|
|
555
|
+
expect(utils.isDateInRange(yesterday, 'future')).toBe(false)
|
|
556
|
+
expect(utils.isDateInRange(tomorrow, 'future')).toBe(true)
|
|
557
|
+
expect(utils.isDateInRange(today, 'past')).toBe(true)
|
|
558
|
+
expect(utils.isDateInRange(yesterday, 'past')).toBe(true)
|
|
559
|
+
expect(utils.isDateInRange(tomorrow, 'past')).toBe(false)
|
|
560
|
+
})
|
|
561
|
+
}) // end isDateInRange
|
|
562
|
+
|
|
563
|
+
describe('isEmail', function () {
|
|
564
|
+
const emailAddresses = [
|
|
565
|
+
'email@example.com',
|
|
566
|
+
'email+tag@example.com',
|
|
567
|
+
'email.dot@example.com',
|
|
568
|
+
'email@sub.example.com',
|
|
569
|
+
'"email"@example.com',
|
|
570
|
+
'"email@example.com"@example.com',
|
|
571
|
+
'correo@ejemplo.es',
|
|
572
|
+
'user@xn--ls8h.com',
|
|
573
|
+
'user@example.com',
|
|
574
|
+
'user.name@sub.example.com',
|
|
575
|
+
'user_name@example.com',
|
|
576
|
+
// '邮箱@例子.中国',
|
|
577
|
+
// 'почта@пример.рф',
|
|
578
|
+
// '電子郵件@範例.香港',
|
|
579
|
+
// '이메일@예시.한국',
|
|
580
|
+
// '電子メール@サンプル.日本',
|
|
581
|
+
'a.valid.email.that.is.very.very.long.and.should.validate.because.it.is.under.two.hundred.and.fifty.five.characters.with.lots.of.subdomains.in.it.that.is.quite.ridiculous@a.very.long.top.level.domain.that.is.also.very.long.and.has.lots.of.letters.in.it.com',
|
|
582
|
+
]
|
|
583
|
+
|
|
584
|
+
const invalidEmailAddresses = [
|
|
585
|
+
'john.doe@',
|
|
586
|
+
'john.doe@.com',
|
|
587
|
+
'john.doe@com.',
|
|
588
|
+
'john.doe@-example.com',
|
|
589
|
+
'john.doe@example-.com',
|
|
590
|
+
'john.doe@example.com-',
|
|
591
|
+
'john.doe@example.com/',
|
|
592
|
+
'john.doe@example..com',
|
|
593
|
+
'john.doe@ex@mple.com',
|
|
594
|
+
'john.doe@example.com?',
|
|
595
|
+
'user@invalid.c',
|
|
596
|
+
'an.email.that.is.a.little.too.long.and.should.not.validate.because.it.is.over.two.hundred.and.fifty.five.characters.with.lots.of.subdomains.in.it.that.is.quite.ridiculous@a.very.long.top.level.domain.that.is.also.very.long.and.has.lots.of.letters.in.it.com',
|
|
597
|
+
'userexample.com',
|
|
598
|
+
'user@',
|
|
599
|
+
'@example.com',
|
|
600
|
+
'user@example@example.com',
|
|
601
|
+
'user@invalid.c',
|
|
602
|
+
'user@example.com!',
|
|
603
|
+
'user@!example.com',
|
|
604
|
+
'',
|
|
605
|
+
]
|
|
606
|
+
|
|
607
|
+
emailAddresses.forEach((email) => {
|
|
608
|
+
it(`should return true for valid email address: ${email}`, function () {
|
|
609
|
+
expect(utils.isEmail(email)).toBe(true)
|
|
610
|
+
})
|
|
611
|
+
})
|
|
612
|
+
|
|
613
|
+
invalidEmailAddresses.forEach((email) => {
|
|
614
|
+
it(`should return false for invalid email address: ${email}`, function () {
|
|
615
|
+
expect(utils.isEmail(email)).toBe(false)
|
|
616
|
+
})
|
|
617
|
+
})
|
|
618
|
+
}) // end isEmail
|
|
619
|
+
|
|
620
|
+
describe('parseNANPTel', function () {
|
|
621
|
+
const validNumbers = [
|
|
622
|
+
'555-555-5555',
|
|
623
|
+
'(555)555-5555',
|
|
624
|
+
'555_555_5555',
|
|
625
|
+
'555 555 5555',
|
|
626
|
+
'555.555.5555',
|
|
627
|
+
'+1 555 555 5555',
|
|
628
|
+
]
|
|
629
|
+
|
|
630
|
+
const invalidNumbers = [
|
|
631
|
+
'555 555',
|
|
632
|
+
'555-555-5555x123',
|
|
633
|
+
'555-555-555',
|
|
634
|
+
'555-555-55555',
|
|
635
|
+
'555-555-5555x',
|
|
636
|
+
'555-555-5555 ext. 123',
|
|
637
|
+
]
|
|
638
|
+
|
|
639
|
+
validNumbers.forEach((telephoneNumber) => {
|
|
640
|
+
it(`should return true for valid North American phone number: ${telephoneNumber}`, function () {
|
|
641
|
+
expect(utils.parseNANPTel(telephoneNumber)).toEqual('555-555-5555')
|
|
642
|
+
})
|
|
643
|
+
})
|
|
644
|
+
|
|
645
|
+
it('removes leading one', function () {
|
|
646
|
+
const validNumbers = ['223-123-1234', '1 223-123-1234', '1223-123-1234']
|
|
647
|
+
validNumbers.forEach((value) => expect(utils.parseNANPTel(value)).toEqual('223-123-1234'))
|
|
648
|
+
})
|
|
649
|
+
|
|
650
|
+
invalidNumbers.forEach((telephoneNumber) => {
|
|
651
|
+
it(`should not modify invalid phone number string: ${telephoneNumber}`, function () {
|
|
652
|
+
expect(utils.parseNANPTel(telephoneNumber)).toBe(telephoneNumber)
|
|
653
|
+
})
|
|
654
|
+
})
|
|
655
|
+
})
|
|
656
|
+
|
|
657
|
+
describe('isNANPTel', function () {
|
|
658
|
+
const invalidNumbers = [
|
|
659
|
+
'1-780-555-1234',
|
|
660
|
+
'(780) 555-1234',
|
|
661
|
+
'555 555',
|
|
662
|
+
'555-555-5555x123',
|
|
663
|
+
'555-555-555',
|
|
664
|
+
'555-555-55555',
|
|
665
|
+
'555-555-5555x',
|
|
666
|
+
'555-555-5555 ext. 123',
|
|
667
|
+
]
|
|
668
|
+
|
|
669
|
+
it('should return true for phone numbers in xxx-xxx-xxxx format', function () {
|
|
670
|
+
const validNumbers = ['555-555-5555']
|
|
671
|
+
validNumbers.forEach((value) => expect(utils.isNANPTel(value)).toEqual(true))
|
|
672
|
+
})
|
|
673
|
+
|
|
674
|
+
it('should return false for other formats', function () {
|
|
675
|
+
invalidNumbers.forEach((value) => expect(utils.isNANPTel(value)).toEqual(false))
|
|
676
|
+
})
|
|
677
|
+
}) // end isNANPTel
|
|
678
|
+
|
|
679
|
+
describe('parseZip', function () {
|
|
680
|
+
const validZipCodes = ['12345', '12345-1234', '12345 1234', '12345_1234', '12345-']
|
|
681
|
+
const expectedCodes = ['12345', '12345-1234', '12345-1234', '12345-1234', '12345']
|
|
682
|
+
|
|
683
|
+
validZipCodes.forEach((value, index) => {
|
|
684
|
+
it(`should return sanitized US zip code for valid input: ${value}`, function () {
|
|
685
|
+
expect(utils.parseZip(value)).toEqual(expectedCodes[index])
|
|
686
|
+
})
|
|
687
|
+
})
|
|
688
|
+
}) // end parseZip
|
|
689
|
+
|
|
690
|
+
describe('isZip', function () {
|
|
691
|
+
const validZipCodes = ['12345', '12345-1234', '99501-1234']
|
|
692
|
+
const invalidZipCodes = [
|
|
693
|
+
'1234', // too short
|
|
694
|
+
'123456', // too long
|
|
695
|
+
'12345-123', // too short
|
|
696
|
+
'12345-12345', // too long
|
|
697
|
+
'12345-1234-1234', // too long
|
|
698
|
+
]
|
|
699
|
+
|
|
700
|
+
validZipCodes.forEach((value) => {
|
|
701
|
+
it(`should return true for valid US zip code: ${value}`, function () {
|
|
702
|
+
expect(utils.isZip(value)).toEqual(true)
|
|
703
|
+
})
|
|
704
|
+
})
|
|
705
|
+
|
|
706
|
+
invalidZipCodes.forEach((value) => {
|
|
707
|
+
it(`should return false for invalid US zip code: ${value}`, function () {
|
|
708
|
+
expect(utils.isZip(value)).toEqual(false)
|
|
709
|
+
})
|
|
710
|
+
})
|
|
711
|
+
}) // end isZip
|
|
712
|
+
|
|
713
|
+
describe('parsePostalCA', function () {
|
|
714
|
+
it('should return sanitized Canadian postal code for valid inputs', function () {
|
|
715
|
+
const validPostalCodes = ['A1A 1A1', 'a1a1a1', 'A1A-1A1', 'A1a1A1']
|
|
716
|
+
const expectedCodes = ['A1A 1A1', 'A1A 1A1', 'A1A 1A1', 'A1A 1A1']
|
|
717
|
+
validPostalCodes.forEach((value, index) =>
|
|
718
|
+
expect(utils.parsePostalCA(value)).toEqual(expectedCodes[index])
|
|
719
|
+
)
|
|
720
|
+
})
|
|
721
|
+
})
|
|
722
|
+
|
|
723
|
+
describe('isPostalCA', function () {
|
|
724
|
+
it('should return true for valid Canadian postal codes', function () {
|
|
725
|
+
const validPostalCodes = [
|
|
726
|
+
'A1A 1A1',
|
|
727
|
+
'B2B 2B2',
|
|
728
|
+
'C3C 3C3',
|
|
729
|
+
'T5X 1C3',
|
|
730
|
+
'V9Z 9Z9',
|
|
731
|
+
'X0X 0X0',
|
|
732
|
+
'Y1Y 1Y1',
|
|
733
|
+
'T6H 4T9',
|
|
734
|
+
]
|
|
735
|
+
validPostalCodes.forEach((value) => expect(utils.isPostalCA(value)).toEqual(true))
|
|
736
|
+
})
|
|
737
|
+
|
|
738
|
+
it('should return false for invalid Canadian postal codes', function () {
|
|
739
|
+
const invalidPostalCodes = [
|
|
740
|
+
'1A1A1A',
|
|
741
|
+
'T5X 13C',
|
|
742
|
+
'A1A1A',
|
|
743
|
+
'A1A1A1A',
|
|
744
|
+
'A1A1A1A1',
|
|
745
|
+
'a1a1a1a1',
|
|
746
|
+
'D5A 1P9',
|
|
747
|
+
'splat',
|
|
748
|
+
'A1U 1A1',
|
|
749
|
+
'Z9Z 9Z9',
|
|
750
|
+
]
|
|
751
|
+
invalidPostalCodes.forEach((value) => expect(utils.isPostalCA(value)).toEqual(false))
|
|
752
|
+
})
|
|
753
|
+
})
|
|
754
|
+
|
|
755
|
+
describe('isColor', () => {
|
|
756
|
+
const validHexColors = [
|
|
757
|
+
'#000000',
|
|
758
|
+
'#00000000',
|
|
759
|
+
'#123456',
|
|
760
|
+
'#12345678',
|
|
761
|
+
'#abcdef',
|
|
762
|
+
'#abcdef01',
|
|
763
|
+
'#ABC',
|
|
764
|
+
'#ABC0',
|
|
765
|
+
'#AbCdEf',
|
|
766
|
+
'#AbCdEf01',
|
|
767
|
+
]
|
|
768
|
+
|
|
769
|
+
const invalidHexColors = [
|
|
770
|
+
'#0000000', // 7 digits
|
|
771
|
+
'#000000000', // 9 digits
|
|
772
|
+
'#12345', // 5 digits
|
|
773
|
+
'#1234567', // 7 digits
|
|
774
|
+
'abcdef', // 6 digits, no hash
|
|
775
|
+
'#ab', // 2 digits
|
|
776
|
+
]
|
|
777
|
+
|
|
778
|
+
const validRgbColors = [
|
|
779
|
+
'rgb(0, 0, 0)',
|
|
780
|
+
'rgb(0, 0, 0, 0)',
|
|
781
|
+
'rgb(255, 255, 255)',
|
|
782
|
+
'rgb(255, 255, 255, 1)',
|
|
783
|
+
'rgb(255, 255, 255, 0.5)',
|
|
784
|
+
'rgb(255, 255, 255, 0.123456)',
|
|
785
|
+
'rgb(255, 255, 255, 0.123456789)',
|
|
786
|
+
'rgb(255, 255, 255, 100%)',
|
|
787
|
+
'rgba(255, 255, 255, 50%)',
|
|
788
|
+
'rgba(255, 255, 255, 12.3456%)',
|
|
789
|
+
'rgba(255,255,255)',
|
|
790
|
+
'rgba(255,255,255,1)',
|
|
791
|
+
'rgb(100%, 40%, 0%)', // percentages
|
|
792
|
+
'rgb(20 30 40)', // spaces
|
|
793
|
+
'rgb(20 30 40 / 0.5)', // spaces and alpha
|
|
794
|
+
]
|
|
795
|
+
|
|
796
|
+
const invalidRgbColors = [
|
|
797
|
+
'rgb(0, 0, 0, 0, 0)', // 5 values
|
|
798
|
+
'rgb(0, 0, 0,)', // no final value
|
|
799
|
+
'rgba(0, 0 0)', // only two values
|
|
800
|
+
]
|
|
801
|
+
|
|
802
|
+
const validHslColors = [
|
|
803
|
+
'hsl(0, 0%, 0%)',
|
|
804
|
+
'hsl(0, 0%, 0%, 0)',
|
|
805
|
+
'hsl(0, 0%, 0%, 0.5)',
|
|
806
|
+
'hsl(100deg, 0%, 0%, 0.123456)',
|
|
807
|
+
'hsl(100deg, 0%, 0%, 12%)',
|
|
808
|
+
'hsla(100deg, 0%, 0%, 12%)',
|
|
809
|
+
'hsl(100deg 0% 0%)', // spaces
|
|
810
|
+
'hsl(100deg 0% 0% / 12.3456%)', // spaces and alpha
|
|
811
|
+
'hsl(100deg 0% 0%/12%)', // spaces and alpha
|
|
812
|
+
'hsl(100, 0%, 0%)',
|
|
813
|
+
'hsl(360deg, 100%, 100%)',
|
|
814
|
+
'hsl(180deg, 50%, 50%)',
|
|
815
|
+
'hsla(180deg, 50%, 50%, 1)',
|
|
816
|
+
'hsla(180deg, 50%, 50%, 0.5)',
|
|
817
|
+
'hsl(180deg, 0%, 100%)',
|
|
818
|
+
'hsla(180deg, 0%, 100%, 0.75)',
|
|
819
|
+
'hsl(0, 0%, 100%)',
|
|
820
|
+
'hsla(0, 0%, 100%, 0.25)',
|
|
821
|
+
]
|
|
822
|
+
|
|
823
|
+
const invalidHslColors = [
|
|
824
|
+
'hsl(0, 0%, 0%, 0, 0)', // 5 values
|
|
825
|
+
'hsl(0, 0%, 0%,)', // no final value
|
|
826
|
+
'hsla(0, 0 0)', // only two values
|
|
827
|
+
'hsl(0, 0%,)', // no final value
|
|
828
|
+
'hsl(0, 0%)', // only two values
|
|
829
|
+
'hsl(0, 0%, 0, 0%)', // 4 values
|
|
830
|
+
'hsl(-100, 0%, 0%)', // negative hue value
|
|
831
|
+
'hsl(0, -100%, 0%)', // negative saturation value
|
|
832
|
+
'hsl(0, 0%, -100%)', // negative lightness value
|
|
833
|
+
'hsl120, 100%, 50%)',
|
|
834
|
+
'asdf',
|
|
835
|
+
// For more thorough testing, add these tests
|
|
836
|
+
// 'hsl(500, 0%, 0%)', // hue value greater than 359
|
|
837
|
+
// 'hsl(0, 200%, 0%)', // saturation value greater than 100
|
|
838
|
+
// 'hsl(0, 0%, 200%)', // lightness value greater than 100
|
|
839
|
+
]
|
|
840
|
+
|
|
841
|
+
const validColorNames = [
|
|
842
|
+
'transparent',
|
|
843
|
+
'currentColor',
|
|
844
|
+
'black',
|
|
845
|
+
'white',
|
|
846
|
+
'red',
|
|
847
|
+
'green',
|
|
848
|
+
'blue',
|
|
849
|
+
'yellow',
|
|
850
|
+
'orange',
|
|
851
|
+
'purple',
|
|
852
|
+
'brown',
|
|
853
|
+
'pink',
|
|
854
|
+
'gray',
|
|
855
|
+
'grey',
|
|
856
|
+
'lavenderblush',
|
|
857
|
+
'honeydew',
|
|
858
|
+
'seashell',
|
|
859
|
+
'azure',
|
|
860
|
+
'lavender',
|
|
861
|
+
'aliceblue',
|
|
862
|
+
'ghostwhite',
|
|
863
|
+
'mintcream',
|
|
864
|
+
'oldlace',
|
|
865
|
+
'linen',
|
|
866
|
+
'cornsilk',
|
|
867
|
+
'papayawhip',
|
|
868
|
+
'beige',
|
|
869
|
+
'bisque',
|
|
870
|
+
'blanchedalmond',
|
|
871
|
+
'wheat',
|
|
872
|
+
'navajowhite',
|
|
873
|
+
'peachpuff',
|
|
874
|
+
'moccasin',
|
|
875
|
+
'gainsboro',
|
|
876
|
+
'lightgrey',
|
|
877
|
+
'lightgray',
|
|
878
|
+
'silver',
|
|
879
|
+
'darkgray',
|
|
880
|
+
'gray',
|
|
881
|
+
'dimgray',
|
|
882
|
+
]
|
|
883
|
+
|
|
884
|
+
const invalidColorNames = ['transparant', 'blak', 'whit', 'redish', 'greenish', 'blueish']
|
|
885
|
+
|
|
886
|
+
validHexColors.forEach((color) => {
|
|
887
|
+
it(`should validate ${color} as a valid hex color`, () => {
|
|
888
|
+
expect(utils.isColor(color)).toBeTruthy()
|
|
889
|
+
})
|
|
890
|
+
})
|
|
891
|
+
|
|
892
|
+
invalidHexColors.forEach((color) => {
|
|
893
|
+
it(`should validate ${color} as an invalid hex color`, () => {
|
|
894
|
+
expect(utils.isColor(color)).toBeFalsy()
|
|
895
|
+
})
|
|
896
|
+
})
|
|
897
|
+
|
|
898
|
+
validRgbColors.forEach((color) => {
|
|
899
|
+
it(`should validate ${color} as a valid rgb color`, () => {
|
|
900
|
+
expect(utils.isColor(color)).toBeTruthy()
|
|
901
|
+
})
|
|
902
|
+
})
|
|
903
|
+
|
|
904
|
+
invalidRgbColors.forEach((color) => {
|
|
905
|
+
it(`should validate ${color} as an invalid rgb color`, () => {
|
|
906
|
+
expect(utils.isColor(color)).toBeFalsy()
|
|
907
|
+
})
|
|
908
|
+
})
|
|
909
|
+
|
|
910
|
+
validHslColors.forEach((color) => {
|
|
911
|
+
it(`should validate ${color} as a valid hsl color`, () => {
|
|
912
|
+
expect(utils.isColor(color)).toBeTruthy()
|
|
913
|
+
})
|
|
914
|
+
})
|
|
915
|
+
|
|
916
|
+
invalidHslColors.forEach((color) => {
|
|
917
|
+
it(`should validate ${color} as an invalid hsl color`, () => {
|
|
918
|
+
expect(utils.isColor(color)).toBeFalsy()
|
|
919
|
+
})
|
|
920
|
+
})
|
|
921
|
+
|
|
922
|
+
validColorNames.forEach((color) => {
|
|
923
|
+
it(`should validate ${color} as a valid color name`, () => {
|
|
924
|
+
expect(utils.isColor(color)).toBeTruthy()
|
|
925
|
+
})
|
|
926
|
+
})
|
|
927
|
+
|
|
928
|
+
invalidColorNames.forEach((color) => {
|
|
929
|
+
it(`should validate ${color} as an invalid color name`, () => {
|
|
930
|
+
expect(utils.isColor(color)).toBeFalsy()
|
|
931
|
+
})
|
|
932
|
+
})
|
|
933
|
+
}) // end isColor
|
|
934
|
+
|
|
935
|
+
describe('isColor', function () {
|
|
936
|
+
it('should return true for valid CSS colors', function () {
|
|
937
|
+
const validColors = [
|
|
938
|
+
'#f5f5f5',
|
|
939
|
+
'rgb(255, 99, 71)',
|
|
940
|
+
'hsl(120, 100%, 50%)',
|
|
941
|
+
'rgba(255, 99, 71, 0.5)',
|
|
942
|
+
'hsla(120, 100%, 50%, 0.5)',
|
|
943
|
+
'transparent',
|
|
944
|
+
'currentColor',
|
|
945
|
+
'red',
|
|
946
|
+
'green',
|
|
947
|
+
'blue',
|
|
948
|
+
]
|
|
949
|
+
validColors.forEach((value) => expect(utils.isColor(value)).toEqual(true))
|
|
950
|
+
})
|
|
951
|
+
|
|
952
|
+
it('should return false for invalid CSS colors', function () {
|
|
953
|
+
const invalidColors = [
|
|
954
|
+
'#f5f5f',
|
|
955
|
+
'asdf',
|
|
956
|
+
'browne',
|
|
957
|
+
'rgb(25599, 71)',
|
|
958
|
+
'hsl120, 100%, 50%)',
|
|
959
|
+
'(255, 99, 71, 0.5)',
|
|
960
|
+
'123456',
|
|
961
|
+
]
|
|
962
|
+
invalidColors.forEach((value) => expect(utils.isColor(value)).toEqual(false))
|
|
963
|
+
})
|
|
964
|
+
|
|
965
|
+
it('should validate a valid color name with CSS.supports', () => {
|
|
966
|
+
// Mock the CSS.supports function
|
|
967
|
+
window.CSS = window.CSS || {}
|
|
968
|
+
window.CSS.supports =
|
|
969
|
+
window.CSS.supports ||
|
|
970
|
+
function () {
|
|
971
|
+
return true
|
|
972
|
+
}
|
|
973
|
+
const supportsSpy = vi.spyOn(window.CSS, 'supports').mockImplementation(() => true)
|
|
974
|
+
|
|
975
|
+
expect(utils.isColor('red')).toBeTruthy()
|
|
976
|
+
|
|
977
|
+
expect(supportsSpy).toHaveBeenCalled()
|
|
978
|
+
})
|
|
979
|
+
}) // end isColor
|
|
980
|
+
|
|
981
|
+
describe('parseColor', function () {
|
|
982
|
+
it('should return hex values for valid inputs', function () {
|
|
983
|
+
const validColors = [
|
|
984
|
+
'#f5f5f5',
|
|
985
|
+
'rgb(255, 99, 71)',
|
|
986
|
+
'hsl(120, 100%, 50%)',
|
|
987
|
+
'red',
|
|
988
|
+
'green',
|
|
989
|
+
'blue',
|
|
990
|
+
]
|
|
991
|
+
const expectedColors = ['#f5f5f5', '#ff6347', '#00ff00', '#ff0000', '#008000', '#0000ff']
|
|
992
|
+
|
|
993
|
+
// TODO: I don't think handling transparent colors works very well
|
|
994
|
+
validColors.forEach((value, index) =>
|
|
995
|
+
expect(utils.parseColor(value)).toEqual(expectedColors[index])
|
|
996
|
+
)
|
|
997
|
+
})
|
|
998
|
+
}) // end parseColor
|
|
999
|
+
|
|
1000
|
+
describe('normalizeValidationResult', () => {
|
|
1001
|
+
it('should return valid=true and messages=[] when input is true', () => {
|
|
1002
|
+
const result = utils.normalizeValidationResult(true)
|
|
1003
|
+
expect(result.valid).toBe(true)
|
|
1004
|
+
expect(result.error).toBe(false)
|
|
1005
|
+
expect(result.messages).toEqual([])
|
|
1006
|
+
})
|
|
1007
|
+
|
|
1008
|
+
it('should return valid=false and messages=[input message] when input is an object with message', () => {
|
|
1009
|
+
const input = { valid: false, message: 'error' }
|
|
1010
|
+
const result = utils.normalizeValidationResult(input)
|
|
1011
|
+
expect(result.valid).toBe(false)
|
|
1012
|
+
expect(result.error).toBe(false)
|
|
1013
|
+
expect(result.messages).toEqual(['error'])
|
|
1014
|
+
})
|
|
1015
|
+
|
|
1016
|
+
it('should return valid=false and messages=[input messages] when input is an object with messages', () => {
|
|
1017
|
+
const input = { valid: false, messages: ['error 1', 'error 2'] }
|
|
1018
|
+
const result = utils.normalizeValidationResult(input)
|
|
1019
|
+
expect(result.valid).toBe(false)
|
|
1020
|
+
expect(result.error).toBe(false)
|
|
1021
|
+
expect(result.messages).toEqual(['error 1', 'error 2'])
|
|
1022
|
+
})
|
|
1023
|
+
|
|
1024
|
+
it('should return valid=false and messages=[] when input is an object with messages as a string', () => {
|
|
1025
|
+
const input = { valid: false, messages: 'error' }
|
|
1026
|
+
const result = utils.normalizeValidationResult(input)
|
|
1027
|
+
expect(result.valid).toBe(false)
|
|
1028
|
+
expect(result.error).toBe(false)
|
|
1029
|
+
expect(result.messages).toEqual(['error'])
|
|
1030
|
+
})
|
|
1031
|
+
|
|
1032
|
+
it('should return valid=false and error=true when input is an object with error=true', () => {
|
|
1033
|
+
const input = { valid: false, error: true }
|
|
1034
|
+
const result = utils.normalizeValidationResult(input)
|
|
1035
|
+
expect(result.valid).toBe(false)
|
|
1036
|
+
expect(result.error).toBe(true)
|
|
1037
|
+
expect(result.messages).toEqual([])
|
|
1038
|
+
})
|
|
1039
|
+
|
|
1040
|
+
it('should return valid=false and messages with the string input is a string', () => {
|
|
1041
|
+
const input = 'error'
|
|
1042
|
+
const result = utils.normalizeValidationResult(input)
|
|
1043
|
+
expect(result.valid).toBe(false)
|
|
1044
|
+
expect(result.error).toBe(false)
|
|
1045
|
+
expect(result.messages).toEqual(['error'])
|
|
1046
|
+
})
|
|
1047
|
+
}) // end normalizeValidationResult
|
|
1048
|
+
})
|