@exodus/test 1.0.0-rc.16 → 1.0.0-rc.17
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/README.md +1 -3
- package/bin/index.js +31 -10
- package/package.json +3 -3
- package/src/jest.config.js +1 -2
- package/src/jest.js +36 -3
package/README.md
CHANGED
|
@@ -58,9 +58,7 @@ Just use `"test": "exodus-test"`
|
|
|
58
58
|
|
|
59
59
|
- `--jest` -- register jest test helpers as global variables, also load `jest.config.*` configuration options
|
|
60
60
|
|
|
61
|
-
- `--
|
|
62
|
-
|
|
63
|
-
- `--esbuild` -- use esbuild loader (currently an alias for `--typescript`)
|
|
61
|
+
- `--esbuild` -- use esbuild loader, also enables Typescript support
|
|
64
62
|
|
|
65
63
|
- `--babel` -- use babel loader (slower than `--esbuild`, makes sense if you have a special config)
|
|
66
64
|
|
package/bin/index.js
CHANGED
|
@@ -9,7 +9,8 @@ import glob from 'fast-glob'
|
|
|
9
9
|
|
|
10
10
|
const bindir = dirname(fileURLToPath(import.meta.url))
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const EXTS = `.?([cm])[jt]s?(x)` // we differt from jest, allowing [cm] before everything
|
|
13
|
+
const DEFAULT_PATTERNS = [`**/__tests__/**/*${EXTS}`, `**/?(*.)+(spec|test)${EXTS}`]
|
|
13
14
|
|
|
14
15
|
function versionCheck() {
|
|
15
16
|
const [major, minor, patch] = process.versions.node.split('.').map(Number)
|
|
@@ -51,7 +52,9 @@ function parseOptions() {
|
|
|
51
52
|
options.jest = true
|
|
52
53
|
break
|
|
53
54
|
case '--typescript':
|
|
55
|
+
console.warn('Option --typescript is going to be gone or changed. Use --esbuild instead')
|
|
54
56
|
options.typescript = true
|
|
57
|
+
options.esbuild = true
|
|
55
58
|
break
|
|
56
59
|
case '--esbuild':
|
|
57
60
|
options.esbuild = true
|
|
@@ -147,7 +150,7 @@ if (options.coverage) {
|
|
|
147
150
|
}
|
|
148
151
|
}
|
|
149
152
|
|
|
150
|
-
if (options.
|
|
153
|
+
if (options.esbuild) {
|
|
151
154
|
if (major >= 22 || (major === 20 && minor >= 6) || (major === 18 && minor >= 18)) {
|
|
152
155
|
assert(resolveImport)
|
|
153
156
|
args.push('--import', resolveImport('tsx'))
|
|
@@ -157,11 +160,12 @@ if (options.typescript || options.esbuild) {
|
|
|
157
160
|
}
|
|
158
161
|
|
|
159
162
|
if (options.babel) {
|
|
160
|
-
assert(!options.
|
|
163
|
+
assert(!options.esbuild, 'Options --babel and --esbuild are mutually exclusive')
|
|
161
164
|
args.push('-r', resolveRequire('./babel.cjs'))
|
|
162
165
|
}
|
|
163
166
|
|
|
164
167
|
const ignore = ['**/node_modules']
|
|
168
|
+
let filter
|
|
165
169
|
if (process.env.EXODUS_TEST_IGNORE) {
|
|
166
170
|
// fast-glob treats negative ignore patterns exactly the same as positive, let's not cause a confusion
|
|
167
171
|
assert(!process.env.EXODUS_TEST_IGNORE.startsWith('!'), 'Ignore pattern should not be negative')
|
|
@@ -172,7 +176,6 @@ if (process.env.EXODUS_TEST_IGNORE) {
|
|
|
172
176
|
if (options.jest) {
|
|
173
177
|
const { loadJestConfig } = await import('../src/jest.config.js')
|
|
174
178
|
const config = await loadJestConfig(process.cwd())
|
|
175
|
-
ignore.push(...config.testPathIgnorePatterns)
|
|
176
179
|
if (major >= 20 || (major === 18 && minor >= 18)) {
|
|
177
180
|
args.push('--import', resolve(bindir, 'jest.js'))
|
|
178
181
|
} else {
|
|
@@ -189,12 +192,30 @@ if (options.jest) {
|
|
|
189
192
|
})
|
|
190
193
|
}
|
|
191
194
|
|
|
192
|
-
|
|
193
|
-
|
|
195
|
+
if (patterns.length > 0) {
|
|
196
|
+
// skip, we already have patterns via argv
|
|
197
|
+
} else if (config.testRegex) {
|
|
198
|
+
assert(typeof config.testRegex === 'string', `config.testRegex should be a string`)
|
|
199
|
+
assert(!config.testMatch, 'config.testRegex can not be used together with config.testMatch')
|
|
200
|
+
patterns.push('**/*')
|
|
201
|
+
} else if (config.testMatch) {
|
|
202
|
+
patterns.push(...(Array.isArray(config.testMatch) ? config.testMatch : [config.testMatch]))
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const testRegex = config.testRegex ? new RegExp(config.testRegex, 'u') : null
|
|
206
|
+
const ignoreRegexes = config.testPathIgnorePatterns.map((x) => new RegExp(x, 'u'))
|
|
207
|
+
if (testRegex || ignoreRegexes.length > 0) {
|
|
208
|
+
filter = (x) => {
|
|
209
|
+
const resolved = `<rootDir>/${x}` // don't actually include cwd, that should be irrelevant
|
|
210
|
+
if (testRegex && !testRegex.test(resolved)) return false
|
|
211
|
+
return !ignoreRegexes.some((r) => r.test(resolved))
|
|
212
|
+
}
|
|
213
|
+
}
|
|
194
214
|
}
|
|
195
215
|
|
|
196
216
|
if (patterns.length === 0) patterns.push(...DEFAULT_PATTERNS) // defaults
|
|
197
|
-
const
|
|
217
|
+
const globbed = await glob(patterns, { ignore })
|
|
218
|
+
const allfiles = filter ? globbed.filter(filter) : globbed
|
|
198
219
|
|
|
199
220
|
if (allfiles.length === 0) {
|
|
200
221
|
if (options.passWithNoTests) {
|
|
@@ -243,13 +264,13 @@ files.sort((a, b) => {
|
|
|
243
264
|
})
|
|
244
265
|
|
|
245
266
|
if (options.debug.files) {
|
|
246
|
-
console.log(
|
|
267
|
+
for (const f of files) console.log(f) // joining with \n can get truncated, too big
|
|
247
268
|
process.exit(1) // do not succeed!
|
|
248
269
|
}
|
|
249
270
|
|
|
250
271
|
const tsTests = files.filter((file) => /\.[mc]?tsx?$/u.test(file))
|
|
251
|
-
if (tsTests.length > 0 && !options.
|
|
252
|
-
console.error(`Some tests require --
|
|
272
|
+
if (tsTests.length > 0 && !options.esbuild) {
|
|
273
|
+
console.error(`Some tests require --esbuild flag:\n ${tsTests.join('\n ')}`)
|
|
253
274
|
process.exit(1)
|
|
254
275
|
} else if (!allfiles.some((file) => file.endsWith('.ts')) && options.typescript) {
|
|
255
276
|
console.warn(`Flag --typescript has been used, but there were no TypeScript tests found!`)
|
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.17",
|
|
4
4
|
"author": "Exodus Movement, Inc.",
|
|
5
5
|
"description": "A test suite runner",
|
|
6
6
|
"homepage": "https://github.com/ExodusMovement/test",
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
"CHANGELOG.md"
|
|
47
47
|
],
|
|
48
48
|
"scripts": {
|
|
49
|
-
"test": "./bin/index.js --jest --
|
|
49
|
+
"test": "./bin/index.js --jest --esbuild",
|
|
50
50
|
"test:tape": "./bin/index.js --esbuild '__test__/tape/test/*.js' __test__/tape.test.js",
|
|
51
|
-
"coverage": "./bin/index.js --jest --
|
|
51
|
+
"coverage": "./bin/index.js --jest --esbuild --coverage",
|
|
52
52
|
"lint": "prettier --list-different . && eslint .",
|
|
53
53
|
"lint:fix": "prettier --write . && eslint --fix ."
|
|
54
54
|
},
|
package/src/jest.config.js
CHANGED
|
@@ -53,7 +53,6 @@ async function getJestConfig(dir) {
|
|
|
53
53
|
const normalizeJestConfig = (config) => ({
|
|
54
54
|
testEnvironment: 'node',
|
|
55
55
|
testTimeout: 5000,
|
|
56
|
-
testMatch: ['**/__tests__/**/*.?([cm])[jt]s?(x)', '**/?(*.)+(spec|test).?([cm])[jt]s?(x)'],
|
|
57
56
|
testPathIgnorePatterns: [],
|
|
58
57
|
snapshotSerializers: [],
|
|
59
58
|
injectGlobals: true,
|
|
@@ -93,7 +92,7 @@ function verifyJestConfig(c) {
|
|
|
93
92
|
assert(!c.preset, 'Jest config.preset is not supported')
|
|
94
93
|
|
|
95
94
|
// TODO
|
|
96
|
-
const TODO = ['globalSetup', 'globalTeardown', 'randomize', 'projects', 'roots'
|
|
95
|
+
const TODO = ['globalSetup', 'globalTeardown', 'randomize', 'projects', 'roots']
|
|
97
96
|
TODO.push('resolver', 'unmockedModulePathPatterns', 'watchPathIgnorePatterns', 'snapshotResolver')
|
|
98
97
|
for (const key of TODO) assert.equal(c[key], undefined, `Jest config.${key} is not supported yet`)
|
|
99
98
|
}
|
package/src/jest.js
CHANGED
|
@@ -15,6 +15,7 @@ const { getCallerLocation, installLocationInNextTest } = createCallerLocationHoo
|
|
|
15
15
|
expect.extend(matchers)
|
|
16
16
|
|
|
17
17
|
let defaultTimeout = jestConfig().testTimeout // overridable via jest.setTimeout()
|
|
18
|
+
const defaultConcurrency = jestConfig().maxConcurrency
|
|
18
19
|
|
|
19
20
|
function parseArgs(list, targs) {
|
|
20
21
|
if (!(Object.isFrozen(list) && list.length === targs.length + 1)) return list // template check
|
|
@@ -79,10 +80,37 @@ const makeEach =
|
|
|
79
80
|
|
|
80
81
|
const forceExit = process.execArgv.map((x) => x.replaceAll('_', '-')).includes('--test-force-exit')
|
|
81
82
|
|
|
82
|
-
const
|
|
83
|
-
const
|
|
83
|
+
const inConcurrent = []
|
|
84
|
+
const concurrent = []
|
|
85
|
+
const describe = (...args) => {
|
|
86
|
+
const fn = args.pop()
|
|
87
|
+
const optionsConcurrent = args?.at(-1)?.concurrency > 1
|
|
88
|
+
if (optionsConcurrent) inConcurrent.push(fn)
|
|
89
|
+
const result = nodeDescribe(...args, async () => {
|
|
90
|
+
const res = fn()
|
|
91
|
+
|
|
92
|
+
// We do only block-level concurrency, not file-level
|
|
93
|
+
if (concurrent.length === 1) {
|
|
94
|
+
const [args, callerLocation] = concurrent[0]
|
|
95
|
+
testRaw(callerLocation, ...args)
|
|
96
|
+
concurrent.length = 0
|
|
97
|
+
} else if (concurrent.length > 0) {
|
|
98
|
+
const queue = [...concurrent]
|
|
99
|
+
concurrent.length = 0
|
|
100
|
+
describe('concurrent', { concurrency: defaultConcurrency }, () => {
|
|
101
|
+
for (const [args, callerLocation] of queue) testRaw(callerLocation, ...args)
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return res
|
|
106
|
+
})
|
|
107
|
+
if (optionsConcurrent) inConcurrent.pop()
|
|
108
|
+
return result
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const testRaw = (callerLocation, name, fn, testTimeout) => {
|
|
84
112
|
const timeout = testTimeout ?? defaultTimeout
|
|
85
|
-
installLocationInNextTest(
|
|
113
|
+
installLocationInNextTest(callerLocation)
|
|
86
114
|
if (fn.length > 0) return nodeTest(name, (t, c) => fn(c))
|
|
87
115
|
if (!forceExit) return nodeTest(name, fn)
|
|
88
116
|
return nodeTest(name, { timeout }, async (t) => {
|
|
@@ -97,8 +125,13 @@ Also, using expect.assertions() to ensure the planned number of assertions is be
|
|
|
97
125
|
})
|
|
98
126
|
}
|
|
99
127
|
|
|
128
|
+
const test = (...args) => testRaw(getCallerLocation(), ...args)
|
|
129
|
+
|
|
100
130
|
describe.each = makeEach(describe)
|
|
101
131
|
test.each = makeEach(test)
|
|
132
|
+
test.concurrent = (...args) =>
|
|
133
|
+
inConcurrent.length > 0 ? test(...args) : concurrent.push([args, getCallerLocation()])
|
|
134
|
+
test.concurrent.each = makeEach(test.concurrent)
|
|
102
135
|
describe.skip = (...args) => nodeDescribe.skip(...args)
|
|
103
136
|
test.skip = (...args) => nodeTest.skip(...args)
|
|
104
137
|
|