itee-validators 5.5.0 → 5.6.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.
Files changed (36) hide show
  1. package/.github/workflows/node.js.yml +1 -1
  2. package/.tasks/_utils.mjs +66 -0
  3. package/.tasks/builds/build.mjs +65 -0
  4. package/.tasks/cleans/clean.mjs +20 -0
  5. package/.tasks/docs/doc.mjs +37 -0
  6. package/.tasks/helps/help.mjs +195 -0
  7. package/.tasks/lints/lint.mjs +33 -0
  8. package/.tasks/patches/patch.mjs +9 -0
  9. package/.tasks/tests/benchmarks/bundle-benchmarks.mjs +33 -0
  10. package/.tasks/tests/benchmarks/compute-benchmarks.mjs +215 -0
  11. package/.tasks/tests/benchmarks/run-benchmarks-for-backend.mjs +24 -0
  12. package/.tasks/tests/benchmarks/run-benchmarks-for-frontend.mjs +38 -0
  13. package/.tasks/tests/bundling/check-bundling-from-esm-build-import.mjs +167 -0
  14. package/.tasks/tests/bundling/check-bundling-from-esm-files-direct.mjs +129 -0
  15. package/.tasks/tests/bundling/check-bundling-from-esm-files-import.mjs +149 -0
  16. package/.tasks/tests/unit-tests/bundle-unit-tests.mjs +33 -0
  17. package/.tasks/tests/unit-tests/compute-unit-tests.mjs +578 -0
  18. package/.tasks/tests/unit-tests/run-unit-tests-for-backend.mjs +25 -0
  19. package/.tasks/tests/unit-tests/run-unit-tests-for-frontend.mjs +41 -0
  20. package/CHANGELOG.md +10 -0
  21. package/builds/itee-validators.cjs.js +18 -10
  22. package/builds/itee-validators.cjs.js.map +1 -1
  23. package/builds/itee-validators.cjs.min.js +62 -62
  24. package/builds/itee-validators.esm.js +2 -2
  25. package/builds/itee-validators.iife.js +4 -3
  26. package/builds/itee-validators.iife.js.map +1 -1
  27. package/builds/itee-validators.iife.min.js +17 -17
  28. package/package.json +1 -1
  29. package/sources/file-system/block-devices/isBlockDevicePath.js +2 -1
  30. package/sources/file-system/character-devices/isCharacterDevicePath.js +2 -1
  31. package/sources/file-system/directories/isDirectoryPath.js +2 -1
  32. package/sources/file-system/fifo-pipes/isFIFOPath.js +2 -1
  33. package/sources/file-system/files/isEmptyFile.js +2 -1
  34. package/sources/file-system/files/isFilePath.js +2 -1
  35. package/sources/file-system/sockets/isSocketPath.js +2 -1
  36. package/sources/file-system/symbolic-links/isSymbolicLinkPath.js +2 -1
