@unrift/core 0.3.0 → 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 +5 -0
- package/dist/esm/expect.d.ts +12 -0
- package/dist/esm/expect.d.ts.map +1 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/matchers.d.ts.map +1 -1
- package/dist/esm/matchers.js +161 -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.map +1 -1
- package/dist/esm/runner.js +13 -8
- package/dist/esm/test.d.ts.map +1 -1
- package/dist/esm/test.js +12 -5
- package/dist/esm/utils/compareEngine.js +3 -3
- package/dist/esm/utils/discoverTestFiles.js +1 -1
- package/dist/esm/utils/helpers.d.ts +10 -0
- package/dist/esm/utils/helpers.d.ts.map +1 -1
- package/dist/esm/utils/helpers.js +13 -0
- package/dist/esm/utils/toThrow.d.ts.map +1 -1
- package/dist/esm/utils/toThrow.js +2 -1
- package/package.json +4 -3
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node module customization hook for unrift module mocking (Tier A).
|
|
3
|
+
*
|
|
4
|
+
* Registered by `bin/unrift.mjs` via `module.register()`. Runs in a
|
|
5
|
+
* worker thread (Node 20.6+ / 18.19+ behavior). Communicates with the
|
|
6
|
+
* main thread via a `MessagePort` to learn which specs have been mocked.
|
|
7
|
+
*
|
|
8
|
+
* Why we use `receiveMessageOnPort()` instead of `port.on("message")`:
|
|
9
|
+
* the resolve hook runs synchronously from the perspective of the
|
|
10
|
+
* import pipeline, but Worker `MessagePort` listeners are async.
|
|
11
|
+
* `receiveMessageOnPort()` (Node 18.7+) drains pending messages
|
|
12
|
+
* synchronously so registrations posted from the main thread are
|
|
13
|
+
* visible by the time the corresponding `await import(spec)` reaches
|
|
14
|
+
* us.
|
|
15
|
+
*
|
|
16
|
+
* The generated synthetic module source runs back in the MAIN thread
|
|
17
|
+
* (Node's loader output is evaluated in the importing thread, not the
|
|
18
|
+
* loader thread). That source reads exports from `globalThis.__unriftMocks`,
|
|
19
|
+
* which the main thread populates in `mock.doMock()`.
|
|
20
|
+
*
|
|
21
|
+
* Source compiled by tsc and shipped at `dist/esm/mock/module-loader.js`.
|
|
22
|
+
*/
|
|
23
|
+
import { type MessagePort } from "node:worker_threads";
|
|
24
|
+
interface InitializeData {
|
|
25
|
+
port: MessagePort;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Generate ESM source for a mocked spec. The source executes in the
|
|
29
|
+
* MAIN thread (not the loader thread) - that's where the actual mock
|
|
30
|
+
* object lives, keyed in `globalThis.__unriftMocks`.
|
|
31
|
+
*
|
|
32
|
+
* Pure function so future tiers can reuse this for hoisted (Tier B)
|
|
33
|
+
* and relative (Tier C) mocks without rewriting the emitter.
|
|
34
|
+
*/
|
|
35
|
+
export declare function generateMockSource(spec: string, exportKeys: readonly string[]): string;
|
|
36
|
+
/** Node hook: called once when the loader is registered. */
|
|
37
|
+
export declare function initialize(data: InitializeData): void;
|
|
38
|
+
interface ResolveContext {
|
|
39
|
+
conditions: readonly string[];
|
|
40
|
+
importAttributes?: Record<string, string>;
|
|
41
|
+
parentURL?: string;
|
|
42
|
+
}
|
|
43
|
+
interface ResolveResult {
|
|
44
|
+
url: string;
|
|
45
|
+
format?: string;
|
|
46
|
+
shortCircuit?: boolean;
|
|
47
|
+
importAttributes?: Record<string, string>;
|
|
48
|
+
}
|
|
49
|
+
type DefaultResolve = (specifier: string, context: ResolveContext) => ResolveResult | Promise<ResolveResult>;
|
|
50
|
+
/** Node hook: short-circuit mocked specs to a `unrift-mock:` URL. */
|
|
51
|
+
export declare function resolve(specifier: string, context: ResolveContext, nextResolve: DefaultResolve): Promise<ResolveResult>;
|
|
52
|
+
interface LoadContext {
|
|
53
|
+
format?: string;
|
|
54
|
+
importAttributes?: Record<string, string>;
|
|
55
|
+
conditions?: readonly string[];
|
|
56
|
+
}
|
|
57
|
+
interface LoadResult {
|
|
58
|
+
format: string;
|
|
59
|
+
source?: string | ArrayBuffer | Uint8Array;
|
|
60
|
+
shortCircuit?: boolean;
|
|
61
|
+
}
|
|
62
|
+
type DefaultLoad = (url: string, context: LoadContext) => LoadResult | Promise<LoadResult>;
|
|
63
|
+
/** Node hook: emit synthetic ESM source for mocked specs. */
|
|
64
|
+
export declare function load(url: string, context: LoadContext, nextLoad: DefaultLoad): Promise<LoadResult>;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=module-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module-loader.d.ts","sourceRoot":"","sources":["../../../src/mock/module-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAwB,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAQ7E,UAAU,cAAc;IACtB,IAAI,EAAE,WAAW,CAAC;CACnB;AAmCD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,SAAS,MAAM,EAAE,GAC5B,MAAM,CAuBR;AAED,4DAA4D;AAC5D,wBAAgB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAErD;AAED,UAAU,cAAc;IACtB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,aAAa;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;AAED,KAAK,cAAc,GAAG,CACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,KACpB,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;AAE5C,qEAAqE;AACrE,wBAAsB,OAAO,CAC3B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,EACvB,WAAW,EAAE,cAAc,GAC1B,OAAO,CAAC,aAAa,CAAC,CAexB;AAED,UAAU,WAAW;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAED,UAAU,UAAU;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;IAC3C,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,KAAK,WAAW,GAAG,CACjB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,WAAW,KACjB,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,6DAA6D;AAC7D,wBAAsB,IAAI,CACxB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,WAAW,GACpB,OAAO,CAAC,UAAU,CAAC,CA0BrB"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node module customization hook for unrift module mocking (Tier A).
|
|
3
|
+
*
|
|
4
|
+
* Registered by `bin/unrift.mjs` via `module.register()`. Runs in a
|
|
5
|
+
* worker thread (Node 20.6+ / 18.19+ behavior). Communicates with the
|
|
6
|
+
* main thread via a `MessagePort` to learn which specs have been mocked.
|
|
7
|
+
*
|
|
8
|
+
* Why we use `receiveMessageOnPort()` instead of `port.on("message")`:
|
|
9
|
+
* the resolve hook runs synchronously from the perspective of the
|
|
10
|
+
* import pipeline, but Worker `MessagePort` listeners are async.
|
|
11
|
+
* `receiveMessageOnPort()` (Node 18.7+) drains pending messages
|
|
12
|
+
* synchronously so registrations posted from the main thread are
|
|
13
|
+
* visible by the time the corresponding `await import(spec)` reaches
|
|
14
|
+
* us.
|
|
15
|
+
*
|
|
16
|
+
* The generated synthetic module source runs back in the MAIN thread
|
|
17
|
+
* (Node's loader output is evaluated in the importing thread, not the
|
|
18
|
+
* loader thread). That source reads exports from `globalThis.__unriftMocks`,
|
|
19
|
+
* which the main thread populates in `mock.doMock()`.
|
|
20
|
+
*
|
|
21
|
+
* Source compiled by tsc and shipped at `dist/esm/mock/module-loader.js`.
|
|
22
|
+
*/
|
|
23
|
+
import { receiveMessageOnPort } from "node:worker_threads";
|
|
24
|
+
import { MOCKS_GLOBAL_KEY, MOCK_URL_PREFIX, } from "./module.js";
|
|
25
|
+
// Loader-thread state. Populated by `initialize()` and updated by
|
|
26
|
+
// drainMessages() on every resolve() call.
|
|
27
|
+
let loaderPort;
|
|
28
|
+
const registered = new Map();
|
|
29
|
+
/**
|
|
30
|
+
* Pull every pending message off the port synchronously. Called at the
|
|
31
|
+
* top of every resolve() and load() hook so the loader's view of the
|
|
32
|
+
* registry is always current.
|
|
33
|
+
*/
|
|
34
|
+
function drainMessages() {
|
|
35
|
+
if (!loaderPort)
|
|
36
|
+
return;
|
|
37
|
+
let received = receiveMessageOnPort(loaderPort);
|
|
38
|
+
while (received !== undefined) {
|
|
39
|
+
const message = received.message;
|
|
40
|
+
if (message.type === "register") {
|
|
41
|
+
registered.set(message.spec, {
|
|
42
|
+
registrationId: message.registrationId,
|
|
43
|
+
exportKeys: message.exportKeys,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
else if (message.type === "unregister") {
|
|
47
|
+
registered.delete(message.spec);
|
|
48
|
+
}
|
|
49
|
+
received = receiveMessageOnPort(loaderPort);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Generate ESM source for a mocked spec. The source executes in the
|
|
54
|
+
* MAIN thread (not the loader thread) - that's where the actual mock
|
|
55
|
+
* object lives, keyed in `globalThis.__unriftMocks`.
|
|
56
|
+
*
|
|
57
|
+
* Pure function so future tiers can reuse this for hoisted (Tier B)
|
|
58
|
+
* and relative (Tier C) mocks without rewriting the emitter.
|
|
59
|
+
*/
|
|
60
|
+
export function generateMockSource(spec, exportKeys) {
|
|
61
|
+
const specJson = JSON.stringify(spec);
|
|
62
|
+
const lines = [
|
|
63
|
+
`// Generated by unrift module mocking - do not edit.`,
|
|
64
|
+
`const __mocks = globalThis[${JSON.stringify(MOCKS_GLOBAL_KEY)}];`,
|
|
65
|
+
`if (!__mocks || !__mocks.has(${specJson})) {`,
|
|
66
|
+
` throw new Error(`,
|
|
67
|
+
` "Unrift module mock registry is missing the entry for " + ${specJson} + ". " +`,
|
|
68
|
+
` "This usually means mock.unmock() ran between registration and import."`,
|
|
69
|
+
` );`,
|
|
70
|
+
`}`,
|
|
71
|
+
`const __exports = __mocks.get(${specJson});`,
|
|
72
|
+
];
|
|
73
|
+
for (const key of exportKeys) {
|
|
74
|
+
if (key === "default") {
|
|
75
|
+
lines.push(`export default __exports.default;`);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
lines.push(`export const ${key} = __exports[${JSON.stringify(key)}];`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return lines.join("\n");
|
|
82
|
+
}
|
|
83
|
+
/** Node hook: called once when the loader is registered. */
|
|
84
|
+
export function initialize(data) {
|
|
85
|
+
loaderPort = data.port;
|
|
86
|
+
}
|
|
87
|
+
/** Node hook: short-circuit mocked specs to a `unrift-mock:` URL. */
|
|
88
|
+
export async function resolve(specifier, context, nextResolve) {
|
|
89
|
+
drainMessages();
|
|
90
|
+
const entry = registered.get(specifier);
|
|
91
|
+
if (entry) {
|
|
92
|
+
// Embed the registration ID in the URL so Node's URL-keyed module
|
|
93
|
+
// cache treats each re-mock of the same spec as a fresh module.
|
|
94
|
+
return {
|
|
95
|
+
url: `${MOCK_URL_PREFIX}${specifier}?v=${entry.registrationId}`,
|
|
96
|
+
format: "module",
|
|
97
|
+
shortCircuit: true,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return nextResolve(specifier, context);
|
|
101
|
+
}
|
|
102
|
+
/** Node hook: emit synthetic ESM source for mocked specs. */
|
|
103
|
+
export async function load(url, context, nextLoad) {
|
|
104
|
+
if (url.startsWith(MOCK_URL_PREFIX)) {
|
|
105
|
+
drainMessages();
|
|
106
|
+
// URL shape: `unrift-mock:<spec>?v=<registrationId>`
|
|
107
|
+
const withoutPrefix = url.slice(MOCK_URL_PREFIX.length);
|
|
108
|
+
const queryIdx = withoutPrefix.indexOf("?");
|
|
109
|
+
const spec = queryIdx === -1 ? withoutPrefix : withoutPrefix.slice(0, queryIdx);
|
|
110
|
+
const entry = registered.get(spec);
|
|
111
|
+
if (!entry) {
|
|
112
|
+
// The user called mock.unmock() between resolve() and load().
|
|
113
|
+
// Fall through to the real module so the import still works.
|
|
114
|
+
return nextLoad(spec, context);
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
format: "module",
|
|
118
|
+
source: generateMockSource(spec, entry.exportKeys),
|
|
119
|
+
shortCircuit: true,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return nextLoad(url, context);
|
|
123
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module mocking - main-thread registry and public API (Tier A).
|
|
3
|
+
*
|
|
4
|
+
* Tier A scope:
|
|
5
|
+
* - `mock.doMock(spec, factory)` registers a mock for an external module
|
|
6
|
+
* specifier (bare package name or `node:*` built-in).
|
|
7
|
+
* - Subsequent `await import(spec)` calls return the factory's result.
|
|
8
|
+
* - Static imports are NOT supported in Tier A; see the docs / ISSUES.md
|
|
9
|
+
* for the planned Tier B (hoisted `mock.module`) work.
|
|
10
|
+
*
|
|
11
|
+
* Architecture:
|
|
12
|
+
* - bin/unrift.mjs registers `module-loader.js` via `module.register()` and
|
|
13
|
+
* pipes a MessagePort into the loader thread.
|
|
14
|
+
* - The main-side port is stashed on `globalThis.__unriftLoaderPort` before
|
|
15
|
+
* the test runner starts.
|
|
16
|
+
* - `doMock()` invokes the factory once, caches the result on
|
|
17
|
+
* `globalThis.__unriftMocks`, and posts a `register` message with the
|
|
18
|
+
* spec + export-key list to the loader.
|
|
19
|
+
* - The loader (see module-loader.ts) drains pending messages on every
|
|
20
|
+
* resolve() and short-circuits matched specs to a synthetic module that
|
|
21
|
+
* reads back from `globalThis.__unriftMocks`.
|
|
22
|
+
*
|
|
23
|
+
* Future tiers will reuse the same registry shape and IPC protocol -
|
|
24
|
+
* adding `register-hoisted` (Tier B) and `register-relative` (Tier C)
|
|
25
|
+
* message types without breaking the existing surface.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Sentinel field name on globalThis. Loader-generated synthetic source
|
|
29
|
+
* reads back through this same name, so it must stay stable.
|
|
30
|
+
*/
|
|
31
|
+
export declare const MOCKS_GLOBAL_KEY = "__unriftMocks";
|
|
32
|
+
/** Sentinel field name for the IPC port set by bin/unrift.mjs. */
|
|
33
|
+
export declare const LOADER_PORT_GLOBAL_KEY = "__unriftLoaderPort";
|
|
34
|
+
/** URL prefix the loader uses for mocked modules. Public so tests can assert. */
|
|
35
|
+
export declare const MOCK_URL_PREFIX = "unrift-mock:";
|
|
36
|
+
/**
|
|
37
|
+
* Message contract between main thread and loader thread. Stable across
|
|
38
|
+
* future tiers - new message types extend this union.
|
|
39
|
+
*
|
|
40
|
+
* `registrationId` is a monotonically increasing number embedded in the
|
|
41
|
+
* synthetic mock URL. It defeats Node's URL-keyed module cache so that
|
|
42
|
+
* re-mocking the same spec in a later test yields a fresh module (rather
|
|
43
|
+
* than returning the previous test's cached mock).
|
|
44
|
+
*/
|
|
45
|
+
export type LoaderMessage = {
|
|
46
|
+
type: "register";
|
|
47
|
+
spec: string;
|
|
48
|
+
registrationId: number;
|
|
49
|
+
exportKeys: readonly string[];
|
|
50
|
+
} | {
|
|
51
|
+
type: "unregister";
|
|
52
|
+
spec: string;
|
|
53
|
+
};
|
|
54
|
+
type ModuleFactory = () => Record<string, unknown>;
|
|
55
|
+
/**
|
|
56
|
+
* Register a mock for `spec`. Subsequent `await import(spec)` calls will
|
|
57
|
+
* resolve to the factory's return value instead of the real module.
|
|
58
|
+
*
|
|
59
|
+
* Tier A constraints (planned to be lifted in Tier B and beyond):
|
|
60
|
+
* - Only **dynamic** imports (`await import(spec)`) hit the mock; static
|
|
61
|
+
* `import { x } from "spec"` is hoisted by ESM above this call and
|
|
62
|
+
* will see the real module.
|
|
63
|
+
* - Only **external** specifiers work - bare package names (`"react"`)
|
|
64
|
+
* and Node built-ins (`"node:fs"`). Relative specs (`"./bar"`) are
|
|
65
|
+
* bundled inline by esbuild and can't be intercepted at runtime.
|
|
66
|
+
* - The factory is invoked **once** at registration time. Its return
|
|
67
|
+
* value is reused for every subsequent import.
|
|
68
|
+
*
|
|
69
|
+
* Throws if the Unrift CLI loader isn't attached (i.e., tests were run
|
|
70
|
+
* with raw `node` instead of `unrift`).
|
|
71
|
+
*/
|
|
72
|
+
export declare function doMock(spec: string, factory: ModuleFactory): void;
|
|
73
|
+
/**
|
|
74
|
+
* Remove a previously registered mock. Subsequent `await import(spec)`
|
|
75
|
+
* calls fall through to the real module.
|
|
76
|
+
*/
|
|
77
|
+
export declare function unmock(spec: string): void;
|
|
78
|
+
/** Returns the current set of mocked specs. Useful for tests / debugging. */
|
|
79
|
+
export declare function listMockedSpecs(): readonly string[];
|
|
80
|
+
/**
|
|
81
|
+
* Convenience helper: imports the real module, builds a version with every
|
|
82
|
+
* function export wrapped in a spy, registers it via `mock.doMock`, and
|
|
83
|
+
* returns the mocked exports.
|
|
84
|
+
*
|
|
85
|
+
* Non-function exports are passed through unchanged. Tests can customize
|
|
86
|
+
* individual spies with `.mockReturnValue`, `.mockImplementation`, etc.
|
|
87
|
+
*
|
|
88
|
+
* Subject to the same Tier A limits as `doMock` - only affects
|
|
89
|
+
* `await import(spec)` after the call. Static imports of `spec` see the
|
|
90
|
+
* real module.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* const fs = await mock.fromModule("node:fs");
|
|
94
|
+
* fs.readFileSync.mockReturnValue("fake content");
|
|
95
|
+
* // ... test code that does `await import("node:fs")` now gets the spies
|
|
96
|
+
*/
|
|
97
|
+
export declare function fromModule<T extends Record<string, unknown>>(spec: string): Promise<T>;
|
|
98
|
+
export {};
|
|
99
|
+
//# sourceMappingURL=module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../src/mock/module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AASH;;;GAGG;AACH,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAEhD,kEAAkE;AAClE,eAAO,MAAM,sBAAsB,uBAAuB,CAAC;AAE3D,iFAAiF;AACjF,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAE9C;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GACrB;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B,GACD;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,KAAK,aAAa,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAwFnD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI,CAqCjE;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAQzC;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,IAAI,SAAS,MAAM,EAAE,CAEnD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChE,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,CAAC,CAAC,CAyBZ"}
|