mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 21:19:50 +01:00
e973c3e94b
PR-URL: https://github.com/nodejs/node/pull/54965 Fixes: https://github.com/nodejs/node/issues/54962 Reviewed-By: Matthew Aitken <maitken033380023@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
135 lines
5.3 KiB
JavaScript
135 lines
5.3 KiB
JavaScript
import * as common from '../common/index.mjs';
|
|
import { describe, it } from 'node:test';
|
|
import { once } from 'node:events';
|
|
import assert from 'node:assert';
|
|
|
|
describe('AbortSignal.any()', { concurrency: !process.env.TEST_PARALLEL }, () => {
|
|
it('should throw when not receiving an array', () => {
|
|
const expectedError = { code: 'ERR_INVALID_ARG_TYPE' };
|
|
assert.throws(() => AbortSignal.any(), expectedError);
|
|
assert.throws(() => AbortSignal.any(null), expectedError);
|
|
assert.throws(() => AbortSignal.any(undefined), expectedError);
|
|
});
|
|
|
|
it('should throw when input contains non-signal values', () => {
|
|
assert.throws(
|
|
() => AbortSignal.any([AbortSignal.abort(), undefined]),
|
|
{
|
|
code: 'ERR_INVALID_ARG_TYPE',
|
|
message: 'The "signals[1]" argument must be an instance of AbortSignal. Received undefined'
|
|
},
|
|
);
|
|
});
|
|
|
|
it('creates a non-aborted signal for an empty input', () => {
|
|
const signal = AbortSignal.any([]);
|
|
assert.strictEqual(signal.aborted, false);
|
|
signal.addEventListener('abort', common.mustNotCall());
|
|
});
|
|
|
|
it('returns a new signal', () => {
|
|
const originalSignal = new AbortController().signal;
|
|
const signalAny = AbortSignal.any([originalSignal]);
|
|
assert.notStrictEqual(originalSignal, signalAny);
|
|
});
|
|
|
|
it('returns an aborted signal if input has an aborted signal', () => {
|
|
const signal = AbortSignal.any([AbortSignal.abort('some reason')]);
|
|
assert.strictEqual(signal.aborted, true);
|
|
assert.strictEqual(signal.reason, 'some reason');
|
|
signal.addEventListener('abort', common.mustNotCall());
|
|
});
|
|
|
|
it('returns an aborted signal with the reason of first aborted signal input', () => {
|
|
const signal = AbortSignal.any([AbortSignal.abort('some reason'), AbortSignal.abort('another reason')]);
|
|
assert.strictEqual(signal.aborted, true);
|
|
assert.strictEqual(signal.reason, 'some reason');
|
|
signal.addEventListener('abort', common.mustNotCall());
|
|
});
|
|
|
|
it('returns the correct signal in the event target', async () => {
|
|
const signal = AbortSignal.any([AbortSignal.timeout(5)]);
|
|
const interval = setInterval(() => {}, 100000); // Keep event loop alive
|
|
const [{ target }] = await once(signal, 'abort');
|
|
clearInterval(interval);
|
|
assert.strictEqual(target, signal);
|
|
assert.ok(signal.aborted);
|
|
assert.strictEqual(signal.reason.name, 'TimeoutError');
|
|
});
|
|
|
|
it('aborts with reason of first aborted signal', () => {
|
|
const controllers = Array.from({ length: 3 }, () => new AbortController());
|
|
const combinedSignal = AbortSignal.any(controllers.map((c) => c.signal));
|
|
controllers[1].abort(1);
|
|
controllers[2].abort(2);
|
|
assert.ok(combinedSignal.aborted);
|
|
assert.strictEqual(combinedSignal.reason, 1);
|
|
});
|
|
|
|
it('can accept the same signal more than once', () => {
|
|
const controller = new AbortController();
|
|
const signal = AbortSignal.any([controller.signal, controller.signal]);
|
|
assert.strictEqual(signal.aborted, false);
|
|
controller.abort('reason');
|
|
assert.ok(signal.aborted);
|
|
assert.strictEqual(signal.reason, 'reason');
|
|
});
|
|
|
|
it('handles deeply aborted signals', async () => {
|
|
const controllers = Array.from({ length: 2 }, () => new AbortController());
|
|
const composedSignal1 = AbortSignal.any([controllers[0].signal]);
|
|
const composedSignal2 = AbortSignal.any([composedSignal1, controllers[1].signal]);
|
|
|
|
composedSignal2.onabort = common.mustCall();
|
|
controllers[0].abort();
|
|
assert.ok(composedSignal2.aborted);
|
|
assert.ok(composedSignal2.reason instanceof DOMException);
|
|
assert.strictEqual(composedSignal2.reason.name, 'AbortError');
|
|
});
|
|
|
|
it('executes abort handlers in correct order', () => {
|
|
const controller = new AbortController();
|
|
const signals = [];
|
|
signals.push(controller.signal);
|
|
signals.push(AbortSignal.any([controller.signal]));
|
|
signals.push(AbortSignal.any([controller.signal]));
|
|
signals.push(AbortSignal.any([signals[0]]));
|
|
signals.push(AbortSignal.any([signals[1]]));
|
|
|
|
let result = '';
|
|
signals.forEach((signal, i) => signal.addEventListener('abort', () => result += i));
|
|
controller.abort();
|
|
assert.strictEqual(result, '01234');
|
|
});
|
|
|
|
it('must accept WebIDL sequence', () => {
|
|
const controller = new AbortController();
|
|
const iterable = {
|
|
*[Symbol.iterator]() {
|
|
yield controller.signal;
|
|
yield new AbortController().signal;
|
|
yield new AbortController().signal;
|
|
yield new AbortController().signal;
|
|
},
|
|
};
|
|
const signal = AbortSignal.any(iterable);
|
|
let result = 0;
|
|
signal.addEventListener('abort', () => result += 1);
|
|
controller.abort();
|
|
assert.strictEqual(result, 1);
|
|
});
|
|
|
|
it('throws TypeError if any value does not implement AbortSignal', () => {
|
|
const expectedError = { code: 'ERR_INVALID_ARG_TYPE' };
|
|
assert.throws(() => AbortSignal.any([ null ]), expectedError);
|
|
assert.throws(() => AbortSignal.any([ undefined ]), expectedError);
|
|
assert.throws(() => AbortSignal.any([ '123' ]), expectedError);
|
|
assert.throws(() => AbortSignal.any([ 123 ]), expectedError);
|
|
assert.throws(() => AbortSignal.any([{}]), expectedError);
|
|
assert.throws(() => AbortSignal.any([{ aborted: true }]), expectedError);
|
|
assert.throws(() => AbortSignal.any([{
|
|
aborted: true, reason: '', throwIfAborted: null,
|
|
}]), expectedError);
|
|
});
|
|
});
|