@@ -0,0 +1,578 @@
1
+ import {
2
+ join,
3
+ normalize,
4
+ basename,
5
+ dirname,
6
+ extname,
7
+ relative
8
+ } from 'path'
9
+ import {
10
+ getDirname,
11
+ packageInfos
12
+ } from '../../_utils.mjs'
13
+ import glob from 'glob'
14
+ import fs from 'fs'
15
+ import log from 'fancy-log'
16
+ import colors from 'ansi-colors'
17
+ import { getGulpConfigForTask } from '../../../configs/gulp.conf.mjs'
18
+ import childProcess from 'child_process'
19
+
20
+ const {
21
+ red,
22
+ green,
23
+ blue,
24
+ cyan,
25
+ yellow,
26
+ magenta
27
+ } = colors
28
+
29
+
30
+ function computeUnitTests( done ) {
31
+
32
+ const baseDir = getDirname()
33
+ const sourcesDir = join( baseDir, 'sources' )
34
+ const testsDir = join( baseDir, 'tests' )
35
+ const unitsDir = join( testsDir, 'units' )
36
+
37
+ fs.mkdirSync( unitsDir, { recursive: true } )
38
+
39
+ const filePathsToIgnore = getGulpConfigForTask( 'compute-unit-tests' )
40
+
41
+ const sourcesFiles = glob.sync( join( sourcesDir, '**' ) )
42
+ .map( filePath => normalize( filePath ) )
43
+ .filter( filePath => {
44
+ const fileName = basename( filePath )
45
+ const isJsFile = fileName.endsWith( '.js' )
46
+ const isNotPrivateFile = !fileName.startsWith( '_' )
47
+ const isNotIgnoredFile = !filePathsToIgnore.includes( fileName )
48
+ return isJsFile && isNotPrivateFile && isNotIgnoredFile
49
+ } )
50
+
51
+ const unitsImportMap = []
52
+ for ( let sourceFile of sourcesFiles ) {
53
+
54
+ const specificFilePath = sourceFile.replace( sourcesDir, '' )
55
+ const specificDir = dirname( specificFilePath )
56
+
57
+ const fileName = basename( sourceFile, extname( sourceFile ) )
58
+ const unitFileName = `${ fileName }.unit.js`
59
+ const unitDirPath = join( unitsDir, specificDir )
60
+ const unitFilePath = join( unitDirPath, unitFileName )
61
+
62
+ const nsName = `${ fileName }Namespace`
63
+ const unitName = `${ fileName }Units`
64
+ const importDirPath = relative( unitDirPath, sourcesDir )
65
+ const importFilePath = join( importDirPath, specificFilePath ).replace( /\\/g, '/' )
66
+
67
+ try {
68
+
69
+ const jsdocPath = join( baseDir, '/node_modules/jsdoc/jsdoc.js' )
70
+ const jsdocOutput = childProcess.execFileSync( 'node', [ jsdocPath, '-X', sourceFile ] )
71
+ .toString()
72
+
73
+ const classNames = []
74
+ const usedLongnames = []
75
+ const jsonData = JSON.parse( jsdocOutput ).filter( data => {
76
+
77
+ const longName = data.longname
78
+
79
+ const kind = data.kind
80
+ if ( kind !== 'function' ) {
81
+ if ( kind === 'class' && !classNames.includes( longName ) ) {
82
+ classNames.push( longName )
83
+ }
84
+ return false
85
+ }
86
+
87
+ const undocumented = data.undocumented
88
+ if ( undocumented ) {
89
+ return false
90
+ }
91
+
92
+ const scope = data.scope
93
+ if ( ![ 'global', 'static' ].includes( scope ) ) {
94
+ return false
95
+ }
96
+
97
+ if ( longName.includes( ' ' ) || longName.includes( '~' ) || usedLongnames.includes( longName ) ) {
98
+ return false
99
+ }
100
+
101
+ for ( let className of classNames ) {
102
+ if ( longName.includes( className ) ) {
103
+ return false
104
+ }
105
+ }
106
+
107
+ usedLongnames.push( longName )
108
+
109
+ return true
110
+
111
+ } )
112
+
113
+ if ( jsonData.length === 0 ) {
114
+ log( yellow( `No usable exports found in [${ sourceFile }]. Ignore it !` ) )
115
+ continue
116
+ }
117
+
118
+ let describes = ''
119
+ const I = n => '\t'.repeat( n )
120
+ I._ = I( 1 )
121
+ I.__ = I( 2 )
122
+ I.___ = I( 3 )
123
+ I.____ = I( 4 )
124
+ I._____ = I( 5 )
125
+
126
+ for ( let docData of jsonData ) {
127
+
128
+ try {
129
+
130
+ //check input parameters and types
131
+ const docParameters = docData.params || []
132
+ const parameters = []
133
+ for ( let pIndex = 0 ; pIndex < docParameters.length ; pIndex++ ) {
134
+ const param = docParameters[ pIndex ]
135
+ let paramName = param.name
136
+ if ( !paramName ) {
137
+ paramName = `param${ pIndex }`
138
+ // eslint-disable-next-line no-console
139
+ console.warn( `Missing parameter name for [${ docData.longname }]. Defaulting to [${ paramName }]` )
140
+ }
141
+
142
+ const paramType = param.type
143
+ if ( !paramType ) {
144
+ throw new ReferenceError( `Missing parameter type. Unable to create unit test for [${ docData.longname }] !` )
145
+ }
146
+
147
+ const parameter = {
148
+ name: paramName,
149
+ types: []
150
+ }
151
+
152
+ const paramTypeNames = paramType.names
153
+ for ( let type of paramTypeNames ) {
154
+ parameter.types.push( type )
155
+ }
156
+
157
+ parameters.push( parameter )
158
+ }
159
+
160
+ // Check returns types
161
+ const docReturns = docData.returns || []
162
+ const returns = []
163
+ for ( let docReturn of docReturns ) {
164
+ const returnType = docReturn.type
165
+ if ( !returnType ) {
166
+ throw new ReferenceError( `Missing return type for [${ docData.longname }]. Ignore current target !` )
167
+ }
168
+ returns.push( ...returnType.names )
169
+ }
170
+
171
+ // Todo check throws
172
+
173
+ // Get user define rules
174
+ // const rules = []
175
+
176
+
177
+ // Infer basic rules
178
+ let its = ''
179
+
180
+ if ( parameters.length === 0 ) {
181
+
182
+ if ( returns.length === 0 ) {
183
+
184
+ const result = `${ I( 1 + 1 + 1 + 1 ) }const result = ${ nsName }.${ docData.name }()` + '\n'
185
+ const expect = `${ I( 1 + 1 + 1 + 1 ) }expect(result).to.be.a('undefined')` + '\n'
186
+
187
+ its += '' +
188
+ `${ I( 1 + 1 + 1 ) }it( 'return type is undefined', () => {` + '\n' +
189
+ '\n' +
190
+ `${ result }` +
191
+ `${ expect }` +
192
+ '\n' +
193
+ `${ I( 1 + 1 + 1 ) }} )` + '\n'
194
+
195
+ } else if ( returns.length === 1 ) {
196
+
197
+ const firstReturnType = returns[ 0 ]
198
+ const lowerName = firstReturnType.toLowerCase()
199
+
200
+ const result = `${ I( 1 + 1 + 1 + 1 ) }const result = ${ nsName }.${ docData.name }()` + '\n'
201
+
202
+ let expect = ''
203
+ if ( lowerName.startsWith( 'array' ) ) {
204
+ //todo array of...
205
+ expect += `${ I( 1 + 1 + 1 + 1 ) }expect(result).to.be.a('array')` + '\n'
206
+ } else {
207
+ expect += `${ I( 1 + 1 + 1 + 1 ) }expect(result).to.be.a('${ lowerName }')` + '\n'
208
+ }
209
+
210
+ its += '' +
211
+ `${ I( 1 + 1 + 1 ) }it( 'return type is ${ lowerName }', () => {` + '\n' +
212
+ '\n' +
213
+ `${ result }` +
214
+ `${ expect }` +
215
+ '\n' +
216
+ `${ I( 1 + 1 + 1 ) }} )` + '\n'
217
+
218
+ } else {
219
+
220
+ const result = `${ I( 1 + 1 + 1 + 1 ) }const result = ${ nsName }.${ docData.name }()` + '\n'
221
+
222
+ let returnTypesLabel = []
223
+ let expects = []
224
+ for ( let returnType of returns ) {
225
+
226
+ const lowerName = returnType.toLowerCase()
227
+ returnTypesLabel.push( lowerName )
228
+
229
+ if ( lowerName.startsWith( 'array' ) ) {
230
+ expects.push( `expect(result).to.be.a('array')` )
231
+ //todo array of...
232
+ } else {
233
+ expects.push( `expect(result).to.be.a('${ lowerName }')` )
234
+ }
235
+
236
+ }
237
+
238
+ let indent = 1 + 1 + 1 + 1
239
+ let openTry = ''
240
+ let closeTry = ''
241
+ for ( let expect of expects ) {
242
+ openTry += '' +
243
+ `${ I( indent ) }try {` + '\n' +
244
+ `${ I( indent + 1 ) }${ expect }` + '\n' +
245
+ `${ I( indent ) }} catch(e) {` + '\n'
246
+
247
+ closeTry = `${ I( indent ) }}` + '\n' + `${ closeTry }`
248
+
249
+ indent++
250
+ }
251
+ const _expect = '' +
252
+ `${ openTry }` +
253
+ `${ I( indent ) }expect.fail("expect result to be of type ${ returnTypesLabel.join( ' or ' ) }")` + '\n' +
254
+ `${ closeTry }`
255
+
256
+ its += '' +
257
+ `${ I( 1 + 1 + 1 ) }it( 'return type is ${ returnTypesLabel.join( ' or ' ) }', () => {` + '\n' +
258
+ '\n' +
259
+ `${ result }` +
260
+ `${ _expect }` +
261
+ '\n' +
262
+ `${ I( 1 + 1 + 1 ) }} )` + '\n'
263
+
264
+ }
265
+
266
+ } else {
267
+
268
+ if ( returns.length === 0 ) {
269
+
270
+ let itDeclaration = []
271
+ let index = 0
272
+ let indent = 1 + 1 + 1 + 1
273
+ let localIndent = indent
274
+ let dataSets = ''
275
+ let forLoopOpens = ''
276
+ let forLoopCloses = ''
277
+ let args = []
278
+ for ( let parameter of parameters ) {
279
+
280
+ const parameterType = parameter.types[ 0 ]
281
+ itDeclaration.push( `${ parameter.name } is of type ${ parameterType }` )
282
+
283
+ dataSets += `${ I( indent ) }const dataSet${ index } = this._dataMap[ '${ parameterType }s' ]` + '\n'
284
+ forLoopOpens += '' + '\n' +
285
+ `${ I( localIndent ) }for ( let key${ index } in dataSet${ index } ) {` + '\n' +
286
+ `${ I( localIndent + 1 ) }const dataSetValue${ index } = dataSet${ index }[ key${ index } ]` + '\n'
287
+
288
+ args.push( `dataSetValue${ index }` )
289
+
290
+ forLoopCloses = `${ I( localIndent ) }}` + '\n' + `${ forLoopCloses }`
291
+
292
+ index++
293
+ localIndent++
294
+ }
295
+
296
+ const result = `${ I( localIndent ) }const result = ${ nsName }.${ docData.name }( ${ args.join( ', ' ) } )` + '\n'
297
+ const expect = `${ I( localIndent ) }expect(result).to.be.a('undefined')` + '\n'
298
+
299
+ const param = '' +
300
+ `${ dataSets }` +
301
+ `${ forLoopOpens }` +
302
+ `${ result }` +
303
+ `${ expect }` +
304
+ `${ forLoopCloses }`
305
+
306
+ its += '' +
307
+ `${ I( 1 + 1 + 1 ) }it( 'return type is undefined when ${ itDeclaration.join( ' and ' ) }', () => {` + '\n' +
308
+ '\n' +
309
+ `${ param }` +
310
+ '\n' +
311
+ `${ I( 1 + 1 + 1 ) }} )` + '\n'
312
+
313
+ } else if ( returns.length === 1 ) {
314
+
315
+ const firstReturnType = returns[ 0 ]
316
+ const lowerName = firstReturnType.toLowerCase()
317
+
318
+ let itDeclaration = []
319
+ let index = 0
320
+ let indent = 1 + 1 + 1 + 1
321
+ let localIndent = indent
322
+ let dataSets = ''
323
+ let forLoopOpens = ''
324
+ let forLoopCloses = ''
325
+ let args = []
326
+ for ( let parameter of parameters ) {
327
+
328
+ const parameterType = parameter.types[ 0 ]
329
+ const isAnyType = ( parameterType === '*' || parameterType.toLowerCase() === 'any' )
330
+ const declaration = ( isAnyType )
331
+ ? `${ parameter.name } is of any type`
332
+ : `${ parameter.name } is of type ${ parameterType }`
333
+ itDeclaration.push( declaration )
334
+
335
+ if ( isAnyType ) {
336
+
337
+ dataSets += `${ I( indent ) }const dataMap${ index } = this._dataMap` + '\n' +
338
+ `${ I( localIndent ) }for ( let dataSetKey${ index } in dataMap${ index } ) {` + '\n'
339
+
340
+ localIndent++
341
+ dataSets += `${ I( indent + 1 ) }const dataSet${ index } = dataMap${ index }[ dataSetKey${ index } ]` + '\n'
342
+ forLoopOpens += '' + '\n' +
343
+ `${ I( localIndent ) }for ( let key${ index } in dataSet${ index } ) {` + '\n' +
344
+ `${ I( localIndent + 1 ) }const dataSetValue${ index } = dataSet${ index }[ key${ index } ]` + '\n'
345
+
346
+ args.push( `dataSetValue${ index }` )
347
+
348
+ forLoopCloses = `${ I( localIndent ) }}` + '\n' +
349
+ `${ I( localIndent - 1 ) }}` + '\n' +
350
+ `${ forLoopCloses }`
351
+
352
+ } else {
353
+
354
+ dataSets += `${ I( indent ) }const dataSet${ index } = this._dataMap[ '${ parameterType }s' ]` + '\n'
355
+ forLoopOpens += '' + '\n' +
356
+ `${ I( localIndent ) }for ( let key${ index } in dataSet${ index } ) {` + '\n' +
357
+ `${ I( localIndent + 1 ) }const dataSetValue${ index } = dataSet${ index }[ key${ index } ]` + '\n'
358
+
359
+ args.push( `dataSetValue${ index }` )
360
+
361
+ forLoopCloses = `${ I( localIndent ) }}` + '\n' + `${ forLoopCloses }`
362
+
363
+ }
364
+
365
+
366
+ index++
367
+ localIndent++
368
+ }
369
+
370
+ const result = `${ I( localIndent ) }const result = ${ nsName }.${ docData.name }( ${ args.join( ', ' ) } )` + '\n'
371
+
372
+ let expect = ''
373
+ if ( lowerName.startsWith( 'array' ) ) {
374
+ expect = `${ I( localIndent ) }expect(result).to.be.a('array')` + '\n'
375
+ //todo array of...
376
+ } else {
377
+ expect = `${ I( localIndent ) }expect(result).to.be.a('${ lowerName }')` + '\n'
378
+ }
379
+
380
+ const param = '' +
381
+ `${ dataSets }` +
382
+ `${ forLoopOpens }` +
383
+ `${ result }` +
384
+ `${ expect }` +
385
+ `${ forLoopCloses }`
386
+
387
+ its += '' +
388
+ `${ I( 1 + 1 + 1 ) }it( 'return type is ${ lowerName } when ${ itDeclaration.join( ' and ' ) }', () => {` + '\n' +
389
+ '\n' +
390
+ `${ param }` +
391
+ '\n' +
392
+ `${ I( 1 + 1 + 1 ) }} )` + '\n'
393
+
394
+ } else {
395
+
396
+ let itDeclaration = []
397
+ let index = 0
398
+ let indent = 1 + 1 + 1 + 1
399
+ let localIndent = indent
400
+ let dataSets = ''
401
+ let forLoopOpens = ''
402
+ let forLoopCloses = ''
403
+ let args = []
404
+ for ( let parameter of parameters ) {
405
+
406
+ const parameterType = parameter.types[ 0 ]
407
+ itDeclaration.push( `${ parameter.name } is of type ${ parameterType }` )
408
+
409
+ dataSets += `${ I( indent ) }const dataSet${ index } = this._dataMap[ '${ parameterType }s' ]` + '\n'
410
+ forLoopOpens += '' + '\n' +
411
+ `${ I( localIndent ) }for ( let key${ index } in dataSet${ index } ) {` + '\n' +
412
+ `${ I( localIndent + 1 ) }const dataSetValue${ index } = dataSet${ index }[ key${ index } ]` + '\n'
413
+
414
+ args.push( `dataSetValue${ index }` )
415
+
416
+ forLoopCloses = `${ I( localIndent ) }}` + '\n' + `${ forLoopCloses }`
417
+
418
+ index++
419
+ localIndent++
420
+ }
421
+
422
+ const result = `${ I( localIndent ) }const result = ${ nsName }.${ docData.name }( ${ args.join( ', ' ) } )` + '\n'
423
+
424
+ let returnTypesLabel = []
425
+ let expects = []
426
+ for ( let returnType of returns ) {
427
+
428
+ const lowerName = returnType.toLowerCase()
429
+ returnTypesLabel.push( lowerName )
430
+
431
+ if ( lowerName.startsWith( 'array' ) ) {
432
+ expects.push( `expect(result).to.be.a('array')` )
433
+ //todo array of...
434
+ } else {
435
+ expects.push( `expect(result).to.be.a('${ lowerName }')` )
436
+ }
437
+
438
+ }
439
+ let openTry = ''
440
+ let closeTry = ''
441
+ for ( let expect of expects ) {
442
+ openTry += '' +
443
+ `${ I( localIndent ) }try {` + '\n' +
444
+ `${ I( localIndent + 1 ) }${ expect }` + '\n' +
445
+ `${ I( localIndent ) }} catch(e) {` + '\n'
446
+
447
+ closeTry = `${ I( localIndent ) }}` + '\n' + `${ closeTry }`
448
+
449
+ localIndent++
450
+ }
451
+ const _expect = '' +
452
+ `${ openTry }` +
453
+ `${ I( localIndent ) }expect.fail("expect result to be of type ${ returnTypesLabel.join( ' or ' ) }")` + '\n' +
454
+ `${ closeTry }`
455
+
456
+ const param = '' +
457
+ `${ dataSets }` +
458
+ `${ forLoopOpens }` +
459
+ `${ result }` +
460
+ `${ _expect }` +
461
+ `${ forLoopCloses }`
462
+
463
+ its += '' +
464
+ `${ I( 1 + 1 + 1 ) }it( 'return type is ${ returnTypesLabel.join( ' or ' ) } when ${ itDeclaration.join( ' and ' ) }', () => {` + '\n' +
465
+ '\n' +
466
+ `${ param }` +
467
+ '\n' +
468
+ `${ I( 1 + 1 + 1 ) }} )` + '\n'
469
+
470
+ }
471
+
472
+ }
473
+
474
+ describes += '' +
475
+ `${ I.__ }describe( '${ docData.name }()', () => {` + '\n' +
476
+ '\n' +
477
+ `${ I.___ }it( 'is bundlable', () => {` + '\n' +
478
+ '\n' +
479
+ `${ I.____ }expect(${ nsName }.${ docData.name }).to.exist` + '\n' +
480
+ '\n' +
481
+ `${ I.___ }} )` + '\n' +
482
+ '\n' +
483
+ `${ its }` +
484
+ '\n' +
485
+ `${ I.__ }} )` + '\n' +
486
+ '\n'
487
+
488
+ } catch ( error ) {
489
+
490
+ log( red( error.message ) )
491
+
492
+ }
493
+
494
+ }
495
+
496
+ const template = '' +
497
+ `import { expect } from 'chai'` + '\n' +
498
+ `import { beforeEach, afterEach, describe, it } from 'mocha'` + '\n' +
499
+ `import { Testing } from 'itee-utils'` + '\n' +
500
+ `import * as ${ nsName } from '${ importFilePath }'` + '\n' +
501
+ '\n' +
502
+ `function ${ unitName } () {` + '\n' +
503
+ '\n' +
504
+ `${ I( 1 ) }beforeEach( () => {` + '\n' +
505
+ '\n' +
506
+ `${ I( 1 + 1 ) }this._dataMap = Testing.createDataMap()` + '\n' +
507
+ '\n' +
508
+ `${ I( 1 ) }} )` + '\n' +
509
+ '\n' +
510
+ `${ I( 1 ) }afterEach( () => {` + '\n' +
511
+ '\n' +
512
+ `${ I( 1 + 1 ) }delete this._dataMap` + '\n' +
513
+ '\n' +
514
+ `${ I( 1 ) }} )` + '\n' +
515
+ '\n' +
516
+ `${ I( 1 ) }describe( '${ unitName }', () => {` + '\n' +
517
+ '\n' +
518
+ `${ describes }` +
519
+ '' +
520
+ `${ I( 1 ) }} )` + '\n' +
521
+ '\n' +
522
+ '}' + '\n' +
523
+ '\n' +
524
+ `export { ${ unitName } }` + '\n' +
525
+ '\n'
526
+
527
+ const importUnitFilePath = relative( unitsDir, unitFilePath )
528
+ unitsImportMap.push( {
529
+ exportName: unitName,
530
+ path: importUnitFilePath.replace( /\\/g, '/' )
531
+ } )
532
+
533
+ log( green( `Create ${ unitFilePath }` ) )
534
+ fs.mkdirSync( unitDirPath, { recursive: true } )
535
+ fs.writeFileSync( unitFilePath, template )
536
+
537
+ } catch ( error ) {
538
+
539
+ log( red( error.message ) )
540
+
541
+ }
542
+
543
+ }
544
+
545
+ // Global units file
546
+ let computedImports = ''
547
+ let computedUnitCalls = ''
548
+ for ( let entry of unitsImportMap ) {
549
+ computedImports += `import { ${ entry.exportName } } from './${ entry.path }'` + '\n'
550
+ computedUnitCalls += ` ${ entry.exportName }.call( root )` + '\n'
551
+ }
552
+
553
+ const unitsTemplate = '' +
554
+ 'import { describe } from \'mocha\'' + '\n' +
555
+ `${ computedImports }` +
556
+ '\n' +
557
+ 'const root = typeof window === \'undefined\'' + '\n' +
558
+ ' ? typeof global === \'undefined\'' + '\n' +
559
+ ' ? Function( \'return this\' )() ' + '\n' +
560
+ ' : global ' + '\n' +
561
+ ' : window' + '\n' +
562
+ '\n' +
563
+ 'describe( \'Itee#Validators\', () => {' + '\n' +
564
+ '\n' +
565
+ `${ computedUnitCalls }` +
566
+ '\n' +
567
+ '} )' + '\n'
568
+
569
+ const unitsFilePath = join( unitsDir, `${ packageInfos.name }.units.js` )
570
+
571
+ log( green( `Create ${ unitsFilePath }` ) )
572
+ fs.writeFileSync( unitsFilePath, unitsTemplate )
573
+
574
+ done()
575
+
576
+ }
577
+
578
+ export { computeUnitTests }
@@ -0,0 +1,25 @@
1
+ import { join } from 'path'
2
+ import { spawn } from 'child_process'
3
+ import {
4
+ getDirname,
5
+ packageInfos
6
+ } from '../../_utils.mjs'
7
+
8
+
9
+ function runUnitTestsForBackend( done ) {
10
+
11
+ const projectDir = getDirname()
12
+ const mochaPath = join( projectDir, 'node_modules/mocha/bin/mocha' )
13
+ const testsPath = join( projectDir, `tests/units/builds/${ packageInfos.name }.units.cjs.js` )
14
+ const mocha = spawn( 'node', [ mochaPath, testsPath ], { stdio: 'inherit' } )
15
+ mocha.on( 'close', ( code ) => {
16
+
17
+ ( code === 0 )
18
+ ? done()
19
+ : done( `mocha exited with code ${ code }` )
20
+
21
+ } )
22
+
23
+ }
24
+
25
+ export { runUnitTestsForBackend }
@@ -0,0 +1,41 @@
1
+ import { normalize } from 'path'
2
+ import karma from 'karma'
3
+ import log from 'fancy-log'
4
+ import colors from 'ansi-colors'
5
+ import {
6
+ getDirname,
7
+ packageInfos
8
+ } from '../../_utils.mjs'
9
+
10
+ const {
11
+ red,
12
+ green,
13
+ blue,
14
+ cyan,
15
+ yellow,
16
+ magenta
17
+ } = colors
18
+
19
+
20
+ async function runUnitTestsForFrontend( done ) {
21
+
22
+ const projectDir = getDirname()
23
+ const configFile = normalize( `${ projectDir }/configs/karma.units.conf.js` )
24
+ const karmaConfig = karma.config.parseConfig( configFile )
25
+ const karmaServer = new karma.Server( karmaConfig, ( exitCode ) => {
26
+ if ( exitCode === 0 ) {
27
+ log( `Karma server exit with code ${ exitCode }` )
28
+ done()
29
+ } else {
30
+ done( `Karma server exit with code ${ exitCode }` )
31
+ }
32
+ } )
33
+ karmaServer.on( 'browser_error', ( browser, error ) => {
34
+ log( red( error.message ) )
35
+ } )
36
+
37
+ await karmaServer.start()
38
+
39
+ }
40
+
41
+ export { runUnitTestsForFrontend }
package/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ # [v5.6.0](https://github.com/Itee/itee-validators/compare/v5.5.1...v5.6.0) (2025-10-18)
2
+
3
+ ## ✨ New Features
4
+ - [`187970c`](https://github.com/Itee/itee-validators/commit/187970c) (gulpfile) split gulpfile tasks into sub-tasks files
5
+
6
+ ## 🐛 Bug Fixes
7
+ - [`1ae6d25`](https://github.com/Itee/itee-validators/commit/1ae6d25) (sources) avoid validator with thrown during migration
8
+
9
+ # [v5.5.1](https://github.com/Itee/itee-validators/compare/v5.5.0...v5.5.1) (2025-10-16)
10
+
1
11
  # [v5.5.0](https://github.com/Itee/itee-validators/compare/v5.4.0...v5.5.0) (2025-10-16)
2
12
 
3
13
  ## ✨ New Features