Skip to content

Commit

Permalink
Runs CI tests on Node 23 (#1169)
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky authored Oct 29, 2024
1 parent e3572d0 commit e330bd8
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 80 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
fail-fast: false
matrix:
node-version:
- 22
- 23
- 18
os:
- ubuntu
Expand All @@ -31,7 +31,7 @@ jobs:
with:
args: --cache --verbose --no-progress --include-fragments --exclude packagephobia --exclude /pull/ --exclude linkedin --exclude file:///test --exclude invalid.com '*.md' 'docs/*.md' '.github/**/*.md' '*.json' '*.js' 'lib/**/*.js' 'test/**/*.js' '*.ts' 'test-d/**/*.ts'
fail: true
if: ${{ matrix.os == 'ubuntu' && matrix.node-version == 22 }}
if: ${{ matrix.os == 'ubuntu' && matrix.node-version == 23 }}
- run: npm run lint
- run: npm run type
- run: npm run unit
Expand Down
23 changes: 16 additions & 7 deletions test/convert/duplex.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
getReadWriteSubprocess,
} from '../helpers/convert.js';
import {foobarString} from '../helpers/input.js';
import {majorNodeVersion} from '../helpers/node-version.js';
import {prematureClose, fullStdio, fullReadableStdio} from '../helpers/stdio.js';
import {defaultHighWaterMark} from '../helpers/stream.js';

Expand Down Expand Up @@ -148,13 +149,21 @@ test('.duplex() can pipe to errored stream with Stream.pipeline()', async t => {
const cause = new Error('test');
outputStream.destroy(cause);

await assertPromiseError(t, pipeline(inputStream, stream, outputStream), cause);
await t.throwsAsync(finishedStream(stream));

await assertStreamError(t, inputStream, cause);
const error = await assertStreamError(t, stream, cause);
await assertStreamReadError(t, outputStream, cause);
await assertSubprocessError(t, subprocess, {cause: error});
// Node 23 does not allow calling `stream.pipeline()` with an already errored stream
if (majorNodeVersion >= 23) {
outputStream.on('error', () => {});
stream.on('error', () => {});
await t.throwsAsync(pipeline(stream, outputStream), {code: 'ERR_STREAM_UNABLE_TO_PIPE'});
stream.end();
} else {
await assertPromiseError(t, pipeline(inputStream, stream, outputStream), cause);
await t.throwsAsync(finishedStream(stream));

await assertStreamError(t, inputStream, cause);
const error = await assertStreamError(t, stream, cause);
await assertStreamReadError(t, outputStream, cause);
await assertSubprocessError(t, subprocess, {cause: error});
}
});

