@unrift/core 0.0.0 → 0.2.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/dist/esm/cli.js +75 -5
- package/dist/esm/expect.d.ts +8 -0
- package/dist/esm/expect.d.ts.map +1 -1
- package/dist/esm/expect.js +54 -4
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/interface.d.ts +4 -1
- package/dist/esm/interface.d.ts.map +1 -1
- package/dist/esm/interface.js +4 -1
- package/dist/esm/matchers.d.ts +1 -0
- package/dist/esm/matchers.d.ts.map +1 -1
- package/dist/esm/matchers.js +61 -0
- package/dist/esm/run.d.ts +1 -1
- package/dist/esm/run.d.ts.map +1 -1
- package/dist/esm/run.js +3 -4
- package/dist/esm/runner.d.ts.map +1 -1
- package/dist/esm/runner.js +201 -33
- package/dist/esm/suite.d.ts +2 -10
- package/dist/esm/suite.d.ts.map +1 -1
- package/dist/esm/suite.js +29 -10
- package/dist/esm/test.d.ts +1 -7
- package/dist/esm/test.d.ts.map +1 -1
- package/dist/esm/test.js +11 -2
- package/dist/esm/types.d.ts +3 -3
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/types.js +1 -0
- package/dist/esm/utils/colors.d.ts +21 -0
- package/dist/esm/utils/colors.d.ts.map +1 -1
- package/dist/esm/utils/colors.js +34 -4
- package/dist/esm/utils/compareEngine.d.ts +10 -0
- package/dist/esm/utils/compareEngine.d.ts.map +1 -0
- package/dist/esm/utils/compareEngine.js +222 -0
- package/dist/esm/utils/deepEqual.d.ts.map +1 -1
- package/dist/esm/utils/deepEqual.js +7 -185
- package/dist/esm/utils/diff.d.ts +2 -0
- package/dist/esm/utils/diff.d.ts.map +1 -0
- package/dist/esm/utils/diff.js +84 -0
- package/dist/esm/utils/discoverTestFiles.d.ts.map +1 -1
- package/dist/esm/utils/discoverTestFiles.js +17 -2
- package/dist/esm/utils/looseEqual.d.ts +9 -0
- package/dist/esm/utils/looseEqual.d.ts.map +1 -1
- package/dist/esm/utils/looseEqual.js +16 -197
- package/dist/esm/utils/normalizePath.d.ts +2 -0
- package/dist/esm/utils/normalizePath.d.ts.map +1 -0
- package/dist/esm/utils/normalizePath.js +4 -0
- package/dist/esm/utils/toError.d.ts +2 -0
- package/dist/esm/utils/toError.d.ts.map +1 -0
- package/dist/esm/utils/toError.js +3 -0
- package/dist/esm/utils/toThrow.d.ts.map +1 -1
- package/dist/esm/utils/toThrow.js +12 -1
- package/dist/esm/utils/transform.d.ts.map +1 -1
- package/dist/esm/utils/transform.js +31 -21
- package/package.json +21 -3
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { isObjectLike, isPrimitive, isTypedArray, sameValue, } from "./helpers.js";
|
|
2
|
+
export function compareInternal(left, right, options, leftToRightSeen, rightToLeftSeen) {
|
|
3
|
+
const worklist = [[left, right]];
|
|
4
|
+
while (worklist.length) {
|
|
5
|
+
const [currentLeft, currentRight] = worklist.pop();
|
|
6
|
+
if (sameValue(currentLeft, currentRight))
|
|
7
|
+
continue;
|
|
8
|
+
if (typeof currentLeft !== typeof currentRight)
|
|
9
|
+
return false;
|
|
10
|
+
if (isPrimitive(currentLeft) || isPrimitive(currentRight))
|
|
11
|
+
return false;
|
|
12
|
+
if (!isObjectLike(currentLeft) || !isObjectLike(currentRight))
|
|
13
|
+
return false;
|
|
14
|
+
const leftObj = currentLeft;
|
|
15
|
+
const rightObj = currentRight;
|
|
16
|
+
// Cycle detection
|
|
17
|
+
const mappedRight = leftToRightSeen.get(leftObj);
|
|
18
|
+
if (mappedRight !== undefined) {
|
|
19
|
+
if (mappedRight !== rightObj)
|
|
20
|
+
return false;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (options.bijectiveCycleCheck && rightToLeftSeen) {
|
|
24
|
+
const mappedLeft = rightToLeftSeen.get(rightObj);
|
|
25
|
+
if (mappedLeft !== undefined) {
|
|
26
|
+
if (mappedLeft !== leftObj)
|
|
27
|
+
return false;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
rightToLeftSeen.set(rightObj, leftObj);
|
|
31
|
+
}
|
|
32
|
+
leftToRightSeen.set(leftObj, rightObj);
|
|
33
|
+
// Arrays
|
|
34
|
+
const leftIsArray = Array.isArray(currentLeft);
|
|
35
|
+
const rightIsArray = Array.isArray(currentRight);
|
|
36
|
+
if (leftIsArray || rightIsArray) {
|
|
37
|
+
if (!leftIsArray || !rightIsArray)
|
|
38
|
+
return false;
|
|
39
|
+
const leftArr = currentLeft;
|
|
40
|
+
const rightArr = currentRight;
|
|
41
|
+
if (leftArr.length !== rightArr.length)
|
|
42
|
+
return false;
|
|
43
|
+
for (let index = 0; index < leftArr.length; index++) {
|
|
44
|
+
if (options.treatSparseAsUndefined) {
|
|
45
|
+
const leftValue = Object.hasOwn(leftArr, index)
|
|
46
|
+
? leftArr[index]
|
|
47
|
+
: undefined;
|
|
48
|
+
const rightValue = Object.hasOwn(rightArr, index)
|
|
49
|
+
? rightArr[index]
|
|
50
|
+
: undefined;
|
|
51
|
+
worklist.push([leftValue, rightValue]);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
worklist.push([leftArr[index], rightArr[index]]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
// Typed arrays
|
|
60
|
+
const leftIsTyped = isTypedArray(currentLeft);
|
|
61
|
+
const rightIsTyped = isTypedArray(currentRight);
|
|
62
|
+
if (leftIsTyped || rightIsTyped) {
|
|
63
|
+
if (!leftIsTyped || !rightIsTyped)
|
|
64
|
+
return false;
|
|
65
|
+
const leftTA = currentLeft;
|
|
66
|
+
const rightTA = currentRight;
|
|
67
|
+
if (leftTA.constructor !== rightTA.constructor)
|
|
68
|
+
return false;
|
|
69
|
+
if (leftTA.length !== rightTA.length)
|
|
70
|
+
return false;
|
|
71
|
+
for (let index = 0; index < leftTA.length; index++) {
|
|
72
|
+
if (!sameValue(leftTA[index], rightTA[index]))
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
// ArrayBuffer
|
|
78
|
+
const leftIsBuffer = currentLeft instanceof ArrayBuffer;
|
|
79
|
+
const rightIsBuffer = currentRight instanceof ArrayBuffer;
|
|
80
|
+
if (leftIsBuffer || rightIsBuffer) {
|
|
81
|
+
if (!leftIsBuffer || !rightIsBuffer)
|
|
82
|
+
return false;
|
|
83
|
+
const leftBuf = currentLeft;
|
|
84
|
+
const rightBuf = currentRight;
|
|
85
|
+
if (leftBuf.byteLength !== rightBuf.byteLength)
|
|
86
|
+
return false;
|
|
87
|
+
const leftBytes = new Uint8Array(leftBuf);
|
|
88
|
+
const rightBytes = new Uint8Array(rightBuf);
|
|
89
|
+
for (let index = 0; index < leftBytes.length; index++) {
|
|
90
|
+
if (leftBytes[index] !== rightBytes[index])
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
// DataView
|
|
96
|
+
const leftIsView = currentLeft instanceof DataView;
|
|
97
|
+
const rightIsView = currentRight instanceof DataView;
|
|
98
|
+
if (leftIsView || rightIsView) {
|
|
99
|
+
if (!leftIsView || !rightIsView)
|
|
100
|
+
return false;
|
|
101
|
+
const leftView = currentLeft;
|
|
102
|
+
const rightView = currentRight;
|
|
103
|
+
if (leftView.byteLength !== rightView.byteLength)
|
|
104
|
+
return false;
|
|
105
|
+
for (let offset = 0; offset < leftView.byteLength; offset++) {
|
|
106
|
+
if (leftView.getUint8(offset) !== rightView.getUint8(offset))
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
// Date
|
|
112
|
+
const leftIsDate = currentLeft instanceof Date;
|
|
113
|
+
const rightIsDate = currentRight instanceof Date;
|
|
114
|
+
if (leftIsDate || rightIsDate) {
|
|
115
|
+
if (!leftIsDate || !rightIsDate)
|
|
116
|
+
return false;
|
|
117
|
+
if (currentLeft.getTime() !== currentRight.getTime())
|
|
118
|
+
return false;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
// RegExp
|
|
122
|
+
const leftIsRegExp = currentLeft instanceof RegExp;
|
|
123
|
+
const rightIsRegExp = currentRight instanceof RegExp;
|
|
124
|
+
if (leftIsRegExp || rightIsRegExp) {
|
|
125
|
+
if (!leftIsRegExp || !rightIsRegExp)
|
|
126
|
+
return false;
|
|
127
|
+
const leftRe = currentLeft;
|
|
128
|
+
const rightRe = currentRight;
|
|
129
|
+
if (leftRe.source !== rightRe.source)
|
|
130
|
+
return false;
|
|
131
|
+
if (leftRe.flags !== rightRe.flags)
|
|
132
|
+
return false;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
// Set (order-insensitive)
|
|
136
|
+
const leftIsSet = currentLeft instanceof Set;
|
|
137
|
+
const rightIsSet = currentRight instanceof Set;
|
|
138
|
+
if (leftIsSet || rightIsSet) {
|
|
139
|
+
if (!leftIsSet || !rightIsSet)
|
|
140
|
+
return false;
|
|
141
|
+
const leftSet = currentLeft;
|
|
142
|
+
const rightSet = currentRight;
|
|
143
|
+
if (leftSet.size !== rightSet.size)
|
|
144
|
+
return false;
|
|
145
|
+
const remaining = new Set(rightSet);
|
|
146
|
+
outer: for (const leftValue of leftSet) {
|
|
147
|
+
for (const rightValue of remaining) {
|
|
148
|
+
if (compareInternal(leftValue, rightValue, options, leftToRightSeen, rightToLeftSeen)) {
|
|
149
|
+
remaining.delete(rightValue);
|
|
150
|
+
continue outer;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
// Map (order-insensitive)
|
|
158
|
+
const leftIsMap = currentLeft instanceof Map;
|
|
159
|
+
const rightIsMap = currentRight instanceof Map;
|
|
160
|
+
if (leftIsMap || rightIsMap) {
|
|
161
|
+
if (!leftIsMap || !rightIsMap)
|
|
162
|
+
return false;
|
|
163
|
+
const leftMap = currentLeft;
|
|
164
|
+
const rightMap = currentRight;
|
|
165
|
+
if (leftMap.size !== rightMap.size)
|
|
166
|
+
return false;
|
|
167
|
+
const remainingEntries = new Set(rightMap.entries());
|
|
168
|
+
outer: for (const [leftKey, leftValue] of leftMap.entries()) {
|
|
169
|
+
for (const candidate of remainingEntries) {
|
|
170
|
+
const [rightKey, rightValue] = candidate;
|
|
171
|
+
if (compareInternal(leftKey, rightKey, options, leftToRightSeen, rightToLeftSeen) &&
|
|
172
|
+
compareInternal(leftValue, rightValue, options, leftToRightSeen, rightToLeftSeen)) {
|
|
173
|
+
remainingEntries.delete(candidate);
|
|
174
|
+
continue outer;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
// Fallback: plain objects / custom class instances
|
|
182
|
+
if (options.checkPrototype) {
|
|
183
|
+
// Prototype-sensitive (deepEqual)
|
|
184
|
+
const leftProto = Object.getPrototypeOf(currentLeft);
|
|
185
|
+
const rightProto = Object.getPrototypeOf(currentRight);
|
|
186
|
+
if (leftProto !== rightProto)
|
|
187
|
+
return false;
|
|
188
|
+
const leftKeys = Object.keys(currentLeft);
|
|
189
|
+
const rightKeys = Object.keys(currentRight);
|
|
190
|
+
if (leftKeys.length !== rightKeys.length)
|
|
191
|
+
return false;
|
|
192
|
+
const leftRecord = currentLeft;
|
|
193
|
+
const rightRecord = currentRight;
|
|
194
|
+
for (let i = 0; i < leftKeys.length; i++) {
|
|
195
|
+
const key = leftKeys[i];
|
|
196
|
+
if (!Object.hasOwn(rightRecord, key))
|
|
197
|
+
return false;
|
|
198
|
+
worklist.push([leftRecord[key], rightRecord[key]]);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// Prototype-insensitive (looseEqual) — union-of-keys, missing ≈ undefined
|
|
203
|
+
const leftKeys = Object.keys(currentLeft);
|
|
204
|
+
const rightKeys = Object.keys(currentRight);
|
|
205
|
+
const allKeys = new Set([...leftKeys, ...rightKeys]);
|
|
206
|
+
const leftRecord = currentLeft;
|
|
207
|
+
const rightRecord = currentRight;
|
|
208
|
+
for (const key of allKeys) {
|
|
209
|
+
const leftHasKey = Object.hasOwn(leftRecord, key);
|
|
210
|
+
const rightHasKey = Object.hasOwn(rightRecord, key);
|
|
211
|
+
const leftValue = leftHasKey ? leftRecord[key] : undefined;
|
|
212
|
+
const rightValue = rightHasKey ? rightRecord[key] : undefined;
|
|
213
|
+
if (leftHasKey !== rightHasKey) {
|
|
214
|
+
if (leftValue !== undefined || rightValue !== undefined)
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
worklist.push([leftValue, rightValue]);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deepEqual.d.ts","sourceRoot":"","sources":["../../../src/utils/deepEqual.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"deepEqual.d.ts","sourceRoot":"","sources":["../../../src/utils/deepEqual.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,CAkBhD"}
|
|
@@ -1,187 +1,5 @@
|
|
|
1
|
-
import { isObjectLike, isPrimitive,
|
|
2
|
-
|
|
3
|
-
const worklist = [[left, right]];
|
|
4
|
-
while (worklist.length) {
|
|
5
|
-
const [currentLeft, currentRight] = worklist.pop();
|
|
6
|
-
if (sameValue(currentLeft, currentRight))
|
|
7
|
-
continue;
|
|
8
|
-
// Types must match at this point
|
|
9
|
-
if (typeof currentLeft !== typeof currentRight)
|
|
10
|
-
return false;
|
|
11
|
-
// Primitive mismatch - we already handled equality above
|
|
12
|
-
if (isPrimitive(currentLeft) || isPrimitive(currentRight))
|
|
13
|
-
return false;
|
|
14
|
-
// Functions, etc. – only ever equal by reference, which we've already checked
|
|
15
|
-
if (!isObjectLike(currentLeft) || !isObjectLike(currentRight))
|
|
16
|
-
return false;
|
|
17
|
-
const leftObj = currentLeft;
|
|
18
|
-
const rightObj = currentRight;
|
|
19
|
-
// Cycle detection: require consistent mapping for left-side objects
|
|
20
|
-
const previouslyMappedRight = leftToRightSeen.get(leftObj);
|
|
21
|
-
if (previouslyMappedRight !== undefined) {
|
|
22
|
-
if (previouslyMappedRight !== rightObj)
|
|
23
|
-
return false;
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
leftToRightSeen.set(leftObj, rightObj);
|
|
27
|
-
// Arrays - prototype-sensitive handled by prototype check later
|
|
28
|
-
const leftIsArray = Array.isArray(currentLeft);
|
|
29
|
-
const rightIsArray = Array.isArray(currentRight);
|
|
30
|
-
if (leftIsArray || rightIsArray) {
|
|
31
|
-
if (!leftIsArray || !rightIsArray)
|
|
32
|
-
return false;
|
|
33
|
-
const leftArr = currentLeft;
|
|
34
|
-
const rightArr = currentRight;
|
|
35
|
-
if (leftArr.length !== rightArr.length)
|
|
36
|
-
return false;
|
|
37
|
-
// Preserve sparseness by reading indexed values directly.
|
|
38
|
-
for (let index = 0; index < leftArr.length; index++) {
|
|
39
|
-
worklist.push([leftArr[index], rightArr[index]]);
|
|
40
|
-
}
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
// Typed arrays
|
|
44
|
-
const leftIsTyped = isTypedArray(currentLeft);
|
|
45
|
-
const rightIsTyped = isTypedArray(currentRight);
|
|
46
|
-
if (leftIsTyped || rightIsTyped) {
|
|
47
|
-
if (!leftIsTyped || !rightIsTyped)
|
|
48
|
-
return false;
|
|
49
|
-
const leftTA = currentLeft;
|
|
50
|
-
const rightTA = currentRight;
|
|
51
|
-
if (leftTA.constructor !== rightTA.constructor)
|
|
52
|
-
return false;
|
|
53
|
-
if (leftTA.length !== rightTA.length)
|
|
54
|
-
return false;
|
|
55
|
-
for (let index = 0; index < leftTA.length; index++) {
|
|
56
|
-
if (!sameValue(leftTA[index], rightTA[index]))
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
// ArrayBuffer
|
|
62
|
-
const leftIsBuffer = currentLeft instanceof ArrayBuffer;
|
|
63
|
-
const rightIsBuffer = currentRight instanceof ArrayBuffer;
|
|
64
|
-
if (leftIsBuffer || rightIsBuffer) {
|
|
65
|
-
if (!leftIsBuffer || !rightIsBuffer)
|
|
66
|
-
return false;
|
|
67
|
-
const leftBuf = currentLeft;
|
|
68
|
-
const rightBuf = currentRight;
|
|
69
|
-
if (leftBuf.byteLength !== rightBuf.byteLength)
|
|
70
|
-
return false;
|
|
71
|
-
const leftBytes = new Uint8Array(leftBuf);
|
|
72
|
-
const rightBytes = new Uint8Array(rightBuf);
|
|
73
|
-
for (let index = 0; index < leftBytes.length; index++) {
|
|
74
|
-
if (leftBytes[index] !== rightBytes[index])
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
// DataView
|
|
80
|
-
const leftIsView = currentLeft instanceof DataView;
|
|
81
|
-
const rightIsView = currentRight instanceof DataView;
|
|
82
|
-
if (leftIsView || rightIsView) {
|
|
83
|
-
if (!leftIsView || !rightIsView)
|
|
84
|
-
return false;
|
|
85
|
-
const leftView = currentLeft;
|
|
86
|
-
const rightView = currentRight;
|
|
87
|
-
if (leftView.byteLength !== rightView.byteLength)
|
|
88
|
-
return false;
|
|
89
|
-
for (let offset = 0; offset < leftView.byteLength; offset++) {
|
|
90
|
-
if (leftView.getUint8(offset) !== rightView.getUint8(offset))
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
// Date
|
|
96
|
-
const leftIsDate = currentLeft instanceof Date;
|
|
97
|
-
const rightIsDate = currentRight instanceof Date;
|
|
98
|
-
if (leftIsDate || rightIsDate) {
|
|
99
|
-
if (!leftIsDate || !rightIsDate)
|
|
100
|
-
return false;
|
|
101
|
-
if (currentLeft.getTime() !== currentRight.getTime())
|
|
102
|
-
return false;
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
// RegExp
|
|
106
|
-
const leftIsRegExp = currentLeft instanceof RegExp;
|
|
107
|
-
const rightIsRegExp = currentRight instanceof RegExp;
|
|
108
|
-
if (leftIsRegExp || rightIsRegExp) {
|
|
109
|
-
if (!leftIsRegExp || !rightIsRegExp)
|
|
110
|
-
return false;
|
|
111
|
-
const leftRe = currentLeft;
|
|
112
|
-
const rightRe = currentRight;
|
|
113
|
-
if (leftRe.source !== rightRe.source)
|
|
114
|
-
return false;
|
|
115
|
-
if (leftRe.flags !== rightRe.flags)
|
|
116
|
-
return false;
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
// Set
|
|
120
|
-
const leftIsSet = currentLeft instanceof Set;
|
|
121
|
-
const rightIsSet = currentRight instanceof Set;
|
|
122
|
-
if (leftIsSet || rightIsSet) {
|
|
123
|
-
if (!leftIsSet || !rightIsSet)
|
|
124
|
-
return false;
|
|
125
|
-
const leftSet = currentLeft;
|
|
126
|
-
const rightSet = currentRight;
|
|
127
|
-
if (leftSet.size !== rightSet.size)
|
|
128
|
-
return false;
|
|
129
|
-
const unmatchedRightValues = new Set(rightSet);
|
|
130
|
-
outer: for (const leftValue of leftSet) {
|
|
131
|
-
for (const rightValue of unmatchedRightValues) {
|
|
132
|
-
if (deepEqualInternal(leftValue, rightValue, leftToRightSeen)) {
|
|
133
|
-
unmatchedRightValues.delete(rightValue);
|
|
134
|
-
continue outer;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
// Map
|
|
142
|
-
const leftIsMap = currentLeft instanceof Map;
|
|
143
|
-
const rightIsMap = currentRight instanceof Map;
|
|
144
|
-
if (leftIsMap || rightIsMap) {
|
|
145
|
-
if (!leftIsMap || !rightIsMap)
|
|
146
|
-
return false;
|
|
147
|
-
const leftMap = currentLeft;
|
|
148
|
-
const rightMap = currentRight;
|
|
149
|
-
if (leftMap.size !== rightMap.size)
|
|
150
|
-
return false;
|
|
151
|
-
const unmatchedRightEntries = new Set(rightMap.entries());
|
|
152
|
-
outer: for (const [leftKey, leftValue] of leftMap.entries()) {
|
|
153
|
-
for (const candidate of unmatchedRightEntries) {
|
|
154
|
-
const [rightKey, rightValue] = candidate;
|
|
155
|
-
if (deepEqualInternal(leftKey, rightKey, leftToRightSeen) &&
|
|
156
|
-
deepEqualInternal(leftValue, rightValue, leftToRightSeen)) {
|
|
157
|
-
unmatchedRightEntries.delete(candidate);
|
|
158
|
-
continue outer;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
return false;
|
|
162
|
-
}
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
// Fallback: plain objects / custom class instances (prototype-sensitive)
|
|
166
|
-
const leftProto = Object.getPrototypeOf(currentLeft);
|
|
167
|
-
const rightProto = Object.getPrototypeOf(currentRight);
|
|
168
|
-
if (leftProto !== rightProto)
|
|
169
|
-
return false;
|
|
170
|
-
const leftKeys = Object.keys(currentLeft);
|
|
171
|
-
const rightKeys = Object.keys(currentRight);
|
|
172
|
-
if (leftKeys.length !== rightKeys.length)
|
|
173
|
-
return false;
|
|
174
|
-
const leftRecord = currentLeft;
|
|
175
|
-
const rightRecord = currentRight;
|
|
176
|
-
for (let i = 0; i < leftKeys.length; i++) {
|
|
177
|
-
const key = leftKeys[i];
|
|
178
|
-
if (!Object.hasOwn(rightRecord, key))
|
|
179
|
-
return false;
|
|
180
|
-
worklist.push([leftRecord[key], rightRecord[key]]);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return true;
|
|
184
|
-
}
|
|
1
|
+
import { isObjectLike, isPrimitive, sameValue } from "./helpers.js";
|
|
2
|
+
import { compareInternal } from "./compareEngine.js";
|
|
185
3
|
/**
|
|
186
4
|
* Deep structural equality with:
|
|
187
5
|
* - Value semantics for primitives (+ NaN equal, +0/-0 equal)
|
|
@@ -197,5 +15,9 @@ export function deepEqual(a, b) {
|
|
|
197
15
|
return false;
|
|
198
16
|
if (!isObjectLike(a) || !isObjectLike(b))
|
|
199
17
|
return false;
|
|
200
|
-
return
|
|
18
|
+
return compareInternal(a, b, {
|
|
19
|
+
checkPrototype: true,
|
|
20
|
+
treatSparseAsUndefined: false,
|
|
21
|
+
bijectiveCycleCheck: false,
|
|
22
|
+
}, new WeakMap());
|
|
201
23
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../src/utils/diff.ts"],"names":[],"mappings":"AA2EA,wBAAgB,UAAU,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CA6BvE"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { colors } from "./colors.js";
|
|
2
|
+
function formatValue(value, indent = 2) {
|
|
3
|
+
if (value === undefined)
|
|
4
|
+
return "undefined";
|
|
5
|
+
if (value === null)
|
|
6
|
+
return "null";
|
|
7
|
+
if (typeof value === "string")
|
|
8
|
+
return JSON.stringify(value);
|
|
9
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
10
|
+
return String(value);
|
|
11
|
+
if (typeof value === "bigint")
|
|
12
|
+
return `${value}n`;
|
|
13
|
+
if (typeof value === "symbol")
|
|
14
|
+
return value.toString();
|
|
15
|
+
if (typeof value === "function")
|
|
16
|
+
return `[Function: ${value.name || "anonymous"}]`;
|
|
17
|
+
if (value instanceof Date)
|
|
18
|
+
return `Date(${value.toISOString()})`;
|
|
19
|
+
if (value instanceof RegExp)
|
|
20
|
+
return String(value);
|
|
21
|
+
if (value instanceof Error)
|
|
22
|
+
return `${value.name}: ${value.message}`;
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
if (value.length === 0)
|
|
25
|
+
return "[]";
|
|
26
|
+
const padding = " ".repeat(indent);
|
|
27
|
+
const items = value.map((v) => `${padding}${formatValue(v, indent + 2)}`);
|
|
28
|
+
return `[\n${items.join(",\n")}\n${" ".repeat(indent - 2)}]`;
|
|
29
|
+
}
|
|
30
|
+
if (typeof value === "object") {
|
|
31
|
+
const keys = Object.keys(value);
|
|
32
|
+
if (keys.length === 0)
|
|
33
|
+
return "{}";
|
|
34
|
+
const padding = " ".repeat(indent);
|
|
35
|
+
const entries = keys.map((k) => `${padding}${k}: ${formatValue(value[k], indent + 2)}`);
|
|
36
|
+
return `{\n${entries.join(",\n")}\n${" ".repeat(indent - 2)}}`;
|
|
37
|
+
}
|
|
38
|
+
return String(value);
|
|
39
|
+
}
|
|
40
|
+
function diffObjects(received, expected) {
|
|
41
|
+
const allKeys = new Set([...Object.keys(received), ...Object.keys(expected)]);
|
|
42
|
+
const lines = [];
|
|
43
|
+
for (const key of allKeys) {
|
|
44
|
+
const inReceived = Object.hasOwn(received, key);
|
|
45
|
+
const inExpected = Object.hasOwn(expected, key);
|
|
46
|
+
if (inReceived && inExpected) {
|
|
47
|
+
const rv = formatValue(received[key]);
|
|
48
|
+
const ev = formatValue(expected[key]);
|
|
49
|
+
if (rv !== ev) {
|
|
50
|
+
lines.push(colors.red(` - ${key}: ${rv}`));
|
|
51
|
+
lines.push(colors.green(` + ${key}: ${ev}`));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
lines.push(` ${key}: ${rv}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else if (inReceived) {
|
|
58
|
+
lines.push(colors.red(` - ${key}: ${formatValue(received[key])}`));
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
lines.push(colors.green(` + ${key}: ${formatValue(expected[key])}`));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return lines.join("\n");
|
|
65
|
+
}
|
|
66
|
+
export function formatDiff(received, expected) {
|
|
67
|
+
// For objects, provide a structural diff
|
|
68
|
+
if (received !== null &&
|
|
69
|
+
expected !== null &&
|
|
70
|
+
typeof received === "object" &&
|
|
71
|
+
typeof expected === "object" &&
|
|
72
|
+
!Array.isArray(received) &&
|
|
73
|
+
!Array.isArray(expected) &&
|
|
74
|
+
!(received instanceof Date) &&
|
|
75
|
+
!(expected instanceof Date) &&
|
|
76
|
+
!(received instanceof RegExp) &&
|
|
77
|
+
!(expected instanceof RegExp)) {
|
|
78
|
+
const diff = diffObjects(received, expected);
|
|
79
|
+
return (`\n${colors.red("- Received")} / ${colors.green("+ Expected")}\n\n` + diff);
|
|
80
|
+
}
|
|
81
|
+
// Default: simple received/expected display
|
|
82
|
+
return (`\n ${colors.red("Received:")} ${formatValue(received)}` +
|
|
83
|
+
`\n ${colors.green("Expected:")} ${formatValue(expected)}`);
|
|
84
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discoverTestFiles.d.ts","sourceRoot":"","sources":["../../../src/utils/discoverTestFiles.ts"],"names":[],"mappings":"AAGA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"discoverTestFiles.d.ts","sourceRoot":"","sources":["../../../src/utils/discoverTestFiles.ts"],"names":[],"mappings":"AAGA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAqCvD"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
1
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
export function isTestFileName(name) {
|
|
4
|
-
return /\.(spec|test)\.(ts|js)$/.test(name);
|
|
4
|
+
return /\.(spec|test)\.(ts|js|mts|cts|tsx|jsx|mjs|cjs)$/.test(name);
|
|
5
5
|
}
|
|
6
6
|
export function discoverTestFiles(dir) {
|
|
7
7
|
if (!existsSync(dir))
|
|
@@ -10,6 +10,21 @@ export function discoverTestFiles(dir) {
|
|
|
10
10
|
const files = [];
|
|
11
11
|
for (const entry of entries) {
|
|
12
12
|
const fullPath = join(dir, entry.name);
|
|
13
|
+
if (entry.isSymbolicLink()) {
|
|
14
|
+
try {
|
|
15
|
+
const stat = statSync(fullPath);
|
|
16
|
+
if (stat.isDirectory()) {
|
|
17
|
+
files.push(...discoverTestFiles(fullPath));
|
|
18
|
+
}
|
|
19
|
+
else if (stat.isFile() && isTestFileName(entry.name)) {
|
|
20
|
+
files.push(fullPath);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Broken symlink — skip silently
|
|
25
|
+
}
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
13
28
|
if (entry.isDirectory()) {
|
|
14
29
|
files.push(...discoverTestFiles(fullPath));
|
|
15
30
|
continue;
|
|
@@ -1,2 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loose structural equality with:
|
|
3
|
+
* - Value semantics for primitives (+ NaN equal, +0/-0 equal)
|
|
4
|
+
* - Prototype-insensitive object comparison
|
|
5
|
+
* - Sparse array holes treated as undefined
|
|
6
|
+
* - Missing keys ≈ undefined (union-of-keys approach)
|
|
7
|
+
* - Bidirectional cycle tracking
|
|
8
|
+
* - Order-insensitive Set/Map comparison
|
|
9
|
+
*/
|
|
1
10
|
export declare function looseEqual<T>(left: T, right: T): boolean;
|
|
2
11
|
//# sourceMappingURL=looseEqual.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"looseEqual.d.ts","sourceRoot":"","sources":["../../../src/utils/looseEqual.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"looseEqual.d.ts","sourceRoot":"","sources":["../../../src/utils/looseEqual.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAgBxD"}
|