Skip to content

Commit

Permalink
feat: added --no-timeout option (#26)
Browse files Browse the repository at this point in the history
* feat: added --no-timeout option

Closes #20

* test(timeout): resolve windows failures
  • Loading branch information
10xLaCroixDrinker committed Apr 24, 2024
1 parent 702833d commit 090e94e
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -93,6 +93,7 @@ Note the use of `incremental: true`, which speed up compilation massively.
* `--only` or `-o`, only run `node:test` with the `only` option set
* `--watch` or `-w`, re-run tests on changes
* `--timeout` or `-t`, timeouts the tests after a given time; default is 30000 ms
* `--no-timeout`, disables the timeout
* `--coverage-exclude` or `-X`, a list of comma-separated patterns to exclude from the coverage report. All tests files are ignored by default.
* `--ignore` or `-i`, ignore a glob pattern, and not look for tests there
* `--expose-gc`, exposes the gc() function to tests
Expand Down
5 changes: 5 additions & 0 deletions borp.js
Expand Up @@ -29,6 +29,7 @@ const args = parseArgs({
concurrency: { type: 'string', short: 'c', default: os.availableParallelism() - 1 + '' },
coverage: { type: 'boolean', short: 'C' },
timeout: { type: 'string', short: 't', default: '30000' },
'no-timeout': { type: 'boolean' },
'coverage-exclude': { type: 'string', short: 'X', multiple: true },
ignore: { type: 'string', short: 'i', multiple: true },
'expose-gc': { type: 'boolean' },
Expand Down Expand Up @@ -74,6 +75,10 @@ if (args.values.concurrency) {
args.values.concurrency = parseInt(args.values.concurrency)
}

if (args.values['no-timeout']) {
delete args.values.timeout
}

if (args.values.timeout) {
args.values.timeout = parseInt(args.values.timeout)
}
Expand Down
10 changes: 10 additions & 0 deletions fixtures/long/test/long.test.js
@@ -0,0 +1,10 @@
import { ok } from 'node:assert'
import { test } from 'node:test'

test('this will take a long time', (t, done) => {
setTimeout(() => {
ok(true)
done()
}, 1e3)
console.log('test:waiting')
})
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -14,7 +14,7 @@
"scripts": {
"clean": "rm -rf fixtures/*/dist .test-*",
"lint": "standard | snazzy",
"unit": "node borp.js --ignore \"fixtures/**/*\" --coverage --coverage-exclude \"fixtures/**/*\" --coverage-exclude \"test/**/*\"",
"unit": "node borp.js --ignore \"fixtures/**/*\" --coverage --coverage-exclude \"fixtures/**/*\" --coverage-exclude \"test*/**/*\"",
"test": "npm run clean ; npm run lint && npm run unit"
},
"keywords": [],
Expand All @@ -23,6 +23,7 @@
"devDependencies": {
"@matteo.collina/tspl": "^0.1.0",
"@reporters/silent": "^1.2.4",
"@sinonjs/fake-timers": "^11.2.2",
"@types/node": "^20.10.0",
"desm": "^1.3.0",
"snazzy": "^9.0.0",
Expand Down
19 changes: 19 additions & 0 deletions test-utils/clock.js
@@ -0,0 +1,19 @@
import FakeTimers from '@sinonjs/fake-timers'

let clock

if (process.argv[1].endsWith('borp.js')) {
clock = FakeTimers.install({
node: Date.now(),
shouldAdvanceTime: true,
advanceTimeDelta: 100
})
process.on('message', listener)
}

function listener ([fn, ...args]) {
clock[fn](...args)
if (fn === 'uninstall') {
process.off('message', listener)
}
}
69 changes: 69 additions & 0 deletions test/timeout.test.js
@@ -0,0 +1,69 @@
import { test } from 'node:test'
import { once } from 'node:events'
import { pathToFileURL } from 'node:url'
import { fork } from 'node:child_process'
import { tspl } from '@matteo.collina/tspl'
import { join } from 'desm'

const borp = join(import.meta.url, '..', 'borp.js')
const clock = join(import.meta.url, '..', 'test-utils', 'clock.js')
const forkOpts = {
cwd: join(import.meta.url, '..', 'fixtures', 'long'),
env: { NODE_OPTIONS: `--import=${pathToFileURL(clock)}` },
stdio: ['pipe', 'pipe', 'pipe', 'ipc']
}

test('times out after 30s by default', async (t) => {
const { ok, equal } = tspl(t, { plan: 4 })
const borpProcess = fork(borp, forkOpts)
let stdout = ''
borpProcess.stdout.on('data', (data) => {
stdout += data
if (data.includes('test:waiting')) {
borpProcess.send(['tick', 30e3])
borpProcess.send(['uninstall'])
}
})
const [code] = await once(borpProcess, 'exit')
equal(code, 1)
ok(stdout.includes('test timed out after 30000ms'))
ok(stdout.includes('tests 1'))
ok(stdout.includes('cancelled 1'))
})

test('does not timeout when setting --no-timeout', async (t) => {
const { ok, equal } = tspl(t, { plan: 4 })
const borpProcess = fork(borp, ['--no-timeout'], forkOpts)
borpProcess.stderr.pipe(process.stderr)
let stdout = ''
borpProcess.stdout.on('data', (data) => {
stdout += data
if (data.includes('test:waiting')) {
borpProcess.send(['tick', 30e3])
borpProcess.send(['uninstall'])
}
})
const [code] = await once(borpProcess, 'exit')
equal(code, 0)
ok(stdout.includes('✔ this will take a long time'))
ok(stdout.includes('tests 1'))
ok(stdout.includes('pass 1'))
})

test('timeout is configurable', async (t) => {
const { ok, equal } = tspl(t, { plan: 4 })
const borpProcess = fork(borp, ['--timeout', '10000'], forkOpts)
let stdout = ''
borpProcess.stdout.on('data', (data) => {
stdout += data
if (data.includes('test:waiting')) {
borpProcess.send(['tick', 10e3])
borpProcess.send(['uninstall'])
}
})
const [code] = await once(borpProcess, 'exit')
equal(code, 1)
ok(stdout.includes('test timed out after 10000ms'))
ok(stdout.includes('tests 1'))
ok(stdout.includes('cancelled 1'))
})

0 comments on commit 090e94e

Please sign in to comment.