Skip to content

Commit

Permalink
better Node.js version detection, ban versions with broken after/before
Browse files Browse the repository at this point in the history
  • Loading branch information
ChALkeR committed Jul 16, 2024
1 parent b30aada commit bc17b58
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 32 deletions.
31 changes: 6 additions & 25 deletions bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,13 @@ import { basename, dirname, resolve } from 'node:path'
import { createRequire } from 'node:module'
import assert from 'node:assert/strict'
import glob from 'fast-glob'
import { haveModuleMocks, haveSnapshots, haveForceExit, haveWatch } from '../src/version.js'

const bindir = dirname(fileURLToPath(import.meta.url))

const EXTS = `.?([cm])[jt]s?(x)` // we differt from jest, allowing [cm] before everything
const DEFAULT_PATTERNS = [`**/__tests__/**/*${EXTS}`, `**/?(*.)+(spec|test)${EXTS}`]

function versionCheck() {
const [major, minor, patch] = process.versions.node.split('.').map(Number)
assert((major === 18 && minor >= 13) || major >= 20, 'Node.js version too old!')
assert(major !== 21, 'Node.js version deprecated!')

return { major, minor, patch }
}

function parseOptions() {
const options = {
jest: false,
Expand Down Expand Up @@ -100,7 +93,6 @@ function parseOptions() {
return { options, patterns }
}

const { major, minor } = versionCheck()
const { options, patterns } = parseOptions()

let program = 'node'
Expand All @@ -114,10 +106,7 @@ if (resolveImport) assert.equal(c8, resolveImport('c8/bin/c8.js'))

const args = ['--test', '--no-warnings=ExperimentalWarning']

const haveModuleMocks = major > 22 || (major === 22 && minor >= 3)
if (haveModuleMocks) args.push('--experimental-test-module-mocks')

const haveSnapshots = major > 22 || (major === 22 && minor >= 3)
if (haveSnapshots) args.push('--experimental-test-snapshots')

if (options.writeSnapshots) {
Expand All @@ -126,12 +115,12 @@ if (options.writeSnapshots) {
}

if (options.forceExit) {
assert((major === 20 && minor > 13) || major >= 22, 'For forceExit, use Node.js >= 20.14.0')
assert(haveForceExit, 'For forceExit, use Node.js >= 20.14.0')
args.push('--test-force-exit')
}

if (options.watch) {
assert((major === 18 && minor > 13) || major >= 20, 'For watch mode, use Node.js >= 18.13.0')
assert(haveWatch, 'For watch mode, use Node.js >= 18.13.0')
args.push('--watch')
}

Expand All @@ -151,12 +140,8 @@ if (options.coverage) {
}

if (options.esbuild) {
if (major >= 22 || (major === 20 && minor >= 6) || (major === 18 && minor >= 18)) {
assert(resolveImport)
args.push('--import', resolveImport('tsx'))
} else {
args.push('-r', resolveRequire('tsx/cjs'))
}
assert(resolveImport)
args.push('--import', resolveImport('tsx'))
}

if (options.babel) {
Expand All @@ -176,11 +161,7 @@ if (process.env.EXODUS_TEST_IGNORE) {
if (options.jest) {
const { loadJestConfig } = await import('../src/jest.config.js')
const config = await loadJestConfig(process.cwd())
if (major >= 20 || (major === 18 && minor >= 18)) {
args.push('--import', resolve(bindir, 'jest.js'))
} else {
throw new Error('Option --jest requires Node.js >= v18.18.0')
}
args.push('--import', resolve(bindir, 'jest.js'))

if (config.testFailureExitCode !== undefined) {
if (Number(config.testFailureExitCode) === 0) {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"src/jest.snapshot.js",
"src/jest.timers.js",
"src/tape.js",
"src/version.js",
"!__tests__",
"CHANGELOG.md"
],
Expand Down
1 change: 1 addition & 0 deletions src/jest.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { jestmock, requireActual, requireMock, resetModules } from './jest.mock.
import * as jestTimers from './jest.timers.js'
import './jest.snapshot.js'
import { createCallerLocationHook } from './dark.cjs'
import './version.js'
import { expect } from 'expect'
import matchers from 'jest-extended'

Expand Down
11 changes: 4 additions & 7 deletions src/jest.timers.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import assert from 'node:assert/strict'
import { mock } from 'node:test'
import { jestConfig } from './jest.config.js'

const [major, minor] = process.versions.node.split('.').map(Number)
import { haveValidTimers, haveNoTimerInfiniteLoopBug } from './version.js'

const assertHaveTimers = () =>
assert(mock.timers, 'Timer mocking requires Node.js >=20.4.0 || 18 >=18.19.0')

let timersWarned = false
const warnOldTimers = () => {
if (timersWarned) return
if (haveValidTimers || timersWarned) return
timersWarned = true
const ok = major >= 22 || (major === 20 && minor >= 11)
if (!ok) console.warn('Warning: timer mocks are known to be glitchy before Node.js >=20.11.0')
console.warn('Warning: timer mocks are known to be glitchy before Node.js >=20.11.0')
}

export function useRealTimers() {
Expand Down Expand Up @@ -47,8 +45,7 @@ export function runAllTimers() {
}

export function runOnlyPendingTimers() {
const noInfiniteLoopBug = major >= 22 || (major === 20 && minor >= 11)
assert(noInfiniteLoopBug, 'runOnlyPendingTimers requires Node.js >=20.11.0')
assert(haveNoTimerInfiniteLoopBug, 'runOnlyPendingTimers requires Node.js >=20.11.0')
mock.timers.runAll()
return this
}
Expand Down
1 change: 1 addition & 0 deletions src/tape.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import assert from 'node:assert/strict'
import assertLoose from 'node:assert'
import { test } from 'node:test'
import { createCallerLocationHook } from './dark.cjs'
import './version.js'

const knownOptions = new Set(['skip', 'todo', 'concurrency', 'timeout'])

Expand Down
16 changes: 16 additions & 0 deletions src/version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import assert from 'node:assert/strict'

const [major, minor, patch] = process.versions.node.split('.').map(Number)
assert(major !== 21, 'Node.js 21.x is deprecated!') // reached EOL, no reason to even test
// older versions are glitchy with before/after on top-level, which is a deal-breaker
const ok = (major === 18 && minor >= 19) || (major === 20 && minor >= 7) || major >= 22
assert(ok, 'Node.js version too old or glitchy with node:test, use ^18.19.0 || ^20.7.0 || >=22.0.0')

export { major, minor, patch }

export const haveModuleMocks = (major === 22 && minor >= 3) || major > 22
export const haveSnapshots = (major === 22 && minor >= 3) || major > 22
export const haveForceExit = (major === 20 && minor > 13) || major >= 22
export const haveWatch = (major === 18 && minor > 13) || major >= 20
export const haveValidTimers = (major === 20 && minor >= 11) || major >= 22 // older glitch in various ways / stop executing
export const haveNoTimerInfiniteLoopBug = (major === 20 && minor >= 11) || major >= 22 // mock.timers.runAll() can get into infinite recursion

0 comments on commit bc17b58

Please sign in to comment.