@exodus/test 1.0.0-rc.20 → 1.0.0-rc.21
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 +12 -11
- package/package.json +1 -1
- package/src/dark.cjs +29 -2
- package/src/jest.mock.js +31 -7
package/bin/index.js
CHANGED
|
@@ -141,16 +141,6 @@ if (options.coverage) {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
if (options.esbuild) {
|
|
145
|
-
assert(resolveImport)
|
|
146
|
-
args.push('--import', resolveImport('tsx'))
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (options.babel) {
|
|
150
|
-
assert(!options.esbuild, 'Options --babel and --esbuild are mutually exclusive')
|
|
151
|
-
args.push('-r', resolveRequire('./babel.cjs'))
|
|
152
|
-
}
|
|
153
|
-
|
|
154
144
|
const ignore = ['**/node_modules']
|
|
155
145
|
let filter
|
|
156
146
|
if (process.env.EXODUS_TEST_IGNORE) {
|
|
@@ -159,7 +149,8 @@ if (process.env.EXODUS_TEST_IGNORE) {
|
|
|
159
149
|
ignore.push(process.env.EXODUS_TEST_IGNORE)
|
|
160
150
|
}
|
|
161
151
|
|
|
162
|
-
//
|
|
152
|
+
// The comment below is disabled, we don't auto-mock @jest/globals anymore, and having our loader first is faster
|
|
153
|
+
// [Disabled] Our loader should be last, as enabling module mocks confuses other loaders
|
|
163
154
|
if (options.jest) {
|
|
164
155
|
const { loadJestConfig } = await import('../src/jest.config.js')
|
|
165
156
|
const config = await loadJestConfig(process.cwd())
|
|
@@ -196,6 +187,16 @@ if (options.jest) {
|
|
|
196
187
|
}
|
|
197
188
|
}
|
|
198
189
|
|
|
190
|
+
if (options.esbuild) {
|
|
191
|
+
assert(resolveImport)
|
|
192
|
+
args.push('--import', resolveImport('tsx'))
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (options.babel) {
|
|
196
|
+
assert(!options.esbuild, 'Options --babel and --esbuild are mutually exclusive')
|
|
197
|
+
args.push('-r', resolveRequire('./babel.cjs'))
|
|
198
|
+
}
|
|
199
|
+
|
|
199
200
|
if (patterns.length === 0) patterns.push(...DEFAULT_PATTERNS) // defaults
|
|
200
201
|
const globbed = await glob(patterns, { ignore })
|
|
201
202
|
const allfiles = filter ? globbed.filter(filter) : globbed
|
package/package.json
CHANGED
package/src/dark.cjs
CHANGED
|
@@ -4,7 +4,7 @@ const mayBeUrlToPath = (str) => (str.startsWith('file://') ? fileURLToPath(str)
|
|
|
4
4
|
|
|
5
5
|
let locForNextTest
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
const installLocationInNextTest = function (loc) {
|
|
8
8
|
locForNextTest = loc
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -107,4 +107,31 @@ function getTestNamePath(t) {
|
|
|
107
107
|
return [t.name] // last resort
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
function makeEsbuildMockable() {
|
|
111
|
+
const usingTsx = process.execArgv.some((x) => x.endsWith('node_modules/tsx/dist/loader.mjs'))
|
|
112
|
+
if (!usingTsx) return
|
|
113
|
+
// Hook into tsx/esbuild transpiled module conversion magic to make loaded modules mockable in runtime
|
|
114
|
+
// We want all modules to be .configurable = true, so we can override them
|
|
115
|
+
const defineProperty = Object.defineProperty
|
|
116
|
+
const obj = Object.create(null)
|
|
117
|
+
Object.defineProperty = (target, name, options) => {
|
|
118
|
+
if (options.get) {
|
|
119
|
+
const stackTraceLimit = Error.stackTraceLimit
|
|
120
|
+
Error.stackTraceLimit = 2
|
|
121
|
+
Error.captureStackTrace(obj, Object.defineProperty)
|
|
122
|
+
Error.stackTraceLimit = stackTraceLimit
|
|
123
|
+
// This is for speed, we don't want to work with text
|
|
124
|
+
const prepareStackTrace = Error.prepareStackTrace
|
|
125
|
+
// eslint-disable-next-line handle-callback-err
|
|
126
|
+
Error.prepareStackTrace = (err, callsites) => callsites.map((site) => site.getFunctionName())
|
|
127
|
+
const stack = obj.stack
|
|
128
|
+
Error.prepareStackTrace = prepareStackTrace
|
|
129
|
+
// eslint-disable-next-line @exodus/mutable/no-param-reassign-prop-only
|
|
130
|
+
if (stack[0] === '__copyProps' && stack[1] === '__toCommonJS') options.configurable = true
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return defineProperty(target, name, options)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = { createCallerLocationHook, getTestNamePath, makeEsbuildMockable }
|
package/src/jest.mock.js
CHANGED
|
@@ -4,6 +4,7 @@ import { existsSync } from 'node:fs'
|
|
|
4
4
|
import { normalize } from 'node:path'
|
|
5
5
|
import { mock } from 'node:test'
|
|
6
6
|
import { jestfn } from './jest.fn.js'
|
|
7
|
+
import { makeEsbuildMockable } from './dark.cjs'
|
|
7
8
|
|
|
8
9
|
const files = process.argv.slice(1)
|
|
9
10
|
const baseUrl = files.length === 1 && existsSync(files[0]) ? normalize(files[0]) : undefined
|
|
@@ -11,6 +12,7 @@ const mapMocks = new Map()
|
|
|
11
12
|
const mapActual = new Map()
|
|
12
13
|
|
|
13
14
|
const require = createRequire(baseUrl || import.meta.url)
|
|
15
|
+
const isTopLevelESM = () => !baseUrl || !Object.hasOwn(require.cache, baseUrl) // assume ESM otherwise
|
|
14
16
|
|
|
15
17
|
export const relativeRequire = require
|
|
16
18
|
|
|
@@ -62,7 +64,9 @@ function override(resolved, lax = false) {
|
|
|
62
64
|
Object.defineProperties(current, definitions)
|
|
63
65
|
const proto = Object.getPrototypeOf(value)
|
|
64
66
|
if (Object.getPrototypeOf(current) !== proto) Object.setPrototypeOf(current, proto)
|
|
65
|
-
|
|
67
|
+
const checked = { ...current }
|
|
68
|
+
if (current.__esModule === true) checked.__esModule = current.__esModule
|
|
69
|
+
if (!lax) assert.deepEqual(checked, value)
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
function mockClone(obj, cache = new Map()) {
|
|
@@ -86,6 +90,12 @@ function mockCloneItem(obj, cache) {
|
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
if (typeof obj === 'object') {
|
|
93
|
+
// Special path, as .default might be a getter and we want to unwrap it
|
|
94
|
+
if (obj.__esModule === true) {
|
|
95
|
+
const { __esModule, default: def, ...rest } = obj
|
|
96
|
+
return { __esModule, ...mockClone({ default: def, ...rest }, cache) }
|
|
97
|
+
}
|
|
98
|
+
|
|
89
99
|
const prototype = Object.getPrototypeOf(obj)
|
|
90
100
|
const clone = Object.create(prototype === null ? null : Object.prototype)
|
|
91
101
|
cache.set(obj, clone)
|
|
@@ -142,23 +152,37 @@ export function jestmock(name, mocker) {
|
|
|
142
152
|
const value = mocker ? { ...mocker() } : mockClone(mapActual.get(resolved))
|
|
143
153
|
mapMocks.set(resolved, value)
|
|
144
154
|
|
|
155
|
+
let likelyESM = false
|
|
156
|
+
const isBuiltIn = builtinModules.includes(resolved)
|
|
145
157
|
if (Object.hasOwn(require.cache, resolved)) {
|
|
146
158
|
assert.equal(mapActual.get(resolved), require.cache[resolved].exports)
|
|
147
159
|
// If we did't have this prior but have now, it means we just loaded it and there are no leaked instances
|
|
148
160
|
if (havePrior) override(resolved)
|
|
149
161
|
require.cache[resolved].exports = value
|
|
150
|
-
} else if (
|
|
162
|
+
} else if (isBuiltIn) {
|
|
151
163
|
override(resolved, true) // Override builtin modules
|
|
152
164
|
syncBuiltinESMExports()
|
|
153
165
|
} else {
|
|
154
166
|
// The module doesn't exist or is ESM
|
|
155
|
-
|
|
167
|
+
likelyESM = true
|
|
156
168
|
}
|
|
157
169
|
|
|
158
|
-
mock.module
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
170
|
+
if (!mock.module && !isBuiltIn) {
|
|
171
|
+
// Native module mocks is required if loading ESM or __from__ ESM
|
|
172
|
+
// No good way to check the locations that import the module, but we can check top-level file
|
|
173
|
+
// Built-in modules are fine though
|
|
174
|
+
assert(!likelyESM && !isTopLevelESM(), 'ESM module mocks are available only on Node.js >=22.3')
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (likelyESM && isObject(value) && value.__esModule === true) {
|
|
178
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
179
|
+
const { default: defaultExport, __esModule, ...namedExports } = value
|
|
180
|
+
mock.module?.(resolved, { defaultExport, namedExports })
|
|
181
|
+
} else {
|
|
182
|
+
mock.module?.(resolved, { defaultExport: value })
|
|
183
|
+
}
|
|
162
184
|
|
|
163
185
|
return this
|
|
164
186
|
}
|
|
187
|
+
|
|
188
|
+
makeEsbuildMockable()
|