@unrift/core 0.2.1 → 0.4.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.
- package/bin/unrift.mjs +2 -1
- package/dist/esm/cli.js +19 -13
- package/dist/esm/context.d.ts +1 -2
- package/dist/esm/context.d.ts.map +1 -1
- package/dist/esm/expect.d.ts +21 -1
- package/dist/esm/expect.d.ts.map +1 -1
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +3 -0
- package/dist/esm/interface.d.ts +3 -3
- package/dist/esm/interface.d.ts.map +1 -1
- package/dist/esm/interface.js +11 -6
- package/dist/esm/matchers.d.ts.map +1 -1
- package/dist/esm/matchers.js +261 -1
- package/dist/esm/mock/auto.d.ts +27 -0
- package/dist/esm/mock/auto.d.ts.map +1 -0
- package/dist/esm/mock/auto.js +108 -0
- package/dist/esm/mock/fetch.d.ts +47 -0
- package/dist/esm/mock/fetch.d.ts.map +1 -0
- package/dist/esm/mock/fetch.js +149 -0
- package/dist/esm/mock/fs.d.ts +54 -0
- package/dist/esm/mock/fs.d.ts.map +1 -0
- package/dist/esm/mock/fs.js +125 -0
- package/dist/esm/mock/index.d.ts +70 -0
- package/dist/esm/mock/index.d.ts.map +1 -0
- package/dist/esm/mock/index.js +70 -0
- package/dist/esm/mock/module-loader.d.ts +66 -0
- package/dist/esm/mock/module-loader.d.ts.map +1 -0
- package/dist/esm/mock/module-loader.js +123 -0
- package/dist/esm/mock/module.d.ts +99 -0
- package/dist/esm/mock/module.d.ts.map +1 -0
- package/dist/esm/mock/module.js +198 -0
- package/dist/esm/mock/register-loader.d.ts +17 -0
- package/dist/esm/mock/register-loader.d.ts.map +1 -0
- package/dist/esm/mock/register-loader.js +45 -0
- package/dist/esm/mock/spy.d.ts +73 -0
- package/dist/esm/mock/spy.d.ts.map +1 -0
- package/dist/esm/mock/spy.js +307 -0
- package/dist/esm/mock/stub.d.ts +26 -0
- package/dist/esm/mock/stub.d.ts.map +1 -0
- package/dist/esm/mock/stub.js +82 -0
- package/dist/esm/mock/timers.d.ts +51 -0
- package/dist/esm/mock/timers.d.ts.map +1 -0
- package/dist/esm/mock/timers.js +581 -0
- package/dist/esm/runner.d.ts +1 -0
- package/dist/esm/runner.d.ts.map +1 -1
- package/dist/esm/runner.js +138 -28
- package/dist/esm/test.d.ts +3 -2
- package/dist/esm/test.d.ts.map +1 -1
- package/dist/esm/test.js +17 -7
- package/dist/esm/types.d.ts +0 -1
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/types.js +0 -1
- package/dist/esm/utils/compareEngine.d.ts.map +1 -1
- package/dist/esm/utils/compareEngine.js +32 -1
- package/dist/esm/utils/defineConfig.d.ts +0 -1
- package/dist/esm/utils/defineConfig.d.ts.map +1 -1
- package/dist/esm/utils/diff.d.ts.map +1 -1
- package/dist/esm/utils/diff.js +31 -0
- package/dist/esm/utils/discoverTestFiles.js +1 -1
- package/dist/esm/utils/helpers.d.ts +21 -0
- package/dist/esm/utils/helpers.d.ts.map +1 -1
- package/dist/esm/utils/helpers.js +97 -0
- package/dist/esm/utils/loadConfig.d.ts.map +1 -1
- package/dist/esm/utils/loadConfig.js +2 -2
- package/dist/esm/utils/toThrow.d.ts.map +1 -1
- package/dist/esm/utils/toThrow.js +2 -1
- package/package.json +8 -3
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-mocking helpers: `mock.object` and `mock.class`.
|
|
3
|
+
*
|
|
4
|
+
* Both walk own + prototype methods and replace each function with a spy.
|
|
5
|
+
* Restoration uses the same registry as `spyOn`, so `mock.restoreAll()`
|
|
6
|
+
* cleans up auto mocks too.
|
|
7
|
+
*/
|
|
8
|
+
import { spy, spyOn } from "./spy.js";
|
|
9
|
+
/**
|
|
10
|
+
* Walks the chain from `target` up to (but not including) the built in
|
|
11
|
+
* `Object.prototype` and `Function.prototype`, yielding every own method
|
|
12
|
+
* key encountered. Spying on built in prototypes (`apply`, `call`, etc.)
|
|
13
|
+
* would recurse into the spy machinery itself.
|
|
14
|
+
*/
|
|
15
|
+
function* enumerableMethodKeys(target) {
|
|
16
|
+
const seen = new Set();
|
|
17
|
+
let current = target;
|
|
18
|
+
while (current &&
|
|
19
|
+
current !== Object.prototype &&
|
|
20
|
+
current !== Function.prototype) {
|
|
21
|
+
for (const key of Reflect.ownKeys(current)) {
|
|
22
|
+
if (seen.has(key))
|
|
23
|
+
continue;
|
|
24
|
+
// Skip the `constructor` property, replacing it would break
|
|
25
|
+
// `instance.constructor` checks and is rarely what callers want.
|
|
26
|
+
if (key === "constructor")
|
|
27
|
+
continue;
|
|
28
|
+
// Built in `Function` properties on a constructor we don't want to
|
|
29
|
+
// wrap (length / name are non-writable; prototype is structural).
|
|
30
|
+
if (key === "length" || key === "name" || key === "prototype")
|
|
31
|
+
continue;
|
|
32
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
33
|
+
if (!descriptor)
|
|
34
|
+
continue;
|
|
35
|
+
// Skip getters/setters: spyOn would invoke the getter to read the
|
|
36
|
+
// current value, which can have side effects. Track separately.
|
|
37
|
+
if (descriptor.get || descriptor.set)
|
|
38
|
+
continue;
|
|
39
|
+
if (typeof descriptor.value !== "function")
|
|
40
|
+
continue;
|
|
41
|
+
if (descriptor.configurable === false)
|
|
42
|
+
continue;
|
|
43
|
+
seen.add(key);
|
|
44
|
+
yield { host: current, key };
|
|
45
|
+
}
|
|
46
|
+
current = Object.getPrototypeOf(current);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Replace every method on `obj` (own + inherited, excluding Object.prototype)
|
|
51
|
+
* with a spy. Returns the same reference, mutated in place.
|
|
52
|
+
*
|
|
53
|
+
* Cleanup happens via `mock.restoreAll()` - auto-mock spies share the same
|
|
54
|
+
* restorer registry as `spyOn`.
|
|
55
|
+
*/
|
|
56
|
+
export function mockObject(obj) {
|
|
57
|
+
for (const { host, key } of enumerableMethodKeys(obj)) {
|
|
58
|
+
try {
|
|
59
|
+
spyOn(host, key);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
/** Non-configurable properties (e.g. some built ins), skip silently.
|
|
63
|
+
* Test code targeting these would fail clearly when the spy methods
|
|
64
|
+
* aren't found. We don't want auto mock to throw on the first such key.
|
|
65
|
+
*/
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return obj;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Wrap a constructor so every instance is auto-mocked, and so static
|
|
72
|
+
* methods on the constructor itself are spied too. Returns a Proxy that
|
|
73
|
+
* is `instanceof`-compatible with the original.
|
|
74
|
+
*
|
|
75
|
+
* Instance methods are spied as OWN properties on each instance (shadowing
|
|
76
|
+
* the prototype), NOT by mutating the shared prototype. This keeps call
|
|
77
|
+
* state per-instance (a.run and b.run are separate spies) and leaves the
|
|
78
|
+
* class prototype untouched - nothing to restore, nothing to leak.
|
|
79
|
+
*/
|
|
80
|
+
export function mockClass(Ctor) {
|
|
81
|
+
// Spy static methods on the constructor itself (mutates Ctor in place;
|
|
82
|
+
// statics are own properties of Ctor, so spyOn restores them normally).
|
|
83
|
+
mockObject(Ctor);
|
|
84
|
+
return new Proxy(Ctor, {
|
|
85
|
+
construct(target, args, newTarget) {
|
|
86
|
+
const instance = Reflect.construct(target, args, newTarget);
|
|
87
|
+
for (const { host, key } of enumerableMethodKeys(instance)) {
|
|
88
|
+
if (host === instance) {
|
|
89
|
+
// Own method (assigned in the constructor): spy in place.
|
|
90
|
+
spyOn(instance, key);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
// Inherited method: shadow it with an own-property spy that passes
|
|
94
|
+
// through to the prototype implementation. The prototype itself is
|
|
95
|
+
// never modified.
|
|
96
|
+
const protoMethod = host[key];
|
|
97
|
+
Object.defineProperty(instance, key, {
|
|
98
|
+
value: spy(protoMethod),
|
|
99
|
+
writable: true,
|
|
100
|
+
configurable: true,
|
|
101
|
+
enumerable: false,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return instance;
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mock.fetch` - stub `globalThis.fetch` for HTTP testing.
|
|
3
|
+
*
|
|
4
|
+
* Handlers are matched against requests in registration order; the first
|
|
5
|
+
* matching handler wins. `mock.fetchOnce()` registers a single-use handler
|
|
6
|
+
* that's consumed after its first match.
|
|
7
|
+
*
|
|
8
|
+
* Restoration:
|
|
9
|
+
* - `mock.fetch.restore()` un-patches global fetch and clears handlers
|
|
10
|
+
* - `mock.restoreAll()` does the same as part of bulk cleanup
|
|
11
|
+
*/
|
|
12
|
+
/** Brand applied to `mock.fetch` so matchers can verify their received value. */
|
|
13
|
+
export declare const FETCH_MOCK_BRAND: unique symbol;
|
|
14
|
+
/**
|
|
15
|
+
* Derive WebFetch helper types from the runtime constructors so we don't
|
|
16
|
+
* have to widen the project's `lib` config to include `dom` types. These
|
|
17
|
+
* match what the platform actually accepts.
|
|
18
|
+
*/
|
|
19
|
+
type HeadersInitLike = ConstructorParameters<typeof Headers>[0];
|
|
20
|
+
type BodyInitLike = ConstructorParameters<typeof Response>[0];
|
|
21
|
+
/** What can be passed as the first argument to `mock.fetch(matcher, ...)`. */
|
|
22
|
+
export type FetchMatcher = string | RegExp | ((req: Request) => boolean | Promise<boolean>);
|
|
23
|
+
export interface MockResponseInit {
|
|
24
|
+
status?: number;
|
|
25
|
+
statusText?: string;
|
|
26
|
+
headers?: HeadersInitLike;
|
|
27
|
+
body?: BodyInitLike | null;
|
|
28
|
+
json?: unknown;
|
|
29
|
+
}
|
|
30
|
+
export type MockFetchResponse = Response | MockResponseInit | ((req: Request) => Response | MockResponseInit | Promise<Response | MockResponseInit>);
|
|
31
|
+
/**
|
|
32
|
+
* Public surface for `mock.fetch`. Callable to register a handler, with
|
|
33
|
+
* attached helpers for inspection and cleanup.
|
|
34
|
+
*/
|
|
35
|
+
export interface MockFetch {
|
|
36
|
+
(matcher: FetchMatcher, response: MockFetchResponse): void;
|
|
37
|
+
once(matcher: FetchMatcher, response: MockFetchResponse): void;
|
|
38
|
+
readonly calls: Request[];
|
|
39
|
+
reset(): void;
|
|
40
|
+
restore(): void;
|
|
41
|
+
readonly [FETCH_MOCK_BRAND]: true;
|
|
42
|
+
}
|
|
43
|
+
export declare function isFetchMock(value: unknown): value is MockFetch;
|
|
44
|
+
export declare const mockFetch: MockFetch;
|
|
45
|
+
export declare function mockFetchOnce(matcher: FetchMatcher, response: MockFetchResponse): void;
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../src/mock/fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,EAAE,OAAO,MAAuC,CAAC;AAE9E;;;;GAIG;AACH,KAAK,eAAe,GAAG,qBAAqB,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAChE,KAAK,YAAY,GAAG,qBAAqB,CAAC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AAG9D,8EAA8E;AAC9E,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,MAAM,GACN,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;AAGnD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,IAAI,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAGD,MAAM,MAAM,iBAAiB,GACzB,QAAQ,GACR,gBAAgB,GAChB,CAAC,CACC,GAAG,EAAE,OAAO,KACT,QAAQ,GAAG,gBAAgB,GAAG,OAAO,CAAC,QAAQ,GAAG,gBAAgB,CAAC,CAAC,CAAC;AAuI7E;;;GAGG;AACH,MAAM,WAAW,SAAS;IAExB,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAE3D,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAE/D,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;IAE1B,KAAK,IAAI,IAAI,CAAC;IAEd,OAAO,IAAI,IAAI,CAAC;IAEhB,QAAQ,CAAC,CAAC,gBAAgB,CAAC,EAAE,IAAI,CAAC;CACnC;AAGD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAK9D;AA2BD,eAAO,MAAM,SAAS,EAAE,SAAyB,CAAC;AAElD,wBAAgB,aAAa,CAC3B,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,iBAAiB,GAC1B,IAAI,CAEN"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mock.fetch` - stub `globalThis.fetch` for HTTP testing.
|
|
3
|
+
*
|
|
4
|
+
* Handlers are matched against requests in registration order; the first
|
|
5
|
+
* matching handler wins. `mock.fetchOnce()` registers a single-use handler
|
|
6
|
+
* that's consumed after its first match.
|
|
7
|
+
*
|
|
8
|
+
* Restoration:
|
|
9
|
+
* - `mock.fetch.restore()` un-patches global fetch and clears handlers
|
|
10
|
+
* - `mock.restoreAll()` does the same as part of bulk cleanup
|
|
11
|
+
*/
|
|
12
|
+
import { activeRestorers } from "./spy.js";
|
|
13
|
+
import { regexTest } from "../utils/helpers.js";
|
|
14
|
+
/** Brand applied to `mock.fetch` so matchers can verify their received value. */
|
|
15
|
+
export const FETCH_MOCK_BRAND = Symbol.for("unrift.fetchMock");
|
|
16
|
+
// Process-global singleton state. Correct because unrift runs spec files
|
|
17
|
+
// sequentially in one process and afterEach(restoreAll) resets between tests.
|
|
18
|
+
// Parallel per-file execution (see ISSUES.md) would need per-worker isolation.
|
|
19
|
+
const handlers = [];
|
|
20
|
+
const calls = [];
|
|
21
|
+
let originalFetch;
|
|
22
|
+
// Whether globalThis had a `fetch` at install time. If it didn't (older Node,
|
|
23
|
+
// custom runtimes), restore must DELETE our patch rather than leave it - the
|
|
24
|
+
// original state was "fetch is absent", and `originalFetch` being undefined
|
|
25
|
+
// must not be mistaken for "nothing to restore".
|
|
26
|
+
let fetchWasDefined = false;
|
|
27
|
+
let installed = false;
|
|
28
|
+
function urlOf(req) {
|
|
29
|
+
return req.url;
|
|
30
|
+
}
|
|
31
|
+
async function matches(handler, req) {
|
|
32
|
+
if (handler.consumed)
|
|
33
|
+
return false;
|
|
34
|
+
const m = handler.matcher;
|
|
35
|
+
if (typeof m === "string")
|
|
36
|
+
return urlOf(req) === m;
|
|
37
|
+
if (m instanceof RegExp)
|
|
38
|
+
return regexTest(m, urlOf(req));
|
|
39
|
+
if (typeof m === "function")
|
|
40
|
+
return Boolean(await m(req));
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
async function buildResponse(spec, req) {
|
|
44
|
+
let resolved;
|
|
45
|
+
if (typeof spec === "function") {
|
|
46
|
+
resolved = await spec(req);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
resolved = spec;
|
|
50
|
+
}
|
|
51
|
+
if (resolved instanceof Response)
|
|
52
|
+
return resolved;
|
|
53
|
+
const init = resolved;
|
|
54
|
+
let body = init.body;
|
|
55
|
+
const headers = new Headers(init.headers);
|
|
56
|
+
if (init.json !== undefined) {
|
|
57
|
+
body = JSON.stringify(init.json);
|
|
58
|
+
if (!headers.has("Content-Type")) {
|
|
59
|
+
headers.set("Content-Type", "application/json");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return new Response(body ?? null, {
|
|
63
|
+
status: init.status ?? 200,
|
|
64
|
+
statusText: init.statusText,
|
|
65
|
+
headers,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function patchedFetch(input, init) {
|
|
69
|
+
/**
|
|
70
|
+
* Normalize all call shapes - `fetch(url)`, `fetch(url, init)`,
|
|
71
|
+
* `fetch(new Request(...))`, `fetch(new Request(...), init)` into a
|
|
72
|
+
* single Request object that handlers can introspect.
|
|
73
|
+
*/
|
|
74
|
+
const req = input instanceof Request
|
|
75
|
+
? new Request(input, init)
|
|
76
|
+
: new Request(input, init);
|
|
77
|
+
calls.push(req);
|
|
78
|
+
return (async () => {
|
|
79
|
+
for (const handler of handlers) {
|
|
80
|
+
if (await matches(handler, req)) {
|
|
81
|
+
if (handler.once)
|
|
82
|
+
handler.consumed = true;
|
|
83
|
+
return buildResponse(handler.response, req);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
throw new Error(`No mock.fetch handler matched: ${req.method} ${req.url}`);
|
|
87
|
+
})();
|
|
88
|
+
}
|
|
89
|
+
function install() {
|
|
90
|
+
if (installed)
|
|
91
|
+
return;
|
|
92
|
+
installed = true;
|
|
93
|
+
// `in` checks presence without triggering Node's lazy fetch getter.
|
|
94
|
+
fetchWasDefined = "fetch" in globalThis;
|
|
95
|
+
originalFetch = globalThis.fetch;
|
|
96
|
+
globalThis.fetch = patchedFetch;
|
|
97
|
+
activeRestorers.add(restoreFetch);
|
|
98
|
+
}
|
|
99
|
+
function restoreFetch() {
|
|
100
|
+
if (!installed)
|
|
101
|
+
return;
|
|
102
|
+
installed = false;
|
|
103
|
+
if (fetchWasDefined) {
|
|
104
|
+
globalThis.fetch = originalFetch;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// fetch didn't exist before we patched it - remove our patch so the
|
|
108
|
+
// environment reads `undefined` again, rather than leaking the stale mock.
|
|
109
|
+
delete globalThis.fetch;
|
|
110
|
+
}
|
|
111
|
+
originalFetch = undefined;
|
|
112
|
+
fetchWasDefined = false;
|
|
113
|
+
handlers.length = 0;
|
|
114
|
+
calls.length = 0;
|
|
115
|
+
activeRestorers.delete(restoreFetch);
|
|
116
|
+
}
|
|
117
|
+
function register(matcher, response, once) {
|
|
118
|
+
install();
|
|
119
|
+
handlers.push({ matcher, response, once, consumed: false });
|
|
120
|
+
}
|
|
121
|
+
// Type guard used by toHaveFetched / toHaveFetchedTimes matchers.
|
|
122
|
+
export function isFetchMock(value) {
|
|
123
|
+
return (typeof value === "function" &&
|
|
124
|
+
value[FETCH_MOCK_BRAND] === true);
|
|
125
|
+
}
|
|
126
|
+
const mockFetchImpl = ((matcher, response) => register(matcher, response, false));
|
|
127
|
+
mockFetchImpl.once = (matcher, response) => register(matcher, response, true);
|
|
128
|
+
mockFetchImpl.reset = () => {
|
|
129
|
+
handlers.length = 0;
|
|
130
|
+
calls.length = 0;
|
|
131
|
+
};
|
|
132
|
+
mockFetchImpl.restore = restoreFetch;
|
|
133
|
+
Object.defineProperty(mockFetchImpl, "calls", {
|
|
134
|
+
// Return a snapshot copy, not the internal array, so callers can't mutate
|
|
135
|
+
// recorded state (e.g. push/splice) and corrupt later assertions.
|
|
136
|
+
get: () => calls.slice(),
|
|
137
|
+
enumerable: true,
|
|
138
|
+
configurable: false,
|
|
139
|
+
});
|
|
140
|
+
Object.defineProperty(mockFetchImpl, FETCH_MOCK_BRAND, {
|
|
141
|
+
value: true,
|
|
142
|
+
enumerable: false,
|
|
143
|
+
configurable: false,
|
|
144
|
+
writable: false,
|
|
145
|
+
});
|
|
146
|
+
export const mockFetch = mockFetchImpl;
|
|
147
|
+
export function mockFetchOnce(matcher, response) {
|
|
148
|
+
mockFetch.once(matcher, response);
|
|
149
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File system mocking - `mock.fs()`.
|
|
3
|
+
*
|
|
4
|
+
* Backed by `memfs` today (private implementation detail behind the
|
|
5
|
+
* `FakeFsBackend` interface). The plan is to swap memfs for a hand-rolled
|
|
6
|
+
* package later; consumers of this module should only depend on the public
|
|
7
|
+
* `FakeFsHandle` surface, not on memfs types directly.
|
|
8
|
+
*
|
|
9
|
+
* Interception is layered on Tier A module mocking - `mock.doMock` is
|
|
10
|
+
* called for `node:fs` and `node:fs/promises`, so the fake takes effect
|
|
11
|
+
* when test code does `await import("node:fs")` or `await import("node:fs/promises")`.
|
|
12
|
+
* Static `import { readFileSync } from "node:fs"` is NOT intercepted in
|
|
13
|
+
* Tier A (planned for Tier B). See the docs page for the explicit limit
|
|
14
|
+
* list and workarounds.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* `Volume.fromJSON()` accepts a path-to-content map. `null` represents an
|
|
18
|
+
* empty directory; strings are file contents.
|
|
19
|
+
*/
|
|
20
|
+
export type FakeFsState = Record<string, string | null>;
|
|
21
|
+
/** Public handle returned by `mock.fs()`. */
|
|
22
|
+
export interface FakeFsHandle {
|
|
23
|
+
/** Dump the current state as a path-to-content map. */
|
|
24
|
+
toJSON(): FakeFsState;
|
|
25
|
+
/** Replace the current state with the given map. */
|
|
26
|
+
fromJSON(state: FakeFsState): void;
|
|
27
|
+
/** Read a file's contents, or null if it doesn't exist. */
|
|
28
|
+
read(path: string): string | null;
|
|
29
|
+
/** Write a file, creating parent directories as needed. */
|
|
30
|
+
write(path: string, content: string): void;
|
|
31
|
+
/** Check whether a path exists in the fake fs. */
|
|
32
|
+
exists(path: string): boolean;
|
|
33
|
+
/** Delete a file. Returns true if something was removed. */
|
|
34
|
+
delete(path: string): boolean;
|
|
35
|
+
/** List entries in a directory. Returns an empty array if missing. */
|
|
36
|
+
list(dir: string): string[];
|
|
37
|
+
/**
|
|
38
|
+
* Unpatch node:fs and node:fs/promises for this handle's interception.
|
|
39
|
+
* Idempotent, and a no-op if a later mock.fs() call has already superseded
|
|
40
|
+
* this handle - so holding an old handle for snapshotting can't tear down
|
|
41
|
+
* the current volume's interception.
|
|
42
|
+
*/
|
|
43
|
+
restore(): void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Install an in-memory file system as `node:fs` and `node:fs/promises`
|
|
47
|
+
* for the duration of the test. Subsequent `await import("node:fs")`
|
|
48
|
+
* calls return the fake.
|
|
49
|
+
*
|
|
50
|
+
* Calling `mock.fs()` while a fake is already installed swaps in a fresh
|
|
51
|
+
* volume - the previous handle's references stop receiving updates.
|
|
52
|
+
*/
|
|
53
|
+
export declare function fs(initial?: FakeFsState): FakeFsHandle;
|
|
54
|
+
//# sourceMappingURL=fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../../src/mock/fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;AAqFxD,6CAA6C;AAC7C,MAAM,WAAW,YAAY;IAC3B,uDAAuD;IACvD,MAAM,IAAI,WAAW,CAAC;IACtB,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IACnC,2DAA2D;IAC3D,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAClC,2DAA2D;IAC3D,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,kDAAkD;IAClD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,4DAA4D;IAC5D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,sEAAsE;IACtE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B;;;;;OAKG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAeD;;;;;;;GAOG;AACH,wBAAgB,EAAE,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,YAAY,CAkCtD"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File system mocking - `mock.fs()`.
|
|
3
|
+
*
|
|
4
|
+
* Backed by `memfs` today (private implementation detail behind the
|
|
5
|
+
* `FakeFsBackend` interface). The plan is to swap memfs for a hand-rolled
|
|
6
|
+
* package later; consumers of this module should only depend on the public
|
|
7
|
+
* `FakeFsHandle` surface, not on memfs types directly.
|
|
8
|
+
*
|
|
9
|
+
* Interception is layered on Tier A module mocking - `mock.doMock` is
|
|
10
|
+
* called for `node:fs` and `node:fs/promises`, so the fake takes effect
|
|
11
|
+
* when test code does `await import("node:fs")` or `await import("node:fs/promises")`.
|
|
12
|
+
* Static `import { readFileSync } from "node:fs"` is NOT intercepted in
|
|
13
|
+
* Tier A (planned for Tier B). See the docs page for the explicit limit
|
|
14
|
+
* list and workarounds.
|
|
15
|
+
*/
|
|
16
|
+
import { Volume, createFsFromVolume } from "memfs";
|
|
17
|
+
import { doMock, unmock } from "./module.js";
|
|
18
|
+
import { activeRestorers } from "./spy.js";
|
|
19
|
+
/**
|
|
20
|
+
* The current implementation factory. Swap this single line when replacing
|
|
21
|
+
* memfs with a hand-rolled backend - no other code changes needed.
|
|
22
|
+
*/
|
|
23
|
+
function createBackend(initial) {
|
|
24
|
+
const vol = Volume.fromJSON(initial ?? {});
|
|
25
|
+
const fsExports = createFsFromVolume(vol);
|
|
26
|
+
// memfs's createFsFromVolume returns the fs surface; .promises is on it.
|
|
27
|
+
const fsPromisesExports = fsExports
|
|
28
|
+
.promises;
|
|
29
|
+
return {
|
|
30
|
+
fsExports,
|
|
31
|
+
fsPromisesExports,
|
|
32
|
+
toJSON: () => vol.toJSON(),
|
|
33
|
+
fromJSON: (state) => {
|
|
34
|
+
vol.reset();
|
|
35
|
+
vol.fromJSON(state);
|
|
36
|
+
},
|
|
37
|
+
readFile: (path) => {
|
|
38
|
+
try {
|
|
39
|
+
return vol.readFileSync(path, "utf8");
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
writeFile: (path, content) => {
|
|
46
|
+
// memfs throws if the parent directory doesn't exist; create it for
|
|
47
|
+
// ergonomic test code.
|
|
48
|
+
const lastSlash = path.lastIndexOf("/");
|
|
49
|
+
if (lastSlash > 0) {
|
|
50
|
+
vol.mkdirSync(path.slice(0, lastSlash), { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
vol.writeFileSync(path, content);
|
|
53
|
+
},
|
|
54
|
+
exists: (path) => vol.existsSync(path),
|
|
55
|
+
remove: (path) => {
|
|
56
|
+
try {
|
|
57
|
+
vol.unlinkSync(path);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
list: (dir) => {
|
|
65
|
+
try {
|
|
66
|
+
return vol.readdirSync(dir);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Process-global singleton: only one fake fs interception is active at a time.
|
|
75
|
+
// Safe under sequential per-file execution + afterEach(restoreAll); parallel
|
|
76
|
+
// per-file execution (see ISSUES.md) would need per-worker isolation.
|
|
77
|
+
let activeBackend;
|
|
78
|
+
const restoreFakeFs = () => {
|
|
79
|
+
if (!activeBackend)
|
|
80
|
+
return;
|
|
81
|
+
unmock("node:fs");
|
|
82
|
+
unmock("node:fs/promises");
|
|
83
|
+
activeBackend = undefined;
|
|
84
|
+
activeRestorers.delete(restoreFakeFs);
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Install an in-memory file system as `node:fs` and `node:fs/promises`
|
|
88
|
+
* for the duration of the test. Subsequent `await import("node:fs")`
|
|
89
|
+
* calls return the fake.
|
|
90
|
+
*
|
|
91
|
+
* Calling `mock.fs()` while a fake is already installed swaps in a fresh
|
|
92
|
+
* volume - the previous handle's references stop receiving updates.
|
|
93
|
+
*/
|
|
94
|
+
export function fs(initial) {
|
|
95
|
+
// Tear down any existing fake before installing the new one so the
|
|
96
|
+
// module-mocking registrations don't stack.
|
|
97
|
+
if (activeBackend) {
|
|
98
|
+
unmock("node:fs");
|
|
99
|
+
unmock("node:fs/promises");
|
|
100
|
+
}
|
|
101
|
+
const backend = createBackend(initial);
|
|
102
|
+
activeBackend = backend;
|
|
103
|
+
doMock("node:fs", () => backend.fsExports);
|
|
104
|
+
doMock("node:fs/promises", () => backend.fsPromisesExports);
|
|
105
|
+
activeRestorers.add(restoreFakeFs);
|
|
106
|
+
const handle = {
|
|
107
|
+
toJSON: () => backend.toJSON(),
|
|
108
|
+
fromJSON: (state) => backend.fromJSON(state),
|
|
109
|
+
read: (path) => backend.readFile(path),
|
|
110
|
+
write: (path, content) => backend.writeFile(path, content),
|
|
111
|
+
exists: (path) => backend.exists(path),
|
|
112
|
+
delete: (path) => backend.remove(path),
|
|
113
|
+
list: (dir) => backend.list(dir),
|
|
114
|
+
// Only tear down the interception if THIS handle still owns it. A later
|
|
115
|
+
// mock.fs() call orphans this handle - its volume lives on for snapshot
|
|
116
|
+
// inspection, but restoring it must not rip out the newer volume's
|
|
117
|
+
// interception. On an orphaned handle this is a no-op (also makes restore
|
|
118
|
+
// idempotent: a second call finds activeBackend already cleared).
|
|
119
|
+
restore: () => {
|
|
120
|
+
if (activeBackend === backend)
|
|
121
|
+
restoreFakeFs();
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
return handle;
|
|
125
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mock` namespace - single entry point for all mocking utilities.
|
|
3
|
+
*
|
|
4
|
+
* Both forms are supported:
|
|
5
|
+
* import { mock } from "unrift";
|
|
6
|
+
* mock.fn();
|
|
7
|
+
* mock.spyOn(obj, "method");
|
|
8
|
+
*
|
|
9
|
+
* import { spy, spyOn } from "unrift";
|
|
10
|
+
* spy();
|
|
11
|
+
*
|
|
12
|
+
* They produce identical objects.
|
|
13
|
+
*/
|
|
14
|
+
import { spy, spyOn, isSpy, clearAllMocks, resetAllMocks, restoreAllMocks } from "./spy";
|
|
15
|
+
import { mockObject, mockClass } from "./auto";
|
|
16
|
+
import { stub, mockGlobal } from "./stub";
|
|
17
|
+
import { mockFetch, mockFetchOnce } from "./fetch";
|
|
18
|
+
import { useFakeTimers, useRealTimers, advanceTimersByTime, advanceTimersByTimeAsync, runAllTimers, runAllTimersAsync, runOnlyPendingTimers, runOnlyPendingTimersAsync, getTimerCount, setSystemTime, getRealSystemTime } from "./timers";
|
|
19
|
+
import { doMock, unmock, listMockedSpecs, fromModule } from "./module";
|
|
20
|
+
import { fs as mockFs } from "./fs";
|
|
21
|
+
export { spy, spyOn, isSpy, mockObject, mockClass, stub, mockGlobal, mockFetch, mockFetchOnce, useFakeTimers, useRealTimers, advanceTimersByTime, advanceTimersByTimeAsync, runAllTimers, runAllTimersAsync, runOnlyPendingTimers, runOnlyPendingTimersAsync, getTimerCount, setSystemTime, getRealSystemTime, doMock, unmock, listMockedSpecs, fromModule, mockFs, clearAllMocks, resetAllMocks, restoreAllMocks, };
|
|
22
|
+
export type { Spy, MockState, MockResult, AnyFn } from "./spy";
|
|
23
|
+
export type { MockFetch, FetchMatcher, MockResponseInit, MockFetchResponse, } from "./fetch";
|
|
24
|
+
export type { UseFakeTimersOptions, FakeableApi } from "./timers";
|
|
25
|
+
export type { LoaderMessage } from "./module";
|
|
26
|
+
export type { FakeFsHandle, FakeFsState } from "./fs";
|
|
27
|
+
/**
|
|
28
|
+
* Unified mock namespace. Mirrors Jest / Vitest naming so existing test
|
|
29
|
+
* code reads naturally:
|
|
30
|
+
*
|
|
31
|
+
* mock.fn(impl?) // spy()
|
|
32
|
+
* mock.spyOn(obj, key)
|
|
33
|
+
* mock.object(obj) // auto-spy every method
|
|
34
|
+
* mock.class(Ctor) // auto-spy every method on instances
|
|
35
|
+
* mock.global(key, value) // stub on globalThis
|
|
36
|
+
* mock.stub(target, key, value)
|
|
37
|
+
* mock.clearAll()
|
|
38
|
+
* mock.resetAll()
|
|
39
|
+
* mock.restoreAll()
|
|
40
|
+
*/
|
|
41
|
+
export declare const mock: {
|
|
42
|
+
readonly fn: typeof spy;
|
|
43
|
+
readonly spyOn: typeof spyOn;
|
|
44
|
+
readonly object: typeof mockObject;
|
|
45
|
+
readonly class: typeof mockClass;
|
|
46
|
+
readonly global: typeof mockGlobal;
|
|
47
|
+
readonly stub: typeof stub;
|
|
48
|
+
readonly fetch: import("./fetch").MockFetch;
|
|
49
|
+
readonly fetchOnce: typeof mockFetchOnce;
|
|
50
|
+
readonly useFakeTimers: typeof useFakeTimers;
|
|
51
|
+
readonly useRealTimers: typeof useRealTimers;
|
|
52
|
+
readonly advanceTimersByTime: typeof advanceTimersByTime;
|
|
53
|
+
readonly advanceTimersByTimeAsync: typeof advanceTimersByTimeAsync;
|
|
54
|
+
readonly runAllTimers: typeof runAllTimers;
|
|
55
|
+
readonly runAllTimersAsync: typeof runAllTimersAsync;
|
|
56
|
+
readonly runOnlyPendingTimers: typeof runOnlyPendingTimers;
|
|
57
|
+
readonly runOnlyPendingTimersAsync: typeof runOnlyPendingTimersAsync;
|
|
58
|
+
readonly getTimerCount: typeof getTimerCount;
|
|
59
|
+
readonly setSystemTime: typeof setSystemTime;
|
|
60
|
+
readonly getRealSystemTime: typeof getRealSystemTime;
|
|
61
|
+
readonly doMock: typeof doMock;
|
|
62
|
+
readonly unmock: typeof unmock;
|
|
63
|
+
readonly fromModule: typeof fromModule;
|
|
64
|
+
readonly listMockedSpecs: typeof listMockedSpecs;
|
|
65
|
+
readonly fs: typeof mockFs;
|
|
66
|
+
readonly clearAll: typeof clearAllMocks;
|
|
67
|
+
readonly resetAll: typeof resetAllMocks;
|
|
68
|
+
readonly restoreAll: typeof restoreAllMocks;
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mock/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,GAAG,EACH,KAAK,EACL,KAAK,EACL,aAAa,EACb,aAAa,EACb,eAAe,EAChB,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EACL,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,wBAAwB,EACxB,YAAY,EACZ,iBAAiB,EACjB,oBAAoB,EACpB,yBAAyB,EACzB,aAAa,EACb,aAAa,EACb,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACvE,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,EACL,GAAG,EACH,KAAK,EACL,KAAK,EACL,UAAU,EACV,SAAS,EACT,IAAI,EACJ,UAAU,EACV,SAAS,EACT,aAAa,EACb,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,wBAAwB,EACxB,YAAY,EACZ,iBAAiB,EACjB,oBAAoB,EACpB,yBAAyB,EACzB,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,MAAM,EACN,MAAM,EACN,eAAe,EACf,UAAU,EACV,MAAM,EACN,aAAa,EACb,aAAa,EACb,eAAe,GAChB,CAAC;AAEF,YAAY,EAAE,GAAG,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC/D,YAAY,EACV,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAClE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AAEtD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCP,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mock` namespace - single entry point for all mocking utilities.
|
|
3
|
+
*
|
|
4
|
+
* Both forms are supported:
|
|
5
|
+
* import { mock } from "unrift";
|
|
6
|
+
* mock.fn();
|
|
7
|
+
* mock.spyOn(obj, "method");
|
|
8
|
+
*
|
|
9
|
+
* import { spy, spyOn } from "unrift";
|
|
10
|
+
* spy();
|
|
11
|
+
*
|
|
12
|
+
* They produce identical objects.
|
|
13
|
+
*/
|
|
14
|
+
import { spy, spyOn, isSpy, clearAllMocks, resetAllMocks, restoreAllMocks, } from "./spy.js";
|
|
15
|
+
import { mockObject, mockClass } from "./auto.js";
|
|
16
|
+
import { stub, mockGlobal } from "./stub.js";
|
|
17
|
+
import { mockFetch, mockFetchOnce } from "./fetch.js";
|
|
18
|
+
import { useFakeTimers, useRealTimers, advanceTimersByTime, advanceTimersByTimeAsync, runAllTimers, runAllTimersAsync, runOnlyPendingTimers, runOnlyPendingTimersAsync, getTimerCount, setSystemTime, getRealSystemTime, } from "./timers.js";
|
|
19
|
+
import { doMock, unmock, listMockedSpecs, fromModule } from "./module.js";
|
|
20
|
+
import { fs as mockFs } from "./fs.js";
|
|
21
|
+
export { spy, spyOn, isSpy, mockObject, mockClass, stub, mockGlobal, mockFetch, mockFetchOnce, useFakeTimers, useRealTimers, advanceTimersByTime, advanceTimersByTimeAsync, runAllTimers, runAllTimersAsync, runOnlyPendingTimers, runOnlyPendingTimersAsync, getTimerCount, setSystemTime, getRealSystemTime, doMock, unmock, listMockedSpecs, fromModule, mockFs, clearAllMocks, resetAllMocks, restoreAllMocks, };
|
|
22
|
+
/**
|
|
23
|
+
* Unified mock namespace. Mirrors Jest / Vitest naming so existing test
|
|
24
|
+
* code reads naturally:
|
|
25
|
+
*
|
|
26
|
+
* mock.fn(impl?) // spy()
|
|
27
|
+
* mock.spyOn(obj, key)
|
|
28
|
+
* mock.object(obj) // auto-spy every method
|
|
29
|
+
* mock.class(Ctor) // auto-spy every method on instances
|
|
30
|
+
* mock.global(key, value) // stub on globalThis
|
|
31
|
+
* mock.stub(target, key, value)
|
|
32
|
+
* mock.clearAll()
|
|
33
|
+
* mock.resetAll()
|
|
34
|
+
* mock.restoreAll()
|
|
35
|
+
*/
|
|
36
|
+
export const mock = {
|
|
37
|
+
fn: spy,
|
|
38
|
+
spyOn,
|
|
39
|
+
object: mockObject,
|
|
40
|
+
class: mockClass,
|
|
41
|
+
global: mockGlobal,
|
|
42
|
+
stub,
|
|
43
|
+
fetch: mockFetch,
|
|
44
|
+
fetchOnce: mockFetchOnce,
|
|
45
|
+
useFakeTimers,
|
|
46
|
+
useRealTimers,
|
|
47
|
+
advanceTimersByTime,
|
|
48
|
+
advanceTimersByTimeAsync,
|
|
49
|
+
runAllTimers,
|
|
50
|
+
runAllTimersAsync,
|
|
51
|
+
runOnlyPendingTimers,
|
|
52
|
+
runOnlyPendingTimersAsync,
|
|
53
|
+
getTimerCount,
|
|
54
|
+
setSystemTime,
|
|
55
|
+
getRealSystemTime,
|
|
56
|
+
// Tier A module mocking - dynamic imports of external specs only.
|
|
57
|
+
// The hoisted `mock.module(spec, factory)` for static imports is
|
|
58
|
+
// planned for Tier B and intentionally not exposed yet.
|
|
59
|
+
doMock,
|
|
60
|
+
unmock,
|
|
61
|
+
fromModule,
|
|
62
|
+
listMockedSpecs,
|
|
63
|
+
// File system mocking - backed by memfs, layered on Tier A module
|
|
64
|
+
// mocking. Works with `await import("node:fs")`; static imports of
|
|
65
|
+
// node:fs are not intercepted yet (planned for Tier B).
|
|
66
|
+
fs: mockFs,
|
|
67
|
+
clearAll: clearAllMocks,
|
|
68
|
+
resetAll: resetAllMocks,
|
|
69
|
+
restoreAll: restoreAllMocks,
|
|
70
|
+
};
|