@exodus/test 1.0.0-rc.74 → 1.0.0-rc.76
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/index.js +6 -2
- package/bundler/modules/globals.node.cjs +6 -0
- package/package.json +12 -9
- package/src/engine.pure.cjs +14 -9
- package/src/exodus.js +1 -1
- package/src/expect.cjs +50 -15
- package/src/jest.js +10 -2
- package/src/jest.mock.js +15 -7
- package/src/jest.timers.js +29 -9
- package/src/version.js +2 -2
package/bin/index.js
CHANGED
|
@@ -229,7 +229,6 @@ function parseOptions() {
|
|
|
229
229
|
|
|
230
230
|
const isTTY = process.stdout.isTTY
|
|
231
231
|
const isCI = process.env.CI
|
|
232
|
-
const { options, patterns } = parseOptions()
|
|
233
232
|
const warnHuman = isTTY && !isCI ? (...args) => console.warn(...args) : () => {}
|
|
234
233
|
if (isCI) process.env.FORCE_COLOR = '1' // should support colored output even though not a TTY, overridable with --no-color
|
|
235
234
|
|
|
@@ -239,6 +238,11 @@ const setEnv = (name, value) => {
|
|
|
239
238
|
process.env[name] = value === undefined ? '' : value
|
|
240
239
|
}
|
|
241
240
|
|
|
241
|
+
const { options, patterns } = parseOptions()
|
|
242
|
+
if (!process.env.FORCE_COLOR && process.stdout.hasColors?.() && process.stderr.hasColors?.()) {
|
|
243
|
+
setEnv('FORCE_COLOR', '1') // Default to color output for subprocesses if our stream supports it
|
|
244
|
+
}
|
|
245
|
+
|
|
242
246
|
const engineName = `${options.engine} engine` // used for warnings to user
|
|
243
247
|
const engineOptions = ENGINES.get(options.engine)
|
|
244
248
|
assert(engineOptions, `Unknown engine: ${options.engine}`)
|
|
@@ -565,7 +569,7 @@ async function launch(binary, args, opts = {}, buffering = false) {
|
|
|
565
569
|
if (options.pure) {
|
|
566
570
|
setEnv('EXODUS_TEST_CONTEXT', 'pure')
|
|
567
571
|
warnHuman(`${engineName} is experimental and may not work an expected`)
|
|
568
|
-
const missUnhandled = ['jsc'
|
|
572
|
+
const missUnhandled = ['jsc'].includes(options.platform) || options.browsers
|
|
569
573
|
if (missUnhandled) warnHuman(`Warning: ${engineName} does not have unhandled rejections tracking`)
|
|
570
574
|
|
|
571
575
|
const runOne = async (inputFile) => {
|
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
// A slimmed-down version of globals.cjs specifically for Node.js bundle
|
|
2
2
|
Object.assign(process, { argv: process.argv })
|
|
3
|
+
|
|
4
|
+
if (!globalThis.crypto) {
|
|
5
|
+
// Old Node.js, we polyfill it as our bundler polyfills crypto module using webcrypto RNG
|
|
6
|
+
const r = require // prevent embed
|
|
7
|
+
globalThis.crypto = r('node:crypto').webcrypto
|
|
8
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/test",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.76",
|
|
4
4
|
"author": "Exodus Movement, Inc.",
|
|
5
5
|
"description": "A test suite runner",
|
|
6
6
|
"homepage": "https://github.com/ExodusMovement/test",
|
|
@@ -100,26 +100,28 @@
|
|
|
100
100
|
"CHANGELOG.md"
|
|
101
101
|
],
|
|
102
102
|
"scripts": {
|
|
103
|
-
"test:_pure": "EXODUS_TEST_IGNORE='tests/jest-extended/**' npm run test",
|
|
104
103
|
"test:_bundle": "EXODUS_TEST_IGNORE='tests/{{jest-extended,inband}/**,jest-when/when.test.*,jest/jest.resetModules.*}' npm run test --",
|
|
105
104
|
"test": "npm run test:jest --",
|
|
106
|
-
"test:all": "npm run test:jest && npm run test:tape && npm run test:native && npm run test:pure && npm run test:typescript && npm run test:fetch && npm run test:jsdom",
|
|
105
|
+
"test:all": "npm run test:jest && npm run test:tape && npm run test:native && npm run test:pure && npm run test:typescript && npm run test:fetch && npm run test:jsdom && npm run test:bundle",
|
|
107
106
|
"test:native": "EXODUS_TEST_IGNORE='{**/typescript/**,**/jest-repo/**/user.test.js}' ./bin/index.js --jest 'tests/**/*.test.{js,cjs,mjs}'",
|
|
108
107
|
"test:typescript": "./bin/index.js --jest --typescript tests/typescript.test.ts",
|
|
109
108
|
"test:jest": "./bin/index.js --jest --esbuild",
|
|
110
|
-
"test:tape": "./bin/index.js
|
|
111
|
-
"test:pure": "EXODUS_TEST_ENGINE=node:pure npm run test
|
|
112
|
-
"test:
|
|
109
|
+
"test:tape": "./bin/index.js 'tests/tape/tests/*.js' tests/tape.test.js",
|
|
110
|
+
"test:pure": "EXODUS_TEST_ENGINE=node:pure npm run test --",
|
|
111
|
+
"test:bundle": "EXODUS_TEST_ENGINE=node:bundle npm run test:_bundle --",
|
|
112
|
+
"test:bun:pure": "EXODUS_TEST_ENGINE=bun:pure npm run test --",
|
|
113
|
+
"test:bun:bundle": "EXODUS_TEST_ENGINE=bun:bundle npm run test:_bundle",
|
|
113
114
|
"test:deno": "EXODUS_TEST_ENGINE=deno:bundle npm run test:_bundle --",
|
|
114
115
|
"test:electron": "EXODUS_TEST_ENGINE=electron-as-node:test npm run test",
|
|
115
|
-
"test:electron:pure": "EXODUS_TEST_ENGINE=electron-as-node:pure npm run test
|
|
116
|
+
"test:electron:pure": "EXODUS_TEST_ENGINE=electron-as-node:pure npm run test --",
|
|
116
117
|
"test:electron:bundle": "EXODUS_TEST_ENGINE=electron-as-node:bundle npm run test:_bundle",
|
|
117
118
|
"test:chrome:puppeteer": "EXODUS_TEST_ENGINE=chrome:puppeteer npm run test:_bundle --",
|
|
118
119
|
"test:firefox:puppeteer": "EXODUS_TEST_ENGINE=firefox:puppeteer npm run test:_bundle --",
|
|
119
120
|
"test:chromium:playwright": "EXODUS_TEST_ENGINE=chromium:playwright npm run test:_bundle --",
|
|
120
121
|
"test:firefox:playwright": "EXODUS_TEST_ENGINE=firefox:playwright npm run test:_bundle --",
|
|
121
122
|
"test:webkit:playwright": "EXODUS_TEST_ENGINE=webkit:playwright npm run test:_bundle --",
|
|
122
|
-
"test:
|
|
123
|
+
"test:v8": "npm run test:d8 --",
|
|
124
|
+
"test:javascriptcore": "npm run test:jsc --",
|
|
123
125
|
"test:d8": "EXODUS_TEST_ENGINE=d8:bundle npm run test:_bundle --",
|
|
124
126
|
"test:jsc": "EXODUS_TEST_ENGINE=jsc:bundle npm run test:_bundle --",
|
|
125
127
|
"test:hermes": "EXODUS_TEST_ENGINE=hermes:bundle npm run test:_bundle --",
|
|
@@ -129,6 +131,7 @@
|
|
|
129
131
|
"test:fetch": "./bin/index.js --jest --drop-network --engine node:pure tests/fetch.test.js tests/websocket.test.js",
|
|
130
132
|
"test:jsdom": "EXODUS_TEST_JEST_CONFIG='{\"testMatch\":[\"**/*.jsdom-test.js\"],\"testEnvironment\":\"jsdom\", \"rootDir\": \".\"}' ./bin/index.js --jest",
|
|
131
133
|
"coverage": "./bin/index.js --jest --esbuild --coverage",
|
|
134
|
+
"playwright": "./bin/index.js --playwright",
|
|
132
135
|
"jsvu": "jsvu",
|
|
133
136
|
"jest": "NODE_OPTIONS=--experimental-vm-modules jest tests/jest/ tests/jest-when/",
|
|
134
137
|
"lint": "prettier --list-different . && eslint .",
|
|
@@ -178,7 +181,7 @@
|
|
|
178
181
|
"@jest/globals": "^29.7.0",
|
|
179
182
|
"@types/jest-when": "^3.5.2",
|
|
180
183
|
"@typescript-eslint/eslint-plugin": "^7.15.0",
|
|
181
|
-
"electron": "^35.
|
|
184
|
+
"electron": "^35.2.2",
|
|
182
185
|
"eslint": "^8.44.0",
|
|
183
186
|
"jest": "^29.7.0",
|
|
184
187
|
"jest-matcher-utils": "^29.7.0",
|
package/src/engine.pure.cjs
CHANGED
|
@@ -308,19 +308,23 @@ class MockTimers {
|
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
#microtick() {
|
|
311
|
-
const next =
|
|
312
|
-
this.#queue.find((x) => x.runAt === -1) || // immediates are first
|
|
313
|
-
this.#queue.find((x) => x.runAt <= this.#elapsed)
|
|
311
|
+
const next = this.#queue.find((x) => x.runAt <= this.#elapsed) // sorted
|
|
314
312
|
if (!next) return null
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
} else {
|
|
313
|
+
this.#queue = this.#queue.filter((x) => x !== next)
|
|
314
|
+
if (next.interval !== undefined) {
|
|
318
315
|
next.runAt += next.interval
|
|
316
|
+
this.#schedule(next)
|
|
319
317
|
}
|
|
320
318
|
|
|
321
319
|
next.callback(...next.args)
|
|
322
320
|
}
|
|
323
321
|
|
|
322
|
+
#schedule(entry) {
|
|
323
|
+
const before = this.#queue.findIndex((x) => x.runAt > entry.runAt)
|
|
324
|
+
if (before === -1) return this.#queue.push(entry)
|
|
325
|
+
this.#queue.splice(before, 0, entry)
|
|
326
|
+
}
|
|
327
|
+
|
|
324
328
|
runAll() {
|
|
325
329
|
this.tick(Math.max(0, ...this.#queue.map((x) => x.runAt - this.#elapsed)))
|
|
326
330
|
}
|
|
@@ -331,19 +335,19 @@ class MockTimers {
|
|
|
331
335
|
|
|
332
336
|
#setTimeout(callback, delay, ...args) {
|
|
333
337
|
const id = { callback, runAt: delay + this.#elapsed, args }
|
|
334
|
-
this.#
|
|
338
|
+
this.#schedule(id)
|
|
335
339
|
return id
|
|
336
340
|
}
|
|
337
341
|
|
|
338
342
|
#setInterval(callback, delay, ...args) {
|
|
339
343
|
const id = { callback, runAt: delay + this.#elapsed, interval: delay, args }
|
|
340
|
-
this.#
|
|
344
|
+
this.#schedule(id)
|
|
341
345
|
return id
|
|
342
346
|
}
|
|
343
347
|
|
|
344
348
|
#setImmediate(callback, ...args) {
|
|
345
349
|
const id = { callback, runAt: -1, args }
|
|
346
|
-
this.#
|
|
350
|
+
this.#schedule(id)
|
|
347
351
|
return id
|
|
348
352
|
}
|
|
349
353
|
|
|
@@ -462,6 +466,7 @@ const isPromise = (x) => Boolean(x && x.then && x.catch && x.finally)
|
|
|
462
466
|
const nodeVersion = '9999.99.99'
|
|
463
467
|
const awaitForMicrotaskQueue = async () => {
|
|
464
468
|
if (globalThis?.process?.nextTick) {
|
|
469
|
+
if (globalThis.Bun) await Promise.resolve() // No idea what's up with Bun microtasks
|
|
465
470
|
// We are in microtasks, awaiting for "next" tick will get us out of here
|
|
466
471
|
return new Promise((resolve) => globalThis.process.nextTick(resolve))
|
|
467
472
|
}
|
package/src/exodus.js
CHANGED
|
@@ -15,8 +15,8 @@ export const exodus = {
|
|
|
15
15
|
timers: Boolean(mock.timers && haveValidTimers),
|
|
16
16
|
dynamicRequire: Boolean(!isBundle), // require(non-literal-non-glob), createRequire()(non-builtin)
|
|
17
17
|
esmMocks: Boolean(mock.module || isBundle), // support for ESM mocks
|
|
18
|
+
esmNamedBuiltinMocks: Boolean(mock.module || isBundle || insideEsbuild), // support for named ESM imports from builtin module mocks: also fine in --esbuild
|
|
18
19
|
esmInterop: Boolean(insideEsbuild && !isBundle), // loading/using ESM as CJS, ESM mocks creation without a mocker function
|
|
19
|
-
esmNamedBuiltinMocks: Boolean(mock.module || insideEsbuild || isBundle), // support for named ESM imports from builtin module mocks
|
|
20
20
|
concurrency: node.engine !== 'pure', // pure engine doesn't support concurrency
|
|
21
21
|
},
|
|
22
22
|
mock: {
|
package/src/expect.cjs
CHANGED
|
@@ -11,9 +11,11 @@ function fixupAssertions() {
|
|
|
11
11
|
assertionsDelta = 0
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
15
|
+
function loadExpect(loadReason) {
|
|
15
16
|
if (expect) return expect
|
|
16
17
|
expect = require('expect').expect
|
|
18
|
+
// console.log('expect load reason:', loadReason)
|
|
17
19
|
const matchers = require('jest-extended')
|
|
18
20
|
expect.extend(matchers)
|
|
19
21
|
for (const x of extend) expect.extend(...x)
|
|
@@ -26,7 +28,7 @@ const areNumeric = (...args) => args.every((a) => typeof a === 'number' || typeo
|
|
|
26
28
|
|
|
27
29
|
const matchers = {
|
|
28
30
|
__proto__: null,
|
|
29
|
-
toBe: (x, y) => x
|
|
31
|
+
toBe: (x, y) => Object.is(x, y),
|
|
30
32
|
toBeNull: (x) => x === null,
|
|
31
33
|
toBeTruthy: (x) => x,
|
|
32
34
|
toBeFalsy: (x) => !x,
|
|
@@ -53,42 +55,76 @@ const matchers = {
|
|
|
53
55
|
|
|
54
56
|
const matchersFalseNegative = {
|
|
55
57
|
__proto__: null,
|
|
56
|
-
toEqual: (x, y) => x
|
|
57
|
-
toStrictEqual: (x, y) => x
|
|
58
|
+
toEqual: (x, y) => Object.is(x, y),
|
|
59
|
+
toStrictEqual: (x, y) => Object.is(x, y),
|
|
58
60
|
toContain: (x, c) => Array.isArray(x) && [...x].includes(c),
|
|
59
61
|
toBeEven: (x) => Number.isSafeInteger(x) && x % 2 === 0,
|
|
60
62
|
toBeOdd: (x) => Number.isSafeInteger(x) && x % 2 === 1,
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
const doesNotThrow = (x) => {
|
|
66
|
+
try {
|
|
67
|
+
x()
|
|
68
|
+
return [true]
|
|
69
|
+
} catch (err) {
|
|
70
|
+
return [false, err]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
63
74
|
function createExpect() {
|
|
64
75
|
return new Proxy(() => {}, {
|
|
65
76
|
apply: (target, that, [x, ...rest]) => {
|
|
66
|
-
if (rest.length > 0) return loadExpect()(x, ...rest)
|
|
77
|
+
if (rest.length > 0) return loadExpect('rest')(x, ...rest)
|
|
67
78
|
return new Proxy(Object.create(null), {
|
|
68
79
|
get: (_, name) => {
|
|
69
80
|
const matcher = matchers[name] || matchersFalseNegative[name]
|
|
70
|
-
if (matcher)
|
|
81
|
+
if (matcher) {
|
|
82
|
+
return (...args) => {
|
|
83
|
+
if (!matcher(x, ...args)) return loadExpect(`.${name} fail`)(x)[name](...args)
|
|
84
|
+
assertionsDelta++
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (name === 'toThrow') {
|
|
71
89
|
return (...args) => {
|
|
72
|
-
if (
|
|
90
|
+
if (args.length > 0) return loadExpect('.toThrow args')(x)[name](...args)
|
|
91
|
+
const [passed] = doesNotThrow(x)
|
|
92
|
+
if (passed) return loadExpect('.toThrow fail')(() => {})[name](...args)
|
|
73
93
|
assertionsDelta++
|
|
74
94
|
}
|
|
95
|
+
}
|
|
75
96
|
|
|
76
97
|
if (name === 'not')
|
|
77
98
|
return new Proxy(Object.create(null), {
|
|
78
99
|
get: (_, not) => {
|
|
79
|
-
if (
|
|
100
|
+
if (not === 'toThrow') {
|
|
80
101
|
return (...args) => {
|
|
81
|
-
|
|
102
|
+
const [passed, err] = doesNotThrow(x)
|
|
103
|
+
if (!passed) {
|
|
104
|
+
return loadExpect('.not.toThrow fail')(() => {
|
|
105
|
+
throw err
|
|
106
|
+
}).not.toThrow(...args)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
assertionsDelta++
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (matchers[not]) {
|
|
114
|
+
return (...args) => {
|
|
115
|
+
if (matchers[not](x, ...args)) {
|
|
116
|
+
return loadExpect(`.not.${not} fail`)(x).not[not](...args)
|
|
117
|
+
}
|
|
118
|
+
|
|
82
119
|
assertionsDelta++
|
|
83
120
|
}
|
|
121
|
+
}
|
|
84
122
|
|
|
85
|
-
|
|
86
|
-
return loadExpect()(x).not[not]
|
|
123
|
+
return loadExpect(`.not.${not}`)(x).not[not]
|
|
87
124
|
},
|
|
88
125
|
})
|
|
89
126
|
|
|
90
|
-
|
|
91
|
-
return loadExpect()(x)[name]
|
|
127
|
+
return loadExpect(`.${name}`)(x)[name]
|
|
92
128
|
},
|
|
93
129
|
})
|
|
94
130
|
},
|
|
@@ -106,8 +142,7 @@ function createExpect() {
|
|
|
106
142
|
}
|
|
107
143
|
}
|
|
108
144
|
|
|
109
|
-
|
|
110
|
-
return loadExpect()[name]
|
|
145
|
+
return loadExpect(`get ${name}`)[name]
|
|
111
146
|
},
|
|
112
147
|
set: (_, name, value) => {
|
|
113
148
|
if (expect) {
|
package/src/jest.js
CHANGED
|
@@ -46,14 +46,22 @@ function parseArgs(list, targs) {
|
|
|
46
46
|
return result
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
// Hack for common testing with simple arrow functions
|
|
50
|
+
const formatArg = (x) => {
|
|
51
|
+
if (x && x instanceof Function) {
|
|
52
|
+
if (`${x}` === '()=>{}') return '() => {}' // likely minified by esbuild
|
|
53
|
+
if (globalThis.Bun && `${x}`.replace(/\s/g, '') === '()=>{}') return '() => {}' // Bun breaks formatting
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return x
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
const eachCallerLocation = []
|
|
50
60
|
const makeEach =
|
|
51
61
|
(impl) =>
|
|
52
62
|
(list, ...rest) =>
|
|
53
63
|
(template, fn, ...restArgs) => {
|
|
54
64
|
eachCallerLocation.unshift(getCallerLocation())
|
|
55
|
-
// Hack for common testing with simple arrow functions, until we can disable esbuild minification
|
|
56
|
-
const formatArg = (x) => (x && x instanceof Function && `${x}` === '()=>{}' ? '() => {}' : x)
|
|
57
65
|
// better than nothing
|
|
58
66
|
const printed = (x) =>
|
|
59
67
|
x && [null, Array.prototype, Object.prototype].includes(Object.getPrototypeOf(x))
|
package/src/jest.mock.js
CHANGED
|
@@ -190,7 +190,13 @@ function mockCloneItem(obj, cache) {
|
|
|
190
190
|
for (let c = obj; c && c !== Object.prototype; c = Object.getPrototypeOf(c)) stack.unshift(c)
|
|
191
191
|
let modified = stack.length > 1
|
|
192
192
|
for (const level of stack) {
|
|
193
|
-
|
|
193
|
+
const descriptors = Object.getOwnPropertyDescriptors(level)
|
|
194
|
+
const entries = Object.entries(descriptors)
|
|
195
|
+
for (const sym of [Symbol.toStringTag]) {
|
|
196
|
+
if (sym && Object.hasOwn(descriptors, sym)) entries.push([sym, descriptors[sym]]) // Missed by Object.entries
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
for (const [name, desc] of entries) {
|
|
194
200
|
if (name === 'constructor') continue
|
|
195
201
|
|
|
196
202
|
for (const key of ['get', 'set', 'value']) {
|
|
@@ -255,7 +261,7 @@ function jestmock(name, mocker, { override = false, actual, builtin } = {}) {
|
|
|
255
261
|
const value = mocker ? expand(mocker()) : mockClone(mapActual.get(resolved))
|
|
256
262
|
mapMocks.set(resolved, value)
|
|
257
263
|
|
|
258
|
-
loadExpect() // we need to do this as we don't want mocks affecting expect
|
|
264
|
+
loadExpect('jest.mock') // we need to do this as we don't want mocks affecting expect
|
|
259
265
|
|
|
260
266
|
if (process.env.EXODUS_TEST_ENVIRONMENT === 'bundle') {
|
|
261
267
|
if (builtin) globalThis.EXODUS_TEST_MOCK_BUILTINS.set(builtin, value)
|
|
@@ -274,7 +280,7 @@ function jestmock(name, mocker, { override = false, actual, builtin } = {}) {
|
|
|
274
280
|
|
|
275
281
|
const topLevelESM = isTopLevelESM()
|
|
276
282
|
let likelyESM = topLevelESM && !insideEsbuild && ![null, resolved].includes(resolveImport(name))
|
|
277
|
-
let
|
|
283
|
+
let isOverridenBuiltinSynchedWithESM = false
|
|
278
284
|
const isBuiltIn = builtinModules.includes(resolved)
|
|
279
285
|
const isNodeCache = (x) => x && x.id && x.path && x.filename && x.children && x.paths && x.loaded
|
|
280
286
|
if (isBuiltIn && !isNodeCache(require.cache[resolved])) {
|
|
@@ -288,7 +294,7 @@ function jestmock(name, mocker, { override = false, actual, builtin } = {}) {
|
|
|
288
294
|
overrideModule(resolved, true) // Override builtin modules
|
|
289
295
|
if (syncBuiltinESMExports) {
|
|
290
296
|
syncBuiltinESMExports()
|
|
291
|
-
|
|
297
|
+
isOverridenBuiltinSynchedWithESM = true
|
|
292
298
|
}
|
|
293
299
|
}
|
|
294
300
|
|
|
@@ -310,15 +316,17 @@ function jestmock(name, mocker, { override = false, actual, builtin } = {}) {
|
|
|
310
316
|
likelyESM = true
|
|
311
317
|
}
|
|
312
318
|
|
|
313
|
-
|
|
319
|
+
const mocksNodeVersionNote = 'mocks are available only on Node.js >=20.18 <21 || >=22.3'
|
|
320
|
+
if (likelyESM || (!isOverridenBuiltinSynchedWithESM && topLevelESM)) {
|
|
314
321
|
// Native module mocks is required if loading ESM or __from__ ESM
|
|
315
322
|
// No good way to check the locations that import the module, but we can check top-level file
|
|
316
323
|
// Built-in modules are fine though
|
|
317
|
-
assert(mock.module,
|
|
324
|
+
assert(mock.module, `ESM module ${mocksNodeVersionNote}`)
|
|
318
325
|
} else if (isBuiltIn && name.startsWith('node:') && !override) {
|
|
319
|
-
assert(mock.module,
|
|
326
|
+
assert(mock.module, `Native non-overriding node:* ${mocksNodeVersionNote}`)
|
|
320
327
|
}
|
|
321
328
|
|
|
329
|
+
if (value[Symbol.toStringTag] === 'Module') value.__esModule = true
|
|
322
330
|
const obj = { defaultExport: value }
|
|
323
331
|
if (isBuiltIn && isObject(value)) obj.namedExports = value
|
|
324
332
|
if (insideEsbuild) {
|
package/src/jest.timers.js
CHANGED
|
@@ -12,8 +12,15 @@ const warnOldTimers = () => {
|
|
|
12
12
|
console.warn('Warning: timer mocks are known to be glitchy before Node.js >=20.11.0')
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
let enabled = false
|
|
16
|
+
const assertEnabledTimers = () => {
|
|
17
|
+
assertHaveTimers()
|
|
18
|
+
assert(enabled, 'You should enable MockTimers first by calling useFakeTimers()')
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
export function useRealTimers() {
|
|
16
22
|
mock.timers?.reset()
|
|
23
|
+
enabled = false
|
|
17
24
|
return this
|
|
18
25
|
}
|
|
19
26
|
|
|
@@ -41,26 +48,39 @@ export function useFakeTimers({ doNotFake = doNotFakeDefault, ...rest } = {}) {
|
|
|
41
48
|
globalThis[name] = (id) => id && fn(id)
|
|
42
49
|
}
|
|
43
50
|
|
|
51
|
+
enabled = true
|
|
44
52
|
return this
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
export function runAllTimers() {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
mock.timers.tick(100_000_000_000) // > 3 years
|
|
56
|
+
assertEnabledTimers()
|
|
57
|
+
advanceTimersByTime(100_000_000_000) // > 3 years
|
|
51
58
|
return this
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
export function runOnlyPendingTimers() {
|
|
62
|
+
assertEnabledTimers()
|
|
55
63
|
assert(haveNoTimerInfiniteLoopBug, 'runOnlyPendingTimers requires Node.js >=20.11.0')
|
|
56
64
|
mock.timers.runAll()
|
|
57
65
|
return this
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
export function advanceTimersByTime(time) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
assert(Number.isSafeInteger(time) && time > 0)
|
|
70
|
+
assertEnabledTimers()
|
|
71
|
+
// We split this into multiple steps to run timers scheduled during the time we are running
|
|
72
|
+
const minSteps = Math.min(1000, time) // usually just split e.g. 5 seconds into 1000 * 5ms
|
|
73
|
+
const step = Number(Math.floor(time / minSteps).toPrecision(1))
|
|
74
|
+
const steps = Math.floor(time / step) // up to 2x higher than minSteps
|
|
75
|
+
const last = time - steps * step
|
|
76
|
+
// 1999 -> { step: 1, steps: 1999, last: 0 }
|
|
77
|
+
// 2001 -> { step: 2, steps: 1000, last: 1 }
|
|
78
|
+
for (let i = 0; i < steps; i++) {
|
|
79
|
+
if (!enabled) break // got disabled while looping
|
|
80
|
+
mock.timers.tick(step)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (last > 0 && enabled) mock.timers.tick(last)
|
|
64
84
|
return this
|
|
65
85
|
}
|
|
66
86
|
|
|
@@ -79,14 +99,13 @@ export async function runOnlyPendingTimersAsync() {
|
|
|
79
99
|
}
|
|
80
100
|
|
|
81
101
|
export async function advanceTimersByTimeAsync(time) {
|
|
82
|
-
|
|
83
|
-
warnOldTimers()
|
|
84
|
-
|
|
102
|
+
assertEnabledTimers()
|
|
85
103
|
if (mock.timers.tickAsync) {
|
|
86
104
|
await mock.timers.tickAsync(time)
|
|
87
105
|
} else {
|
|
88
106
|
for (let i = 0; i < time; i++) {
|
|
89
107
|
await awaitForMicrotaskQueue()
|
|
108
|
+
if (!enabled) break // got disabled while looping
|
|
90
109
|
mock.timers.tick(1)
|
|
91
110
|
}
|
|
92
111
|
}
|
|
@@ -96,6 +115,7 @@ export async function advanceTimersByTimeAsync(time) {
|
|
|
96
115
|
}
|
|
97
116
|
|
|
98
117
|
export function setSystemTime(time) {
|
|
118
|
+
assertEnabledTimers()
|
|
99
119
|
mock.timers.setTime(+time)
|
|
100
120
|
return this
|
|
101
121
|
}
|
package/src/version.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { assert, nodeVersion } from './engine.js'
|
|
2
2
|
|
|
3
3
|
const [major, minor, patch] = nodeVersion.split('.').map(Number)
|
|
4
|
-
assert(major !== 21, 'Node.js 21.x is deprecated!') // reached EOL, no reason to even test
|
|
5
4
|
// older versions are glitchy with before/after on top-level, which is a deal-breaker
|
|
6
5
|
// 20.7.0 is fine for node:test but broken with tsx, so we bump to 20.8.0
|
|
7
6
|
const ok = (major === 18 && minor >= 19) || (major === 20 && minor >= 8) || major >= 22
|
|
@@ -10,7 +9,8 @@ assert(major !== 22 || minor !== 3, 'Refusing to run on Node.js 22.3.0 specifica
|
|
|
10
9
|
|
|
11
10
|
export { major, minor, patch }
|
|
12
11
|
|
|
13
|
-
export const haveModuleMocks =
|
|
12
|
+
export const haveModuleMocks =
|
|
13
|
+
(major === 20 && minor >= 18) || (major === 22 && minor >= 3) || major > 22
|
|
14
14
|
export const haveSnapshots = (major === 22 && minor >= 3) || major > 22
|
|
15
15
|
export const haveSnapshotsReportUnescaped = (major === 22 && minor >= 5) || major > 22
|
|
16
16
|
export const haveForceExit = (major === 20 && minor > 13) || major >= 22
|