From cb2e4fd52e782de13dfae3ac3a371aa7e0e94c29 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Wed, 3 Aug 2022 13:14:01 +0200 Subject: [PATCH] fix: make test runner work even if there is no `AbortSignal` support (#36) Fixes a breaking change for Node.js v14.x that was introduced in 3.2.0. Fixes: https://github.com/nodejs/node-core-test/issues/35 --- .github/workflows/ci.yml | 7 +++---- README.md | 18 ++++++++++++++++-- lib/internal/abort_controller.js | 30 +++++++++++++++++++++++++++--- test/common/index.js | 4 ++-- test/message.js | 4 ++++ 5 files changed, 52 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a2c556..1dd3faa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,9 +9,6 @@ jobs: strategy: matrix: node: ['14', '16', '18'] - include: - - node: '14' - env: --experimental-abortcontroller --no-warnings steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 @@ -19,5 +16,7 @@ jobs: node-version: ${{ matrix.node }} - run: npm ci - run: npm test + - name: Run test with experimental flag + run: npm test env: - NODE_OPTIONS: ${{ matrix.env }} + NODE_OPTIONS: --experimental-abortcontroller --no-warnings diff --git a/README.md b/README.md index 854b293..66bc7f5 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,10 @@ Minimal dependencies, with full test suite. Differences from the core implementation: - Doesn't hide its own stack frames. -- Requires `--experimental-abortcontroller` CLI flag to work on Node.js v14.x. +- Some features require the use of `--experimental-abortcontroller` CLI flag to + work on Node.js v14.x. It's recommended to pass + `NODE_OPTIONS='--experimental-abortcontroller --no-warnings'` in your env if + you are testing on v14.x. ## Docs @@ -339,7 +342,7 @@ internally. - `only` {boolean} If truthy, and the test context is configured to run `only` tests, then this test will be run. Otherwise, the test is skipped. **Default:** `false`. - * `signal` {AbortSignal} Allows aborting an in-progress test. + - `signal` {AbortSignal} Allows aborting an in-progress test. - `skip` {boolean|string} If truthy, the test is skipped. If a string is provided, that string is displayed in the test results as the reason for skipping the test. **Default:** `false`. @@ -625,6 +628,11 @@ no-op. * [`AbortSignal`][] Can be used to abort test subtasks when the test has been aborted. +> **Warning** +> On Node.js v14.x, this feature won't be available unless you pass the +> `--experimental-abortcontroller` CLI flag or added an external global polyfill +> for `AbortController`. + ```js test('top level test', async (t) => { await fetch('some/uri', { signal: t.signal }); @@ -694,6 +702,12 @@ The name of the suite. * [`AbortSignal`][] Can be used to abort test subtasks when the test has been aborted. +> **Warning** +> On Node.js v14.x, this feature won't be available unless you pass the +> `--experimental-abortcontroller` CLI flag or added an external global polyfill +> for `AbortController`. + + [`AbortSignal`]: https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal [TAP]: https://testanything.org/ [`SuiteContext`]: #class-suitecontext diff --git a/lib/internal/abort_controller.js b/lib/internal/abort_controller.js index 007aa40..36ce226 100644 --- a/lib/internal/abort_controller.js +++ b/lib/internal/abort_controller.js @@ -1,6 +1,30 @@ 'use strict' -module.exports = { - AbortController, - AbortSignal +if (typeof AbortController === 'undefined') { + // Node.js AbortController implementation is behind a CLI flag. + // This is a minimal mock to make the code work if the native + // implementation in not available. + module.exports = { + AbortController: class AbortController { + #eventListeners = new Set() + signal = { + aborted: false, + addEventListener: (_, listener) => { + this.#eventListeners.add(listener) + }, + removeEventListener: (_, listener) => { + this.#eventListeners.delete(listener) + } + } + + abort () { + this.signal.aborted = true + this.#eventListeners.forEach(listener => listener()) + } + } + } +} else { + module.exports = { + AbortController + } } diff --git a/test/common/index.js b/test/common/index.js index ae531e6..91fcd41 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -102,7 +102,7 @@ function expectsError (validator, exact) { }, exact) } -if (typeof AbortSignal.timeout !== 'function') { +if (typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout !== 'function') { // `AbortSignal.timeout` is not available on Node.js 14.x, we need to polyfill // it because some tests are using it. End-users don't need to it. @@ -121,7 +121,7 @@ if (typeof AbortSignal.timeout !== 'function') { } } -if (process.version.startsWith('v14.') || process.version.startsWith('v16.')) { +if (typeof AbortSignal !== 'undefined' && (process.version.startsWith('v14.') || process.version.startsWith('v16.'))) { // Implementation of AbortSignal and AbortController differ slightly with the // v18.x one, creating some difference on the TAP output which makes the tests // fail. We are overriding the built-ins to make the test pass, however that's diff --git a/test/message.js b/test/message.js index ea35fb1..4520448 100755 --- a/test/message.js +++ b/test/message.js @@ -84,6 +84,10 @@ const main = async () => { for await (const dirent of dir) { const ext = extname(dirent.name) if (ext === '.js' || ext === '.mjs') { + if (typeof AbortSignal === 'undefined' && dirent.name.startsWith('test_runner_abort')) { + console.log('no AbortSignal support, skipping', dirent.name) + continue + } const filePath = join(MESSAGE_FOLDER, dirent.name) const expected = filePath.replace(/\.m?js$/, '.out') const testFile = await fs.open(filePath)