@forwardimpact/libmock 0.1.9 → 0.1.10
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/package.json +3 -2
- package/src/expect/index.js +476 -0
- package/src/mock/git-client.js +7 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forwardimpact/libmock",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Shared mocks and test fixtures so every library and service tests the same way.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"test",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
".": "./src/index.js",
|
|
23
23
|
"./mock": "./src/mock/index.js",
|
|
24
24
|
"./fixture": "./src/fixture/index.js",
|
|
25
|
-
"./runtime": "./src/runtime.js"
|
|
25
|
+
"./runtime": "./src/runtime.js",
|
|
26
|
+
"./expect": "./src/expect/index.js"
|
|
26
27
|
},
|
|
27
28
|
"files": [
|
|
28
29
|
"src/**/*.js",
|
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency-free, runner-independent `expect()` shim. Replaces `expect` from
|
|
3
|
+
* `bun:test` so the test suite can run under `node --test` (which ships no
|
|
4
|
+
* `expect`) as well as `bun test`. Mirrors how `spy()` replaced `mock.fn`.
|
|
5
|
+
*
|
|
6
|
+
* Covers exactly the matcher surface the converged test files use: `toBe`,
|
|
7
|
+
* `toEqual`, `toMatchObject`, `toBeNull`, `toBeUndefined`, `toBeDefined`,
|
|
8
|
+
* `toBeTruthy`, `toBeGreaterThan`, `toBeGreaterThanOrEqual`,
|
|
9
|
+
* `toBeLessThanOrEqual`, `toHaveLength`, `toContain`, `toMatch`, `toThrow`,
|
|
10
|
+
* plus the `.not`, `.resolves`, and `.rejects` chain modifiers.
|
|
11
|
+
*
|
|
12
|
+
* Imports only from the Node standard library.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { AssertionError } from "node:assert";
|
|
16
|
+
import { isDeepStrictEqual } from "node:util";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Render a value for assertion messages without throwing on circular refs.
|
|
20
|
+
* @param {unknown} value - Value to stringify.
|
|
21
|
+
* @returns {string} Human-readable rendering.
|
|
22
|
+
*/
|
|
23
|
+
function show(value) {
|
|
24
|
+
if (typeof value === "string") {
|
|
25
|
+
return JSON.stringify(value);
|
|
26
|
+
}
|
|
27
|
+
if (typeof value === "bigint") {
|
|
28
|
+
return `${value}n`;
|
|
29
|
+
}
|
|
30
|
+
if (value instanceof Map) {
|
|
31
|
+
return `Map(${value.size})`;
|
|
32
|
+
}
|
|
33
|
+
if (value instanceof Set) {
|
|
34
|
+
return `Set(${value.size})`;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
return JSON.stringify(value) ?? String(value);
|
|
38
|
+
} catch {
|
|
39
|
+
return String(value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Throw or, when negated, swallow — the single choke point that makes `.not`
|
|
45
|
+
* invert every matcher. `pass` is the un-negated truth of the assertion.
|
|
46
|
+
* @param {boolean} pass - Whether the positive assertion holds.
|
|
47
|
+
* @param {boolean} negated - Whether `.not` is active.
|
|
48
|
+
* @param {string} message - Message for the failing (positive) direction.
|
|
49
|
+
* @param {string} [negatedMessage] - Message for the failing negated direction.
|
|
50
|
+
*/
|
|
51
|
+
function settle(pass, negated, message, negatedMessage) {
|
|
52
|
+
if (negated) {
|
|
53
|
+
if (pass) {
|
|
54
|
+
throw new AssertionError({
|
|
55
|
+
message: negatedMessage ?? `not: ${message}`,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (!pass) {
|
|
61
|
+
throw new AssertionError({ message });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const ASYMMETRIC = Symbol.for("libmock.expect.asymmetric");
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Whether `value` is an asymmetric matcher produced by `expect.any` / friends.
|
|
69
|
+
* @param {unknown} value - Candidate.
|
|
70
|
+
* @returns {boolean} True when `value` carries the asymmetric marker.
|
|
71
|
+
*/
|
|
72
|
+
function isAsymmetric(value) {
|
|
73
|
+
return (
|
|
74
|
+
value != null && typeof value === "object" && value[ASYMMETRIC] === true
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Structural equality that understands asymmetric matchers (`expect.any`).
|
|
80
|
+
* Falls back to `node:util.isDeepStrictEqual` when neither side carries one.
|
|
81
|
+
* @param {unknown} actual - Observed value.
|
|
82
|
+
* @param {unknown} expected - Expected value, possibly an asymmetric matcher.
|
|
83
|
+
* @returns {boolean} Whether they match.
|
|
84
|
+
*/
|
|
85
|
+
function equals(actual, expected) {
|
|
86
|
+
if (isAsymmetric(expected)) {
|
|
87
|
+
return expected.asymmetricMatch(actual);
|
|
88
|
+
}
|
|
89
|
+
if (expected === null || typeof expected !== "object") {
|
|
90
|
+
return Object.is(actual, expected) || isDeepStrictEqual(actual, expected);
|
|
91
|
+
}
|
|
92
|
+
if (actual === null || typeof actual !== "object") {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (Array.isArray(expected)) {
|
|
96
|
+
if (!Array.isArray(actual) || actual.length !== expected.length) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
return expected.every((e, i) => equals(actual[i], e));
|
|
100
|
+
}
|
|
101
|
+
// No asymmetric matcher anywhere → exact deep equality is correct and cheap.
|
|
102
|
+
if (!containsAsymmetric(expected)) {
|
|
103
|
+
return isDeepStrictEqual(actual, expected);
|
|
104
|
+
}
|
|
105
|
+
const keys = Object.keys(expected);
|
|
106
|
+
if (keys.length !== Object.keys(actual).length) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
return keys.every((k) => equals(actual[k], expected[k]));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Whether an expected value tree contains any asymmetric matcher.
|
|
114
|
+
* @param {unknown} value - Tree to scan.
|
|
115
|
+
* @returns {boolean} True if an asymmetric matcher is present.
|
|
116
|
+
*/
|
|
117
|
+
function containsAsymmetric(value) {
|
|
118
|
+
if (isAsymmetric(value)) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
if (value === null || typeof value !== "object") {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
if (Array.isArray(value)) {
|
|
125
|
+
return value.some(containsAsymmetric);
|
|
126
|
+
}
|
|
127
|
+
return Object.values(value).some(containsAsymmetric);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Deep subset match: every own enumerable key of `subset` matches the same key
|
|
132
|
+
* in `actual` (recursively for plain objects, asymmetric-aware).
|
|
133
|
+
* @param {unknown} actual - Candidate object.
|
|
134
|
+
* @param {unknown} subset - Expected subset.
|
|
135
|
+
* @returns {boolean} Whether `actual` contains `subset`.
|
|
136
|
+
*/
|
|
137
|
+
function matchesObject(actual, subset) {
|
|
138
|
+
if (isAsymmetric(subset)) {
|
|
139
|
+
return subset.asymmetricMatch(actual);
|
|
140
|
+
}
|
|
141
|
+
if (subset === null || typeof subset !== "object") {
|
|
142
|
+
return equals(actual, subset);
|
|
143
|
+
}
|
|
144
|
+
if (actual === null || typeof actual !== "object") {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
for (const key of Object.keys(subset)) {
|
|
148
|
+
const sv = subset[key];
|
|
149
|
+
const av = actual[key];
|
|
150
|
+
if (
|
|
151
|
+
sv !== null &&
|
|
152
|
+
typeof sv === "object" &&
|
|
153
|
+
!Array.isArray(sv) &&
|
|
154
|
+
!isAsymmetric(sv)
|
|
155
|
+
) {
|
|
156
|
+
if (!matchesObject(av, sv)) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
} else if (!equals(av, sv)) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Run a possibly-throwing function and apply the `toThrow` predicate.
|
|
168
|
+
* @param {() => unknown} fn - The thunk under test.
|
|
169
|
+
* @param {unknown} matcher - undefined, a string (substring), or a RegExp.
|
|
170
|
+
* @param {boolean} negated - Whether `.not` is active.
|
|
171
|
+
*/
|
|
172
|
+
function assertThrow(fn, matcher, negated) {
|
|
173
|
+
if (typeof fn !== "function") {
|
|
174
|
+
throw new AssertionError({
|
|
175
|
+
message: `toThrow expects a function, got ${show(fn)}`,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
let thrown;
|
|
179
|
+
let didThrow = false;
|
|
180
|
+
try {
|
|
181
|
+
fn();
|
|
182
|
+
} catch (err) {
|
|
183
|
+
didThrow = true;
|
|
184
|
+
thrown = err;
|
|
185
|
+
}
|
|
186
|
+
if (!didThrow) {
|
|
187
|
+
settle(false, negated, "expected function to throw, but it did not");
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const message = thrown instanceof Error ? thrown.message : String(thrown);
|
|
191
|
+
let pass = true;
|
|
192
|
+
if (typeof matcher === "string") {
|
|
193
|
+
pass = message.includes(matcher);
|
|
194
|
+
} else if (matcher instanceof RegExp) {
|
|
195
|
+
pass = matcher.test(message);
|
|
196
|
+
}
|
|
197
|
+
settle(
|
|
198
|
+
pass,
|
|
199
|
+
negated,
|
|
200
|
+
`expected thrown message ${show(message)} to match ${show(matcher)}`,
|
|
201
|
+
`expected thrown message ${show(message)} not to match ${show(matcher)}`,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Build the synchronous matcher object for an already-resolved actual value.
|
|
207
|
+
* @param {unknown} actual - The value under test.
|
|
208
|
+
* @param {boolean} negated - Whether `.not` is active.
|
|
209
|
+
* @returns {object} Matcher object.
|
|
210
|
+
*/
|
|
211
|
+
function matchers(actual, negated) {
|
|
212
|
+
return {
|
|
213
|
+
get not() {
|
|
214
|
+
return matchers(actual, !negated);
|
|
215
|
+
},
|
|
216
|
+
toBe(expected) {
|
|
217
|
+
settle(
|
|
218
|
+
Object.is(actual, expected),
|
|
219
|
+
negated,
|
|
220
|
+
`expected ${show(actual)} to be ${show(expected)}`,
|
|
221
|
+
`expected ${show(actual)} not to be ${show(expected)}`,
|
|
222
|
+
);
|
|
223
|
+
},
|
|
224
|
+
toEqual(expected) {
|
|
225
|
+
settle(
|
|
226
|
+
equals(actual, expected),
|
|
227
|
+
negated,
|
|
228
|
+
`expected ${show(actual)} to deep-equal ${show(expected)}`,
|
|
229
|
+
`expected ${show(actual)} not to deep-equal ${show(expected)}`,
|
|
230
|
+
);
|
|
231
|
+
},
|
|
232
|
+
toMatchObject(expected) {
|
|
233
|
+
settle(
|
|
234
|
+
matchesObject(actual, expected),
|
|
235
|
+
negated,
|
|
236
|
+
`expected ${show(actual)} to match object ${show(expected)}`,
|
|
237
|
+
`expected ${show(actual)} not to match object ${show(expected)}`,
|
|
238
|
+
);
|
|
239
|
+
},
|
|
240
|
+
toBeNull() {
|
|
241
|
+
settle(
|
|
242
|
+
actual === null,
|
|
243
|
+
negated,
|
|
244
|
+
`expected ${show(actual)} to be null`,
|
|
245
|
+
`expected ${show(actual)} not to be null`,
|
|
246
|
+
);
|
|
247
|
+
},
|
|
248
|
+
toBeUndefined() {
|
|
249
|
+
settle(
|
|
250
|
+
actual === undefined,
|
|
251
|
+
negated,
|
|
252
|
+
`expected ${show(actual)} to be undefined`,
|
|
253
|
+
`expected ${show(actual)} not to be undefined`,
|
|
254
|
+
);
|
|
255
|
+
},
|
|
256
|
+
toBeDefined() {
|
|
257
|
+
settle(
|
|
258
|
+
actual !== undefined,
|
|
259
|
+
negated,
|
|
260
|
+
`expected ${show(actual)} to be defined`,
|
|
261
|
+
`expected ${show(actual)} not to be defined`,
|
|
262
|
+
);
|
|
263
|
+
},
|
|
264
|
+
toBeTruthy() {
|
|
265
|
+
settle(
|
|
266
|
+
Boolean(actual),
|
|
267
|
+
negated,
|
|
268
|
+
`expected ${show(actual)} to be truthy`,
|
|
269
|
+
`expected ${show(actual)} not to be truthy`,
|
|
270
|
+
);
|
|
271
|
+
},
|
|
272
|
+
toBeGreaterThan(expected) {
|
|
273
|
+
settle(
|
|
274
|
+
actual > expected,
|
|
275
|
+
negated,
|
|
276
|
+
`expected ${show(actual)} to be greater than ${show(expected)}`,
|
|
277
|
+
`expected ${show(actual)} not to be greater than ${show(expected)}`,
|
|
278
|
+
);
|
|
279
|
+
},
|
|
280
|
+
toBeGreaterThanOrEqual(expected) {
|
|
281
|
+
settle(
|
|
282
|
+
actual >= expected,
|
|
283
|
+
negated,
|
|
284
|
+
`expected ${show(actual)} to be >= ${show(expected)}`,
|
|
285
|
+
`expected ${show(actual)} not to be >= ${show(expected)}`,
|
|
286
|
+
);
|
|
287
|
+
},
|
|
288
|
+
toBeLessThanOrEqual(expected) {
|
|
289
|
+
settle(
|
|
290
|
+
actual <= expected,
|
|
291
|
+
negated,
|
|
292
|
+
`expected ${show(actual)} to be <= ${show(expected)}`,
|
|
293
|
+
`expected ${show(actual)} not to be <= ${show(expected)}`,
|
|
294
|
+
);
|
|
295
|
+
},
|
|
296
|
+
toHaveLength(expected) {
|
|
297
|
+
const len = actual == null ? undefined : actual.length;
|
|
298
|
+
settle(
|
|
299
|
+
len === expected,
|
|
300
|
+
negated,
|
|
301
|
+
`expected length ${show(len)} to be ${show(expected)}`,
|
|
302
|
+
`expected length ${show(len)} not to be ${show(expected)}`,
|
|
303
|
+
);
|
|
304
|
+
},
|
|
305
|
+
toContain(expected) {
|
|
306
|
+
let pass = false;
|
|
307
|
+
if (typeof actual === "string") {
|
|
308
|
+
pass = actual.includes(expected);
|
|
309
|
+
} else if (Array.isArray(actual)) {
|
|
310
|
+
pass = actual.includes(expected);
|
|
311
|
+
} else if (actual instanceof Set) {
|
|
312
|
+
pass = actual.has(expected);
|
|
313
|
+
} else if (
|
|
314
|
+
actual != null &&
|
|
315
|
+
typeof actual[Symbol.iterator] === "function"
|
|
316
|
+
) {
|
|
317
|
+
pass = [...actual].includes(expected);
|
|
318
|
+
}
|
|
319
|
+
settle(
|
|
320
|
+
pass,
|
|
321
|
+
negated,
|
|
322
|
+
`expected ${show(actual)} to contain ${show(expected)}`,
|
|
323
|
+
`expected ${show(actual)} not to contain ${show(expected)}`,
|
|
324
|
+
);
|
|
325
|
+
},
|
|
326
|
+
toMatch(expected) {
|
|
327
|
+
const str = String(actual);
|
|
328
|
+
const pass =
|
|
329
|
+
expected instanceof RegExp
|
|
330
|
+
? expected.test(str)
|
|
331
|
+
: str.includes(String(expected));
|
|
332
|
+
settle(
|
|
333
|
+
pass,
|
|
334
|
+
negated,
|
|
335
|
+
`expected ${show(str)} to match ${show(expected)}`,
|
|
336
|
+
`expected ${show(str)} not to match ${show(expected)}`,
|
|
337
|
+
);
|
|
338
|
+
},
|
|
339
|
+
toThrow(expected) {
|
|
340
|
+
assertThrow(actual, expected, negated);
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Build the async matcher object for a promise-producing actual. Each matcher
|
|
347
|
+
* awaits the promise and applies the underlying matcher to the settled value.
|
|
348
|
+
* @param {unknown} actual - A promise (or thenable) under test.
|
|
349
|
+
* @param {boolean} negated - Whether `.not` is active.
|
|
350
|
+
* @param {"resolves" | "rejects"} mode - Which settlement to assert on.
|
|
351
|
+
* @returns {object} Matcher object whose methods return promises.
|
|
352
|
+
*/
|
|
353
|
+
function asyncMatchers(actual, negated, mode) {
|
|
354
|
+
const wrap = (apply) => async () => {
|
|
355
|
+
let value;
|
|
356
|
+
let rejected = false;
|
|
357
|
+
let rejection;
|
|
358
|
+
try {
|
|
359
|
+
value = await actual;
|
|
360
|
+
} catch (err) {
|
|
361
|
+
rejected = true;
|
|
362
|
+
rejection = err;
|
|
363
|
+
}
|
|
364
|
+
if (mode === "rejects") {
|
|
365
|
+
if (!rejected) {
|
|
366
|
+
throw new AssertionError({
|
|
367
|
+
message: "expected promise to reject, but it resolved",
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
return apply(rejection);
|
|
371
|
+
}
|
|
372
|
+
if (rejected) {
|
|
373
|
+
throw new AssertionError({
|
|
374
|
+
message: `expected promise to resolve, but it rejected with ${show(rejection)}`,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
return apply(value);
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
const make =
|
|
381
|
+
(name) =>
|
|
382
|
+
(...args) => {
|
|
383
|
+
if (name === "toThrow") {
|
|
384
|
+
// `rejects.toThrow(x)` asserts the rejection's message; the settled value
|
|
385
|
+
// here is the rejection itself, so wrap it back into a throwing thunk.
|
|
386
|
+
return wrap((settled) => {
|
|
387
|
+
const thunk = () => {
|
|
388
|
+
throw settled;
|
|
389
|
+
};
|
|
390
|
+
assertThrow(thunk, args[0], negated);
|
|
391
|
+
})();
|
|
392
|
+
}
|
|
393
|
+
return wrap((settled) => matchers(settled, negated)[name](...args))();
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
return {
|
|
397
|
+
get not() {
|
|
398
|
+
return asyncMatchers(actual, !negated, mode);
|
|
399
|
+
},
|
|
400
|
+
toBe: make("toBe"),
|
|
401
|
+
toEqual: make("toEqual"),
|
|
402
|
+
toMatchObject: make("toMatchObject"),
|
|
403
|
+
toBeNull: make("toBeNull"),
|
|
404
|
+
toBeUndefined: make("toBeUndefined"),
|
|
405
|
+
toBeDefined: make("toBeDefined"),
|
|
406
|
+
toBeTruthy: make("toBeTruthy"),
|
|
407
|
+
toBeGreaterThan: make("toBeGreaterThan"),
|
|
408
|
+
toBeGreaterThanOrEqual: make("toBeGreaterThanOrEqual"),
|
|
409
|
+
toBeLessThanOrEqual: make("toBeLessThanOrEqual"),
|
|
410
|
+
toHaveLength: make("toHaveLength"),
|
|
411
|
+
toContain: make("toContain"),
|
|
412
|
+
toMatch: make("toMatch"),
|
|
413
|
+
toThrow: make("toThrow"),
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Jest-style `expect`. Returns a matcher object with `.not`, `.resolves`, and
|
|
419
|
+
* `.rejects` chain modifiers plus the supported matchers.
|
|
420
|
+
* @param {unknown} actual - The value under test.
|
|
421
|
+
* @returns {object} Matcher object.
|
|
422
|
+
*/
|
|
423
|
+
export function expect(actual) {
|
|
424
|
+
const base = matchers(actual, false);
|
|
425
|
+
Object.defineProperties(base, {
|
|
426
|
+
resolves: {
|
|
427
|
+
get() {
|
|
428
|
+
return asyncMatchers(actual, false, "resolves");
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
rejects: {
|
|
432
|
+
get() {
|
|
433
|
+
return asyncMatchers(actual, false, "rejects");
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
return base;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Asymmetric matcher: matches any value constructed by / typed as `ctor`,
|
|
442
|
+
* for use inside `toEqual` / `toMatchObject`. Mirrors Jest/bun `expect.any`.
|
|
443
|
+
* @param {Function} ctor - A constructor or primitive wrapper (String, Number…).
|
|
444
|
+
* @returns {object} An asymmetric matcher.
|
|
445
|
+
*/
|
|
446
|
+
const PRIMITIVE_TYPEOF = new Map([
|
|
447
|
+
[String, "string"],
|
|
448
|
+
[Number, "number"],
|
|
449
|
+
[Boolean, "boolean"],
|
|
450
|
+
[BigInt, "bigint"],
|
|
451
|
+
[Object, "object"],
|
|
452
|
+
[Function, "function"],
|
|
453
|
+
]);
|
|
454
|
+
|
|
455
|
+
expect.any = (ctor) => ({
|
|
456
|
+
[ASYMMETRIC]: true,
|
|
457
|
+
asymmetricMatch(value) {
|
|
458
|
+
if (value == null) {
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
461
|
+
const primitive = PRIMITIVE_TYPEOF.get(ctor);
|
|
462
|
+
if (primitive) {
|
|
463
|
+
return typeof value === primitive || value instanceof ctor;
|
|
464
|
+
}
|
|
465
|
+
return value instanceof ctor;
|
|
466
|
+
},
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Asymmetric matcher: matches any non-null, non-undefined value.
|
|
471
|
+
* @returns {object} An asymmetric matcher.
|
|
472
|
+
*/
|
|
473
|
+
expect.anything = () => ({
|
|
474
|
+
[ASYMMETRIC]: true,
|
|
475
|
+
asymmetricMatch: (value) => value != null,
|
|
476
|
+
});
|
package/src/mock/git-client.js
CHANGED
|
@@ -30,6 +30,13 @@ const GIT_METHODS = [
|
|
|
30
30
|
"mergeBaseExists",
|
|
31
31
|
"remoteBranchExists",
|
|
32
32
|
"fetchDeepen",
|
|
33
|
+
"pushPorcelain",
|
|
34
|
+
"remoteRefTip",
|
|
35
|
+
"isAncestor",
|
|
36
|
+
"statusPorcelain",
|
|
37
|
+
"revParse",
|
|
38
|
+
"diffNameStatus",
|
|
39
|
+
"stashDropBySha",
|
|
33
40
|
];
|
|
34
41
|
|
|
35
42
|
// A response descriptor models one git failure: an `Error` thrown as-is, or
|