test('.duplex() can be piped to errored stream with Stream.pipeline()', async t => {
Expand Down
23 changes: 14 additions & 9 deletions test/convert/readable.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {once} from 'node:events';
import process from 'node:process';
import {
compose,
Readable,
Expand Down Expand Up @@ -29,6 +28,7 @@ import {
} from '../helpers/convert.js';
import {foobarString, foobarBuffer, foobarObject} from '../helpers/input.js';
import {simpleFull} from '../helpers/lines.js';
import {majorNodeVersion} from '../helpers/node-version.js';
import {prematureClose, fullStdio} from '../helpers/stdio.js';
import {outputObjectGenerator, getOutputsAsyncGenerator} from '../helpers/generator.js';
import {defaultHighWaterMark, defaultObjectHighWaterMark} from '../helpers/stream.js';
Expand Down Expand Up @@ -231,12 +231,18 @@ test('.readable() can pipe to errored stream with Stream.pipeline()', async t =>
const cause = new Error('test');
outputStream.destroy(cause);

await assertPromiseError(t, pipeline(stream, outputStream), cause);
await t.throwsAsync(finishedStream(stream));

const error = await assertStreamError(t, stream, cause);
await assertStreamReadError(t, outputStream, cause);
await assertSubprocessError(t, subprocess, {cause: error});
// Node 23 does not allow calling `stream.pipeline()` with an already errored stream
if (majorNodeVersion >= 23) {
outputStream.on('error', () => {});
await t.throwsAsync(pipeline(stream, outputStream), {code: 'ERR_STREAM_UNABLE_TO_PIPE'});
} else {
await assertPromiseError(t, pipeline(stream, outputStream), cause);
await t.throwsAsync(finishedStream(stream));

const error = await assertStreamError(t, stream, cause);
await assertStreamReadError(t, outputStream, cause);
await assertSubprocessError(t, subprocess, {cause: error});
}
});

test('.readable() can be used with Stream.compose()', async t => {
Expand Down Expand Up @@ -421,8 +427,7 @@ test('.duplex() can be paused', async t => {

// This feature does not work on Node 18.
// @todo: remove after dropping support for Node 18.
const majorVersion = Number(process.version.split('.')[0].slice(1));
if (majorVersion >= 20) {
if (majorNodeVersion >= 20) {
const testHighWaterMark = async (t, methodName) => {
const subprocess = execa('stdin.js');
const stream = subprocess[methodName]();
Expand Down
3 changes: 1 addition & 2 deletions test/fixtures/graceful-ref.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#!/usr/bin/env node
import {once} from 'node:events';
import {getCancelSignal} from 'execa';

const cancelSignal = await getCancelSignal();
once(cancelSignal, 'abort');
cancelSignal.addEventListener('abort', () => {});
3 changes: 3 additions & 0 deletions test/helpers/node-version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {version} from 'node:process';

export const majorNodeVersion = Number(version.split('.')[0].slice(1));
63 changes: 63 additions & 0 deletions test/stdio/type-invalid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import test from 'ava';
import {execa, execaSync} from '../../index.js';
import {getStdio} from '../helpers/stdio.js';
import {noopGenerator} from '../helpers/generator.js';
import {generatorsMap} from '../helpers/map.js';
import {setFixtureDirectory} from '../helpers/fixtures-directory.js';

setFixtureDirectory();

const testInvalidGenerator = (t, fdNumber, stdioOption, execaMethod) => {
t.throws(() => {
execaMethod('empty.js', getStdio(fdNumber, {...noopGenerator(), ...stdioOption}));
}, {message: 'final' in stdioOption ? /must be a generator/ : /must be a generator, a Duplex stream or a web TransformStream/});
};

test('Cannot use invalid "transform" with stdin', testInvalidGenerator, 0, {transform: true}, execa);
test('Cannot use invalid "transform" with stdout', testInvalidGenerator, 1, {transform: true}, execa);
test('Cannot use invalid "transform" with stderr', testInvalidGenerator, 2, {transform: true}, execa);
test('Cannot use invalid "transform" with stdio[*]', testInvalidGenerator, 3, {transform: true}, execa);
test('Cannot use invalid "final" with stdin', testInvalidGenerator, 0, {final: true}, execa);
test('Cannot use invalid "final" with stdout', testInvalidGenerator, 1, {final: true}, execa);
test('Cannot use invalid "final" with stderr', testInvalidGenerator, 2, {final: true}, execa);
test('Cannot use invalid "final" with stdio[*]', testInvalidGenerator, 3, {final: true}, execa);
test('Cannot use invalid "transform" with stdin, sync', testInvalidGenerator, 0, {transform: true}, execaSync);
test('Cannot use invalid "transform" with stdout, sync', testInvalidGenerator, 1, {transform: true}, execaSync);
test('Cannot use invalid "transform" with stderr, sync', testInvalidGenerator, 2, {transform: true}, execaSync);
test('Cannot use invalid "transform" with stdio[*], sync', testInvalidGenerator, 3, {transform: true}, execaSync);
test('Cannot use invalid "final" with stdin, sync', testInvalidGenerator, 0, {final: true}, execaSync);
test('Cannot use invalid "final" with stdout, sync', testInvalidGenerator, 1, {final: true}, execaSync);
test('Cannot use invalid "final" with stderr, sync', testInvalidGenerator, 2, {final: true}, execaSync);
test('Cannot use invalid "final" with stdio[*], sync', testInvalidGenerator, 3, {final: true}, execaSync);

// eslint-disable-next-line max-params
const testInvalidBinary = (t, fdNumber, optionName, type, execaMethod) => {
t.throws(() => {
execaMethod('empty.js', getStdio(fdNumber, {...generatorsMap[type].uppercase(), [optionName]: 'true'}));
}, {message: /a boolean/});
};

test('Cannot use invalid "binary" with stdin', testInvalidBinary, 0, 'binary', 'generator', execa);
test('Cannot use invalid "binary" with stdout', testInvalidBinary, 1, 'binary', 'generator', execa);
test('Cannot use invalid "binary" with stderr', testInvalidBinary, 2, 'binary', 'generator', execa);
test('Cannot use invalid "binary" with stdio[*]', testInvalidBinary, 3, 'binary', 'generator', execa);
test('Cannot use invalid "objectMode" with stdin, generators', testInvalidBinary, 0, 'objectMode', 'generator', execa);
test('Cannot use invalid "objectMode" with stdout, generators', testInvalidBinary, 1, 'objectMode', 'generator', execa);
test('Cannot use invalid "objectMode" with stderr, generators', testInvalidBinary, 2, 'objectMode', 'generator', execa);
test('Cannot use invalid "objectMode" with stdio[*], generators', testInvalidBinary, 3, 'objectMode', 'generator', execa);
test('Cannot use invalid "binary" with stdin, sync', testInvalidBinary, 0, 'binary', 'generator', execaSync);
test('Cannot use invalid "binary" with stdout, sync', testInvalidBinary, 1, 'binary', 'generator', execaSync);
test('Cannot use invalid "binary" with stderr, sync', testInvalidBinary, 2, 'binary', 'generator', execaSync);
test('Cannot use invalid "binary" with stdio[*], sync', testInvalidBinary, 3, 'binary', 'generator', execaSync);
test('Cannot use invalid "objectMode" with stdin, generators, sync', testInvalidBinary, 0, 'objectMode', 'generator', execaSync);
test('Cannot use invalid "objectMode" with stdout, generators, sync', testInvalidBinary, 1, 'objectMode', 'generator', execaSync);
test('Cannot use invalid "objectMode" with stderr, generators, sync', testInvalidBinary, 2, 'objectMode', 'generator', execaSync);
test('Cannot use invalid "objectMode" with stdio[*], generators, sync', testInvalidBinary, 3, 'objectMode', 'generator', execaSync);
test('Cannot use invalid "objectMode" with stdin, duplexes', testInvalidBinary, 0, 'objectMode', 'duplex', execa);
test('Cannot use invalid "objectMode" with stdout, duplexes', testInvalidBinary, 1, 'objectMode', 'duplex', execa);
test('Cannot use invalid "objectMode" with stderr, duplexes', testInvalidBinary, 2, 'objectMode', 'duplex', execa);
test('Cannot use invalid "objectMode" with stdio[*], duplexes', testInvalidBinary, 3, 'objectMode', 'duplex', execa);
test('Cannot use invalid "objectMode" with stdin, webTransforms', testInvalidBinary, 0, 'objectMode', 'webTransform', execa);
test('Cannot use invalid "objectMode" with stdout, webTransforms', testInvalidBinary, 1, 'objectMode', 'webTransform', execa);
test('Cannot use invalid "objectMode" with stderr, webTransforms', testInvalidBinary, 2, 'objectMode', 'webTransform', execa);
test('Cannot use invalid "objectMode" with stdio[*], webTransforms', testInvalidBinary, 3, 'objectMode', 'webTransform', execa);
57 changes: 1 addition & 56 deletions test/stdio/type.js → test/stdio/type-undefined.js
Original file line number Diff line number Diff line change
@@ -1,69 +1,14 @@
import test from 'ava';
import {execa, execaSync} from '../../index.js';
import {getStdio} from '../helpers/stdio.js';
import {noopGenerator, uppercaseGenerator} from '../helpers/generator.js';
import {uppercaseGenerator} from '../helpers/generator.js';
import {uppercaseBufferDuplex} from '../helpers/duplex.js';
import {uppercaseBufferWebTransform} from '../helpers/web-transform.js';
import {generatorsMap} from '../helpers/map.js';
import {setFixtureDirectory} from '../helpers/fixtures-directory.js';

setFixtureDirectory();

const testInvalidGenerator = (t, fdNumber, stdioOption, execaMethod) => {
t.throws(() => {
execaMethod('empty.js', getStdio(fdNumber, {...noopGenerator(), ...stdioOption}));
}, {message: 'final' in stdioOption ? /must be a generator/ : /must be a generator, a Duplex stream or a web TransformStream/});
};

test('Cannot use invalid "transform" with stdin', testInvalidGenerator, 0, {transform: true}, execa);
test('Cannot use invalid "transform" with stdout', testInvalidGenerator, 1, {transform: true}, execa);
test('Cannot use invalid "transform" with stderr', testInvalidGenerator, 2, {transform: true}, execa);
test('Cannot use invalid "transform" with stdio[*]', testInvalidGenerator, 3, {transform: true}, execa);
test('Cannot use invalid "final" with stdin', testInvalidGenerator, 0, {final: true}, execa);
test('Cannot use invalid "final" with stdout', testInvalidGenerator, 1, {final: true}, execa);
test('Cannot use invalid "final" with stderr', testInvalidGenerator, 2, {final: true}, execa);
test('Cannot use invalid "final" with stdio[*]', testInvalidGenerator, 3, {final: true}, execa);
test('Cannot use invalid "transform" with stdin, sync', testInvalidGenerator, 0, {transform: true}, execaSync);
test('Cannot use invalid "transform" with stdout, sync', testInvalidGenerator, 1, {transform: true}, execaSync);
test('Cannot use invalid "transform" with stderr, sync', testInvalidGenerator, 2, {transform: true}, execaSync);
test('Cannot use invalid "transform" with stdio[*], sync', testInvalidGenerator, 3, {transform: true}, execaSync);
test('Cannot use invalid "final" with stdin, sync', testInvalidGenerator, 0, {final: true}, execaSync);
test('Cannot use invalid "final" with stdout, sync', testInvalidGenerator, 1, {final: true}, execaSync);
test('Cannot use invalid "final" with stderr, sync', testInvalidGenerator, 2, {final: true}, execaSync);
test('Cannot use invalid "final" with stdio[*], sync', testInvalidGenerator, 3, {final: true}, execaSync);

// eslint-disable-next-line max-params
const testInvalidBinary = (t, fdNumber, optionName, type, execaMethod) => {
t.throws(() => {
execaMethod('empty.js', getStdio(fdNumber, {...generatorsMap[type].uppercase(), [optionName]: 'true'}));
}, {message: /a boolean/});
};

test('Cannot use invalid "binary" with stdin', testInvalidBinary, 0, 'binary', 'generator', execa);
test('Cannot use invalid "binary" with stdout', testInvalidBinary, 1, 'binary', 'generator', execa);
test('Cannot use invalid "binary" with stderr', testInvalidBinary, 2, 'binary', 'generator', execa);
test('Cannot use invalid "binary" with stdio[*]', testInvalidBinary, 3, 'binary', 'generator', execa);
test('Cannot use invalid "objectMode" with stdin, generators', testInvalidBinary, 0, 'objectMode', 'generator', execa);
test('Cannot use invalid "objectMode" with stdout, generators', testInvalidBinary, 1, 'objectMode', 'generator', execa);
test('Cannot use invalid "objectMode" with stderr, generators', testInvalidBinary, 2, 'objectMode', 'generator', execa);
test('Cannot use invalid "objectMode" with stdio[*], generators', testInvalidBinary, 3, 'objectMode', 'generator', execa);
test('Cannot use invalid "binary" with stdin, sync', testInvalidBinary, 0, 'binary', 'generator', execaSync);
test('Cannot use invalid "binary" with stdout, sync', testInvalidBinary, 1, 'binary', 'generator', execaSync);
test('Cannot use invalid "binary" with stderr, sync', testInvalidBinary, 2, 'binary', 'generator', execaSync);
test('Cannot use invalid "binary" with stdio[*], sync', testInvalidBinary, 3, 'binary', 'generator', execaSync);
test('Cannot use invalid "objectMode" with stdin, generators, sync', testInvalidBinary, 0, 'objectMode', 'generator', execaSync);
test('Cannot use invalid "objectMode" with stdout, generators, sync', testInvalidBinary, 1, 'objectMode', 'generator', execaSync);
test('Cannot use invalid "objectMode" with stderr, generators, sync', testInvalidBinary, 2, 'objectMode', 'generator', execaSync);
test('Cannot use invalid "objectMode" with stdio[*], generators, sync', testInvalidBinary, 3, 'objectMode', 'generator', execaSync);
test('Cannot use invalid "objectMode" with stdin, duplexes', testInvalidBinary, 0, 'objectMode', 'duplex', execa);
test('Cannot use invalid "objectMode" with stdout, duplexes', testInvalidBinary, 1, 'objectMode', 'duplex', execa);
test('Cannot use invalid "objectMode" with stderr, duplexes', testInvalidBinary, 2, 'objectMode', 'duplex', execa);
test('Cannot use invalid "objectMode" with stdio[*], duplexes', testInvalidBinary, 3, 'objectMode', 'duplex', execa);
test('Cannot use invalid "objectMode" with stdin, webTransforms', testInvalidBinary, 0, 'objectMode', 'webTransform', execa);
test('Cannot use invalid "objectMode" with stdout, webTransforms', testInvalidBinary, 1, 'objectMode', 'webTransform', execa);
test('Cannot use invalid "objectMode" with stderr, webTransforms', testInvalidBinary, 2, 'objectMode', 'webTransform', execa);
test('Cannot use invalid "objectMode" with stdio[*], webTransforms', testInvalidBinary, 3, 'objectMode', 'webTransform', execa);

// eslint-disable-next-line max-params
const testUndefinedOption = (t, fdNumber, optionName, type, optionValue) => {
t.throws(() => {
Expand Down
7 changes: 3 additions & 4 deletions test/terminate/kill-signal.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import {once} from 'node:events';
import {platform, version} from 'node:process';
import {platform} from 'node:process';
import {constants} from 'node:os';
import {setImmediate} from 'node:timers/promises';
import test from 'ava';
import {execa, execaSync} from '../../index.js';
import {setFixtureDirectory} from '../helpers/fixtures-directory.js';
import {majorNodeVersion} from '../helpers/node-version.js';

setFixtureDirectory();

const isWindows = platform === 'win32';
const majorNodeVersion = Number(version.split('.')[0].slice(1));

const testKillSignal = async (t, killSignal) => {
const {isTerminated, signal} = await t.throwsAsync(execa('forever.js', {killSignal, timeout: 1}));
Expand Down Expand Up @@ -61,11 +61,10 @@ test('Can call `.kill()` multiple times', async t => {
subprocess.kill();

const {exitCode, isTerminated, signal, code} = await t.throwsAsync(subprocess);

// On Windows, calling `subprocess.kill()` twice emits an `error` event on the subprocess.
// This does not happen when passing an `error` argument, nor when passing a non-terminating signal.
// There is no easy way to make this cross-platform, so we document the difference here.
if (isWindows && majorNodeVersion >= 22) {
if (isWindows && majorNodeVersion === 22) {
t.is(exitCode, undefined);
t.false(isTerminated);
t.is(signal, undefined);
Expand Down

0 comments on commit e330bd8

Please sign in to comment.