@ls-stack/utils 3.24.0 → 3.24.1
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/docs/testUtils.md +1 -1
- package/lib/testUtils.cjs +57 -19
- package/lib/testUtils.d.cts +53 -2
- package/lib/testUtils.d.ts +53 -2
- package/lib/testUtils.js +57 -19
- package/package.json +1 -1
package/docs/testUtils.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
function compactSnapshot(value, __namedParameters): string;
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
Defined in: [packages/utils/src/testUtils.ts:
|
|
17
|
+
Defined in: [packages/utils/src/testUtils.ts:487](https://github.com/lucasols/utils/blob/main/packages/utils/src/testUtils.ts#L487)
|
|
18
18
|
|
|
19
19
|
#### Parameters
|
|
20
20
|
|
package/lib/testUtils.cjs
CHANGED
|
@@ -753,14 +753,20 @@ function waitController() {
|
|
|
753
753
|
}
|
|
754
754
|
};
|
|
755
755
|
}
|
|
756
|
-
function matchesKeyPattern(
|
|
757
|
-
if (
|
|
756
|
+
function matchesKeyPattern(fullPath, key, pattern, currentPath) {
|
|
757
|
+
if (fullPath === pattern) {
|
|
758
758
|
return true;
|
|
759
759
|
}
|
|
760
|
-
if (pattern.
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
|
|
760
|
+
if (pattern.startsWith("*.")) {
|
|
761
|
+
const propName = pattern.slice(2);
|
|
762
|
+
return currentPath !== "" && key === propName;
|
|
763
|
+
}
|
|
764
|
+
if (pattern.startsWith("*") && !pattern.startsWith("*.")) {
|
|
765
|
+
const propName = pattern.slice(1);
|
|
766
|
+
return key === propName;
|
|
767
|
+
}
|
|
768
|
+
if (!pattern.includes("*")) {
|
|
769
|
+
return currentPath === "" && key === pattern;
|
|
764
770
|
}
|
|
765
771
|
return false;
|
|
766
772
|
}
|
|
@@ -792,12 +798,19 @@ function applyKeyFiltering(value, { rejectKeys, filterKeys }, currentPath = "",
|
|
|
792
798
|
}
|
|
793
799
|
if (Array.isArray(value)) {
|
|
794
800
|
if (visited.has(value)) {
|
|
795
|
-
throw new Error(
|
|
801
|
+
throw new Error(
|
|
802
|
+
"Circular reference detected in array during key filtering"
|
|
803
|
+
);
|
|
796
804
|
}
|
|
797
805
|
visited.add(value);
|
|
798
806
|
try {
|
|
799
807
|
return value.map(
|
|
800
|
-
(item, index) => applyKeyFiltering(
|
|
808
|
+
(item, index) => applyKeyFiltering(
|
|
809
|
+
item,
|
|
810
|
+
{ rejectKeys, filterKeys },
|
|
811
|
+
currentPath ? `${currentPath}[${index}]` : `[${index}]`,
|
|
812
|
+
visited
|
|
813
|
+
)
|
|
801
814
|
);
|
|
802
815
|
} finally {
|
|
803
816
|
visited.delete(value);
|
|
@@ -805,7 +818,9 @@ function applyKeyFiltering(value, { rejectKeys, filterKeys }, currentPath = "",
|
|
|
805
818
|
}
|
|
806
819
|
if (isPlainObject(value)) {
|
|
807
820
|
if (visited.has(value)) {
|
|
808
|
-
throw new Error(
|
|
821
|
+
throw new Error(
|
|
822
|
+
"Circular reference detected in object during key filtering"
|
|
823
|
+
);
|
|
809
824
|
}
|
|
810
825
|
visited.add(value);
|
|
811
826
|
try {
|
|
@@ -813,23 +828,43 @@ function applyKeyFiltering(value, { rejectKeys, filterKeys }, currentPath = "",
|
|
|
813
828
|
for (const [key, itemValue] of Object.entries(value)) {
|
|
814
829
|
const fullPath = currentPath ? `${currentPath}.${key}` : key;
|
|
815
830
|
if (rejectKeys?.some(
|
|
816
|
-
(rejectPath) => matchesKeyPattern(fullPath,
|
|
831
|
+
(rejectPath) => matchesKeyPattern(fullPath, key, rejectPath, currentPath)
|
|
817
832
|
)) {
|
|
818
833
|
continue;
|
|
819
834
|
}
|
|
820
835
|
if (filterKeys) {
|
|
821
|
-
const
|
|
822
|
-
(filterPath) => (
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
)
|
|
836
|
+
const exactMatch = filterKeys.some(
|
|
837
|
+
(filterPath) => matchesKeyPattern(fullPath, key, filterPath, currentPath)
|
|
838
|
+
);
|
|
839
|
+
const isParent = filterKeys.some(
|
|
840
|
+
(filterPath) => isParentOfPattern(fullPath, filterPath)
|
|
827
841
|
);
|
|
828
|
-
if (!
|
|
842
|
+
if (!exactMatch && !isParent) {
|
|
829
843
|
continue;
|
|
830
844
|
}
|
|
845
|
+
if (exactMatch) {
|
|
846
|
+
result[key] = applyKeyFiltering(
|
|
847
|
+
itemValue,
|
|
848
|
+
{ rejectKeys },
|
|
849
|
+
fullPath,
|
|
850
|
+
visited
|
|
851
|
+
);
|
|
852
|
+
} else {
|
|
853
|
+
result[key] = applyKeyFiltering(
|
|
854
|
+
itemValue,
|
|
855
|
+
{ rejectKeys, filterKeys },
|
|
856
|
+
fullPath,
|
|
857
|
+
visited
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
} else {
|
|
861
|
+
result[key] = applyKeyFiltering(
|
|
862
|
+
itemValue,
|
|
863
|
+
{ rejectKeys, filterKeys },
|
|
864
|
+
fullPath,
|
|
865
|
+
visited
|
|
866
|
+
);
|
|
831
867
|
}
|
|
832
|
-
result[key] = applyKeyFiltering(itemValue, { rejectKeys, filterKeys }, fullPath, visited);
|
|
833
868
|
}
|
|
834
869
|
return result;
|
|
835
870
|
} finally {
|
|
@@ -849,7 +884,10 @@ function compactSnapshot(value, {
|
|
|
849
884
|
} = {}) {
|
|
850
885
|
let processedValue = value;
|
|
851
886
|
if (rejectKeys || filterKeys) {
|
|
852
|
-
processedValue = applyKeyFiltering(processedValue, {
|
|
887
|
+
processedValue = applyKeyFiltering(processedValue, {
|
|
888
|
+
rejectKeys: Array.isArray(rejectKeys) ? rejectKeys : rejectKeys ? [rejectKeys] : void 0,
|
|
889
|
+
filterKeys: Array.isArray(filterKeys) ? filterKeys : filterKeys ? [filterKeys] : void 0
|
|
890
|
+
});
|
|
853
891
|
}
|
|
854
892
|
processedValue = showBooleansAs ? replaceBooleansWithEmoji(processedValue, showBooleansAs) : processedValue;
|
|
855
893
|
return `
|
package/lib/testUtils.d.cts
CHANGED
|
@@ -51,8 +51,59 @@ declare function compactSnapshot(value: unknown, { collapseObjects, maxLineLengt
|
|
|
51
51
|
trueText?: string;
|
|
52
52
|
falseText?: string;
|
|
53
53
|
};
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Reject (exclude) keys from the snapshot using pattern matching.
|
|
56
|
+
*
|
|
57
|
+
* **Pattern Syntax:**
|
|
58
|
+
* - `'prop'` - Only root-level properties named 'prop'
|
|
59
|
+
* - `'prop.nested'` - Exact nested property paths like `obj.prop.nested`
|
|
60
|
+
* - `'*prop'` - Any property named exactly 'prop' at any level (root or nested)
|
|
61
|
+
* - `'*.prop'` - Any nested property named 'prop' (excludes root-level matches)
|
|
62
|
+
*
|
|
63
|
+
* **Examples:**
|
|
64
|
+
* ```typescript
|
|
65
|
+
* // Reject root-level 'secret' only
|
|
66
|
+
* rejectKeys: ['secret']
|
|
67
|
+
*
|
|
68
|
+
* // Reject nested 'password' properties only
|
|
69
|
+
* rejectKeys: ['*.password']
|
|
70
|
+
*
|
|
71
|
+
* // Reject any 'apiKey' property at any level
|
|
72
|
+
* rejectKeys: ['*apiKey']
|
|
73
|
+
*
|
|
74
|
+
* // Reject specific nested path
|
|
75
|
+
* rejectKeys: ['user.settings.theme']
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
rejectKeys?: string[] | string;
|
|
79
|
+
/**
|
|
80
|
+
* Filter (include only) keys that match the specified patterns.
|
|
81
|
+
*
|
|
82
|
+
* **Pattern Syntax:** (same as rejectKeys)
|
|
83
|
+
* - `'prop'` - Only root-level properties named 'prop'
|
|
84
|
+
* - `'prop.nested'` - Exact nested property paths like `obj.prop.nested`
|
|
85
|
+
* - `'*prop'` - Any property named exactly 'prop' at any level (root or nested)
|
|
86
|
+
* - `'*.prop'` - Any nested property named 'prop' (excludes root-level matches)
|
|
87
|
+
*
|
|
88
|
+
* **Examples:**
|
|
89
|
+
* ```typescript
|
|
90
|
+
* // Include only root-level 'user'
|
|
91
|
+
* filterKeys: ['user']
|
|
92
|
+
*
|
|
93
|
+
* // Include all 'name' properties at any level
|
|
94
|
+
* filterKeys: ['*name']
|
|
95
|
+
*
|
|
96
|
+
* // Include only nested 'id' properties
|
|
97
|
+
* filterKeys: ['*.id']
|
|
98
|
+
*
|
|
99
|
+
* // Include specific nested paths
|
|
100
|
+
* filterKeys: ['user.profile.email', 'settings.theme']
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* **Note:** When filtering, parent paths are automatically included if needed
|
|
104
|
+
* to preserve the structure for nested matches.
|
|
105
|
+
*/
|
|
106
|
+
filterKeys?: string[] | string;
|
|
56
107
|
}): string;
|
|
57
108
|
|
|
58
109
|
export { compactSnapshot, createLoggerStore, getResultFn, waitController };
|
package/lib/testUtils.d.ts
CHANGED
|
@@ -51,8 +51,59 @@ declare function compactSnapshot(value: unknown, { collapseObjects, maxLineLengt
|
|
|
51
51
|
trueText?: string;
|
|
52
52
|
falseText?: string;
|
|
53
53
|
};
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Reject (exclude) keys from the snapshot using pattern matching.
|
|
56
|
+
*
|
|
57
|
+
* **Pattern Syntax:**
|
|
58
|
+
* - `'prop'` - Only root-level properties named 'prop'
|
|
59
|
+
* - `'prop.nested'` - Exact nested property paths like `obj.prop.nested`
|
|
60
|
+
* - `'*prop'` - Any property named exactly 'prop' at any level (root or nested)
|
|
61
|
+
* - `'*.prop'` - Any nested property named 'prop' (excludes root-level matches)
|
|
62
|
+
*
|
|
63
|
+
* **Examples:**
|
|
64
|
+
* ```typescript
|
|
65
|
+
* // Reject root-level 'secret' only
|
|
66
|
+
* rejectKeys: ['secret']
|
|
67
|
+
*
|
|
68
|
+
* // Reject nested 'password' properties only
|
|
69
|
+
* rejectKeys: ['*.password']
|
|
70
|
+
*
|
|
71
|
+
* // Reject any 'apiKey' property at any level
|
|
72
|
+
* rejectKeys: ['*apiKey']
|
|
73
|
+
*
|
|
74
|
+
* // Reject specific nested path
|
|
75
|
+
* rejectKeys: ['user.settings.theme']
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
rejectKeys?: string[] | string;
|
|
79
|
+
/**
|
|
80
|
+
* Filter (include only) keys that match the specified patterns.
|
|
81
|
+
*
|
|
82
|
+
* **Pattern Syntax:** (same as rejectKeys)
|
|
83
|
+
* - `'prop'` - Only root-level properties named 'prop'
|
|
84
|
+
* - `'prop.nested'` - Exact nested property paths like `obj.prop.nested`
|
|
85
|
+
* - `'*prop'` - Any property named exactly 'prop' at any level (root or nested)
|
|
86
|
+
* - `'*.prop'` - Any nested property named 'prop' (excludes root-level matches)
|
|
87
|
+
*
|
|
88
|
+
* **Examples:**
|
|
89
|
+
* ```typescript
|
|
90
|
+
* // Include only root-level 'user'
|
|
91
|
+
* filterKeys: ['user']
|
|
92
|
+
*
|
|
93
|
+
* // Include all 'name' properties at any level
|
|
94
|
+
* filterKeys: ['*name']
|
|
95
|
+
*
|
|
96
|
+
* // Include only nested 'id' properties
|
|
97
|
+
* filterKeys: ['*.id']
|
|
98
|
+
*
|
|
99
|
+
* // Include specific nested paths
|
|
100
|
+
* filterKeys: ['user.profile.email', 'settings.theme']
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* **Note:** When filtering, parent paths are automatically included if needed
|
|
104
|
+
* to preserve the structure for nested matches.
|
|
105
|
+
*/
|
|
106
|
+
filterKeys?: string[] | string;
|
|
56
107
|
}): string;
|
|
57
108
|
|
|
58
109
|
export { compactSnapshot, createLoggerStore, getResultFn, waitController };
|
package/lib/testUtils.js
CHANGED
|
@@ -253,14 +253,20 @@ function waitController() {
|
|
|
253
253
|
}
|
|
254
254
|
};
|
|
255
255
|
}
|
|
256
|
-
function matchesKeyPattern(
|
|
257
|
-
if (
|
|
256
|
+
function matchesKeyPattern(fullPath, key, pattern, currentPath) {
|
|
257
|
+
if (fullPath === pattern) {
|
|
258
258
|
return true;
|
|
259
259
|
}
|
|
260
|
-
if (pattern.
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
260
|
+
if (pattern.startsWith("*.")) {
|
|
261
|
+
const propName = pattern.slice(2);
|
|
262
|
+
return currentPath !== "" && key === propName;
|
|
263
|
+
}
|
|
264
|
+
if (pattern.startsWith("*") && !pattern.startsWith("*.")) {
|
|
265
|
+
const propName = pattern.slice(1);
|
|
266
|
+
return key === propName;
|
|
267
|
+
}
|
|
268
|
+
if (!pattern.includes("*")) {
|
|
269
|
+
return currentPath === "" && key === pattern;
|
|
264
270
|
}
|
|
265
271
|
return false;
|
|
266
272
|
}
|
|
@@ -292,12 +298,19 @@ function applyKeyFiltering(value, { rejectKeys, filterKeys }, currentPath = "",
|
|
|
292
298
|
}
|
|
293
299
|
if (Array.isArray(value)) {
|
|
294
300
|
if (visited.has(value)) {
|
|
295
|
-
throw new Error(
|
|
301
|
+
throw new Error(
|
|
302
|
+
"Circular reference detected in array during key filtering"
|
|
303
|
+
);
|
|
296
304
|
}
|
|
297
305
|
visited.add(value);
|
|
298
306
|
try {
|
|
299
307
|
return value.map(
|
|
300
|
-
(item, index) => applyKeyFiltering(
|
|
308
|
+
(item, index) => applyKeyFiltering(
|
|
309
|
+
item,
|
|
310
|
+
{ rejectKeys, filterKeys },
|
|
311
|
+
currentPath ? `${currentPath}[${index}]` : `[${index}]`,
|
|
312
|
+
visited
|
|
313
|
+
)
|
|
301
314
|
);
|
|
302
315
|
} finally {
|
|
303
316
|
visited.delete(value);
|
|
@@ -305,7 +318,9 @@ function applyKeyFiltering(value, { rejectKeys, filterKeys }, currentPath = "",
|
|
|
305
318
|
}
|
|
306
319
|
if (isPlainObject(value)) {
|
|
307
320
|
if (visited.has(value)) {
|
|
308
|
-
throw new Error(
|
|
321
|
+
throw new Error(
|
|
322
|
+
"Circular reference detected in object during key filtering"
|
|
323
|
+
);
|
|
309
324
|
}
|
|
310
325
|
visited.add(value);
|
|
311
326
|
try {
|
|
@@ -313,23 +328,43 @@ function applyKeyFiltering(value, { rejectKeys, filterKeys }, currentPath = "",
|
|
|
313
328
|
for (const [key, itemValue] of Object.entries(value)) {
|
|
314
329
|
const fullPath = currentPath ? `${currentPath}.${key}` : key;
|
|
315
330
|
if (rejectKeys?.some(
|
|
316
|
-
(rejectPath) => matchesKeyPattern(fullPath,
|
|
331
|
+
(rejectPath) => matchesKeyPattern(fullPath, key, rejectPath, currentPath)
|
|
317
332
|
)) {
|
|
318
333
|
continue;
|
|
319
334
|
}
|
|
320
335
|
if (filterKeys) {
|
|
321
|
-
const
|
|
322
|
-
(filterPath) => (
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
)
|
|
336
|
+
const exactMatch = filterKeys.some(
|
|
337
|
+
(filterPath) => matchesKeyPattern(fullPath, key, filterPath, currentPath)
|
|
338
|
+
);
|
|
339
|
+
const isParent = filterKeys.some(
|
|
340
|
+
(filterPath) => isParentOfPattern(fullPath, filterPath)
|
|
327
341
|
);
|
|
328
|
-
if (!
|
|
342
|
+
if (!exactMatch && !isParent) {
|
|
329
343
|
continue;
|
|
330
344
|
}
|
|
345
|
+
if (exactMatch) {
|
|
346
|
+
result[key] = applyKeyFiltering(
|
|
347
|
+
itemValue,
|
|
348
|
+
{ rejectKeys },
|
|
349
|
+
fullPath,
|
|
350
|
+
visited
|
|
351
|
+
);
|
|
352
|
+
} else {
|
|
353
|
+
result[key] = applyKeyFiltering(
|
|
354
|
+
itemValue,
|
|
355
|
+
{ rejectKeys, filterKeys },
|
|
356
|
+
fullPath,
|
|
357
|
+
visited
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
result[key] = applyKeyFiltering(
|
|
362
|
+
itemValue,
|
|
363
|
+
{ rejectKeys, filterKeys },
|
|
364
|
+
fullPath,
|
|
365
|
+
visited
|
|
366
|
+
);
|
|
331
367
|
}
|
|
332
|
-
result[key] = applyKeyFiltering(itemValue, { rejectKeys, filterKeys }, fullPath, visited);
|
|
333
368
|
}
|
|
334
369
|
return result;
|
|
335
370
|
} finally {
|
|
@@ -349,7 +384,10 @@ function compactSnapshot(value, {
|
|
|
349
384
|
} = {}) {
|
|
350
385
|
let processedValue = value;
|
|
351
386
|
if (rejectKeys || filterKeys) {
|
|
352
|
-
processedValue = applyKeyFiltering(processedValue, {
|
|
387
|
+
processedValue = applyKeyFiltering(processedValue, {
|
|
388
|
+
rejectKeys: Array.isArray(rejectKeys) ? rejectKeys : rejectKeys ? [rejectKeys] : void 0,
|
|
389
|
+
filterKeys: Array.isArray(filterKeys) ? filterKeys : filterKeys ? [filterKeys] : void 0
|
|
390
|
+
});
|
|
353
391
|
}
|
|
354
392
|
processedValue = showBooleansAs ? replaceBooleansWithEmoji(processedValue, showBooleansAs) : processedValue;
|
|
355
393
|
return `
|