Skip to content

Commit e330bd8

Browse files
authored
Runs CI tests on Node 23 (#1169)
1 parent e3572d0 commit e330bd8

File tree

8 files changed

+103
-80
lines changed

8 files changed

+103
-80
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
node-version:
13-
- 22
13+
- 23
1414
- 18
1515
os:
1616
- ubuntu
@@ -31,7 +31,7 @@ jobs:
3131
with:
3232
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'
3333
fail: true
34-
if: ${{ matrix.os == 'ubuntu' && matrix.node-version == 22 }}
34+
if: ${{ matrix.os == 'ubuntu' && matrix.node-version == 23 }}
3535
- run: npm run lint
3636
- run: npm run type
3737
- run: npm run unit

test/convert/duplex.js

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
getReadWriteSubprocess,
2424
} from '../helpers/convert.js';
2525
import {foobarString} from '../helpers/input.js';
26+
import {majorNodeVersion} from '../helpers/node-version.js';
2627
import {prematureClose, fullStdio, fullReadableStdio} from '../helpers/stdio.js';
2728
import {defaultHighWaterMark} from '../helpers/stream.js';
2829

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

151-
await assertPromiseError(t, pipeline(inputStream, stream, outputStream), cause);
152-
await t.throwsAsync(finishedStream(stream));
153-
154-
await assertStreamError(t, inputStream, cause);
155-
const error = await assertStreamError(t, stream, cause);
156-
await assertStreamReadError(t, outputStream, cause);
157-
await assertSubprocessError(t, subprocess, {cause: error});
152+
// Node 23 does not allow calling `stream.pipeline()` with an already errored stream
153+
if (majorNodeVersion >= 23) {
154+
outputStream.on('error', () => {});
155+
stream.on('error', () => {});
156+
await t.throwsAsync(pipeline(stream, outputStream), {code: 'ERR_STREAM_UNABLE_TO_PIPE'});
157+
stream.end();
158+
} else {
159+
await assertPromiseError(t, pipeline(inputStream, stream, outputStream), cause);
160+
await t.throwsAsync(finishedStream(stream));
161+
162+
await assertStreamError(t, inputStream, cause);
163+
const error = await assertStreamError(t, stream, cause);
164+
await assertStreamReadError(t, outputStream, cause);
165+
await assertSubprocessError(t, subprocess, {cause: error});
166+
}
158167
});
159168

160169
test('.duplex() can be piped to errored stream with Stream.pipeline()', async t => {

test/convert/readable.js

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {once} from 'node:events';
2-
import process from 'node:process';
32
import {
43
compose,
54
Readable,
@@ -29,6 +28,7 @@ import {
2928
} from '../helpers/convert.js';
3029
import {foobarString, foobarBuffer, foobarObject} from '../helpers/input.js';
3130
import {simpleFull} from '../helpers/lines.js';
31+
import {majorNodeVersion} from '../helpers/node-version.js';
3232
import {prematureClose, fullStdio} from '../helpers/stdio.js';
3333
import {outputObjectGenerator, getOutputsAsyncGenerator} from '../helpers/generator.js';
3434
import {defaultHighWaterMark, defaultObjectHighWaterMark} from '../helpers/stream.js';
@@ -231,12 +231,18 @@ test('.readable() can pipe to errored stream with Stream.pipeline()', async t =>
231231
const cause = new Error('test');
232232
outputStream.destroy(cause);
233233

234-
await assertPromiseError(t, pipeline(stream, outputStream), cause);
235-
await t.throwsAsync(finishedStream(stream));
236-
237-
const error = await assertStreamError(t, stream, cause);
238-
await assertStreamReadError(t, outputStream, cause);
239-
await assertSubprocessError(t, subprocess, {cause: error});
234+
// Node 23 does not allow calling `stream.pipeline()` with an already errored stream
235+
if (majorNodeVersion >= 23) {
236+
outputStream.on('error', () => {});
237+
await t.throwsAsync(pipeline(stream, outputStream), {code: 'ERR_STREAM_UNABLE_TO_PIPE'});
238+
} else {
239+
await assertPromiseError(t, pipeline(stream, outputStream), cause);
240+
await t.throwsAsync(finishedStream(stream));
241+
242+
const error = await assertStreamError(t, stream, cause);
243+
await assertStreamReadError(t, outputStream, cause);
244+
await assertSubprocessError(t, subprocess, {cause: error});
245+
}
240246
});
241247

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

422428
// This feature does not work on Node 18.
423429
// @todo: remove after dropping support for Node 18.
424-
const majorVersion = Number(process.version.split('.')[0].slice(1));
425-
if (majorVersion >= 20) {
430+
if (majorNodeVersion >= 20) {
426431
const testHighWaterMark = async (t, methodName) => {
427432
const subprocess = execa('stdin.js');
428433
const stream = subprocess[methodName]();

test/fixtures/graceful-ref.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#!/usr/bin/env node
2-
import {once} from 'node:events';
32
import {getCancelSignal} from 'execa';
43

54
const cancelSignal = await getCancelSignal();
6-
once(cancelSignal, 'abort');
5+
cancelSignal.addEventListener('abort', () => {});

test/helpers/node-version.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import {version} from 'node:process';
2+
3+
export const majorNodeVersion = Number(version.split('.')[0].slice(1));

test/stdio/type-invalid.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import test from 'ava';
2+
import {execa, execaSync} from '../../index.js';
3+
import {getStdio} from '../helpers/stdio.js';
4+
import {noopGenerator} from '../helpers/generator.js';
5+
import {generatorsMap} from '../helpers/map.js';
6+
import {setFixtureDirectory} from '../helpers/fixtures-directory.js';
7+
8+
setFixtureDirectory();
9+
10+
const testInvalidGenerator = (t, fdNumber, stdioOption, execaMethod) => {
11+
t.throws(() => {
12+
execaMethod('empty.js', getStdio(fdNumber, {...noopGenerator(), ...stdioOption}));
13+
}, {message: 'final' in stdioOption ? /must be a generator/ : /must be a generator, a Duplex stream or a web TransformStream/});
14+
};
15+
16+
test('Cannot use invalid "transform" with stdin', testInvalidGenerator, 0, {transform: true}, execa);
17+
test('Cannot use invalid "transform" with stdout', testInvalidGenerator, 1, {transform: true}, execa);
18+
test('Cannot use invalid "transform" with stderr', testInvalidGenerator, 2, {transform: true}, execa);
19+
test('Cannot use invalid "transform" with stdio[*]', testInvalidGenerator, 3, {transform: true}, execa);
20+
test('Cannot use invalid "final" with stdin', testInvalidGenerator, 0, {final: true}, execa);
21+
test('Cannot use invalid "final" with stdout', testInvalidGenerator, 1, {final: true}, execa);
22+
test('Cannot use invalid "final" with stderr', testInvalidGenerator, 2, {final: true}, execa);
23+
test('Cannot use invalid "final" with stdio[*]', testInvalidGenerator, 3, {final: true}, execa);
24+
test('Cannot use invalid "transform" with stdin, sync', testInvalidGenerator, 0, {transform: true}, execaSync);
25+
test('Cannot use invalid "transform" with stdout, sync', testInvalidGenerator, 1, {transform: true}, execaSync);
26+
test('Cannot use invalid "transform" with stderr, sync', testInvalidGenerator, 2, {transform: true}, execaSync);
27+
test('Cannot use invalid "transform" with stdio[*], sync', testInvalidGenerator, 3, {transform: true}, execaSync);
28+
test('Cannot use invalid "final" with stdin, sync', testInvalidGenerator, 0, {final: true}, execaSync);
29+
test('Cannot use invalid "final" with stdout, sync', testInvalidGenerator, 1, {final: true}, execaSync);
30+
test('Cannot use invalid "final" with stderr, sync', testInvalidGenerator, 2, {final: true}, execaSync);
31+
test('Cannot use invalid "final" with stdio[*], sync', testInvalidGenerator, 3, {final: true}, execaSync);
32+
33+
// eslint-disable-next-line max-params
34+
const testInvalidBinary = (t, fdNumber, optionName, type, execaMethod) => {
35+
t.throws(() => {
36+
execaMethod('empty.js', getStdio(fdNumber, {...generatorsMap[type].uppercase(), [optionName]: 'true'}));
37+
}, {message: /a boolean/});
38+
};
39+
40+
test('Cannot use invalid "binary" with stdin', testInvalidBinary, 0, 'binary', 'generator', execa);
41+
test('Cannot use invalid "binary" with stdout', testInvalidBinary, 1, 'binary', 'generator', execa);
42+
test('Cannot use invalid "binary" with stderr', testInvalidBinary, 2, 'binary', 'generator', execa);
43+
test('Cannot use invalid "binary" with stdio[*]', testInvalidBinary, 3, 'binary', 'generator', execa);
44+
test('Cannot use invalid "objectMode" with stdin, generators', testInvalidBinary, 0, 'objectMode', 'generator', execa);
45+
test('Cannot use invalid "objectMode" with stdout, generators', testInvalidBinary, 1, 'objectMode', 'generator', execa);
46+
test('Cannot use invalid "objectMode" with stderr, generators', testInvalidBinary, 2, 'objectMode', 'generator', execa);
47+
test('Cannot use invalid "objectMode" with stdio[*], generators', testInvalidBinary, 3, 'objectMode', 'generator', execa);
48+
test('Cannot use invalid "binary" with stdin, sync', testInvalidBinary, 0, 'binary', 'generator', execaSync);
49+
test('Cannot use invalid "binary" with stdout, sync', testInvalidBinary, 1, 'binary', 'generator', execaSync);
50+
test('Cannot use invalid "binary" with stderr, sync', testInvalidBinary, 2, 'binary', 'generator', execaSync);
51+
test('Cannot use invalid "binary" with stdio[*], sync', testInvalidBinary, 3, 'binary', 'generator', execaSync);
52+
test('Cannot use invalid "objectMode" with stdin, generators, sync', testInvalidBinary, 0, 'objectMode', 'generator', execaSync);
53+
test('Cannot use invalid "objectMode" with stdout, generators, sync', testInvalidBinary, 1, 'objectMode', 'generator', execaSync);
54+
test('Cannot use invalid "objectMode" with stderr, generators, sync', testInvalidBinary, 2, 'objectMode', 'generator', execaSync);
55+
test('Cannot use invalid "objectMode" with stdio[*], generators, sync', testInvalidBinary, 3, 'objectMode', 'generator', execaSync);
56+
test('Cannot use invalid "objectMode" with stdin, duplexes', testInvalidBinary, 0, 'objectMode', 'duplex', execa);
57+
test('Cannot use invalid "objectMode" with stdout, duplexes', testInvalidBinary, 1, 'objectMode', 'duplex', execa);
58+
test('Cannot use invalid "objectMode" with stderr, duplexes', testInvalidBinary, 2, 'objectMode', 'duplex', execa);
59+
test('Cannot use invalid "objectMode" with stdio[*], duplexes', testInvalidBinary, 3, 'objectMode', 'duplex', execa);
60+
test('Cannot use invalid "objectMode" with stdin, webTransforms', testInvalidBinary, 0, 'objectMode', 'webTransform', execa);
61+
test('Cannot use invalid "objectMode" with stdout, webTransforms', testInvalidBinary, 1, 'objectMode', 'webTransform', execa);
62+
test('Cannot use invalid "objectMode" with stderr, webTransforms', testInvalidBinary, 2, 'objectMode', 'webTransform', execa);
63+
test('Cannot use invalid "objectMode" with stdio[*], webTransforms', testInvalidBinary, 3, 'objectMode', 'webTransform', execa);

test/stdio/type.js renamed to test/stdio/type-undefined.js

Lines changed: 1 addition & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,14 @@
11
import test from 'ava';
22
import {execa, execaSync} from '../../index.js';
33
import {getStdio} from '../helpers/stdio.js';
4-
import {noopGenerator, uppercaseGenerator} from '../helpers/generator.js';
4+
import {uppercaseGenerator} from '../helpers/generator.js';
55
import {uppercaseBufferDuplex} from '../helpers/duplex.js';
66
import {uppercaseBufferWebTransform} from '../helpers/web-transform.js';
77
import {generatorsMap} from '../helpers/map.js';
88
import {setFixtureDirectory} from '../helpers/fixtures-directory.js';
99

1010
setFixtureDirectory();
1111

12-
const testInvalidGenerator = (t, fdNumber, stdioOption, execaMethod) => {
13-
t.throws(() => {
14-
execaMethod('empty.js', getStdio(fdNumber, {...noopGenerator(), ...stdioOption}));
15-
}, {message: 'final' in stdioOption ? /must be a generator/ : /must be a generator, a Duplex stream or a web TransformStream/});
16-
};
17-
18-
test('Cannot use invalid "transform" with stdin', testInvalidGenerator, 0, {transform: true}, execa);
19-
test('Cannot use invalid "transform" with stdout', testInvalidGenerator, 1, {transform: true}, execa);
20-
test('Cannot use invalid "transform" with stderr', testInvalidGenerator, 2, {transform: true}, execa);
21-
test('Cannot use invalid "transform" with stdio[*]', testInvalidGenerator, 3, {transform: true}, execa);
22-
test('Cannot use invalid "final" with stdin', testInvalidGenerator, 0, {final: true}, execa);
23-
test('Cannot use invalid "final" with stdout', testInvalidGenerator, 1, {final: true}, execa);
24-
test('Cannot use invalid "final" with stderr', testInvalidGenerator, 2, {final: true}, execa);
25-
test('Cannot use invalid "final" with stdio[*]', testInvalidGenerator, 3, {final: true}, execa);
26-
test('Cannot use invalid "transform" with stdin, sync', testInvalidGenerator, 0, {transform: true}, execaSync);
27-
test('Cannot use invalid "transform" with stdout, sync', testInvalidGenerator, 1, {transform: true}, execaSync);
28-
test('Cannot use invalid "transform" with stderr, sync', testInvalidGenerator, 2, {transform: true}, execaSync);
29-
test('Cannot use invalid "transform" with stdio[*], sync', testInvalidGenerator, 3, {transform: true}, execaSync);
30-
test('Cannot use invalid "final" with stdin, sync', testInvalidGenerator, 0, {final: true}, execaSync);
31-
test('Cannot use invalid "final" with stdout, sync', testInvalidGenerator, 1, {final: true}, execaSync);
32-
test('Cannot use invalid "final" with stderr, sync', testInvalidGenerator, 2, {final: true}, execaSync);
33-
test('Cannot use invalid "final" with stdio[*], sync', testInvalidGenerator, 3, {final: true}, execaSync);
34-
35-
// eslint-disable-next-line max-params
36-
const testInvalidBinary = (t, fdNumber, optionName, type, execaMethod) => {
37-
t.throws(() => {
38-
execaMethod('empty.js', getStdio(fdNumber, {...generatorsMap[type].uppercase(), [optionName]: 'true'}));
39-
}, {message: /a boolean/});
40-
};
41-
42-
test('Cannot use invalid "binary" with stdin', testInvalidBinary, 0, 'binary', 'generator', execa);
43-
test('Cannot use invalid "binary" with stdout', testInvalidBinary, 1, 'binary', 'generator', execa);
44-
test('Cannot use invalid "binary" with stderr', testInvalidBinary, 2, 'binary', 'generator', execa);
45-
test('Cannot use invalid "binary" with stdio[*]', testInvalidBinary, 3, 'binary', 'generator', execa);
46-
test('Cannot use invalid "objectMode" with stdin, generators', testInvalidBinary, 0, 'objectMode', 'generator', execa);
47-
test('Cannot use invalid "objectMode" with stdout, generators', testInvalidBinary, 1, 'objectMode', 'generator', execa);
48-
test('Cannot use invalid "objectMode" with stderr, generators', testInvalidBinary, 2, 'objectMode', 'generator', execa);
49-
test('Cannot use invalid "objectMode" with stdio[*], generators', testInvalidBinary, 3, 'objectMode', 'generator', execa);
50-
test('Cannot use invalid "binary" with stdin, sync', testInvalidBinary, 0, 'binary', 'generator', execaSync);
51-
test('Cannot use invalid "binary" with stdout, sync', testInvalidBinary, 1, 'binary', 'generator', execaSync);
52-
test('Cannot use invalid "binary" with stderr, sync', testInvalidBinary, 2, 'binary', 'generator', execaSync);
53-
test('Cannot use invalid "binary" with stdio[*], sync', testInvalidBinary, 3, 'binary', 'generator', execaSync);
54-
test('Cannot use invalid "objectMode" with stdin, generators, sync', testInvalidBinary, 0, 'objectMode', 'generator', execaSync);
55-
test('Cannot use invalid "objectMode" with stdout, generators, sync', testInvalidBinary, 1, 'objectMode', 'generator', execaSync);
56-
test('Cannot use invalid "objectMode" with stderr, generators, sync', testInvalidBinary, 2, 'objectMode', 'generator', execaSync);
57-
test('Cannot use invalid "objectMode" with stdio[*], generators, sync', testInvalidBinary, 3, 'objectMode', 'generator', execaSync);
58-
test('Cannot use invalid "objectMode" with stdin, duplexes', testInvalidBinary, 0, 'objectMode', 'duplex', execa);
59-
test('Cannot use invalid "objectMode" with stdout, duplexes', testInvalidBinary, 1, 'objectMode', 'duplex', execa);
60-
test('Cannot use invalid "objectMode" with stderr, duplexes', testInvalidBinary, 2, 'objectMode', 'duplex', execa);
61-
test('Cannot use invalid "objectMode" with stdio[*], duplexes', testInvalidBinary, 3, 'objectMode', 'duplex', execa);
62-
test('Cannot use invalid "objectMode" with stdin, webTransforms', testInvalidBinary, 0, 'objectMode', 'webTransform', execa);
63-
test('Cannot use invalid "objectMode" with stdout, webTransforms', testInvalidBinary, 1, 'objectMode', 'webTransform', execa);
64-
test('Cannot use invalid "objectMode" with stderr, webTransforms', testInvalidBinary, 2, 'objectMode', 'webTransform', execa);
65-
test('Cannot use invalid "objectMode" with stdio[*], webTransforms', testInvalidBinary, 3, 'objectMode', 'webTransform', execa);
66-
6712
// eslint-disable-next-line max-params
6813
const testUndefinedOption = (t, fdNumber, optionName, type, optionValue) => {
6914
t.throws(() => {

test/terminate/kill-signal.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import {once} from 'node:events';
2-
import {platform, version} from 'node:process';
2+
import {platform} from 'node:process';
33
import {constants} from 'node:os';
44
import {setImmediate} from 'node:timers/promises';
55
import test from 'ava';
66
import {execa, execaSync} from '../../index.js';
77
import {setFixtureDirectory} from '../helpers/fixtures-directory.js';
8+
import {majorNodeVersion} from '../helpers/node-version.js';
89

910
setFixtureDirectory();
1011

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

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

6363
const {exitCode, isTerminated, signal, code} = await t.throwsAsync(subprocess);
64-
6564
// On Windows, calling `subprocess.kill()` twice emits an `error` event on the subprocess.
6665
// This does not happen when passing an `error` argument, nor when passing a non-terminating signal.
6766
// There is no easy way to make this cross-platform, so we document the difference here.
68-
if (isWindows && majorNodeVersion >= 22) {
67+
if (isWindows && majorNodeVersion === 22) {
6968
t.is(exitCode, undefined);
7069
t.false(isTerminated);
7170
t.is(signal, undefined);

0 commit comments

Comments
 (0)