Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/js/internal/fs/cp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const {
const { dirname, isAbsolute, join, parse, resolve } = require("node:path");

const PromisePrototypeThen = $Promise.prototype.$then;
const PromiseReject = Promise.$reject;
const PromiseReject = Promise.$reject.bind(Promise);

async function checkPaths(src, dest, opts) {
if (opts.filter && !(await opts.filter(src, dest))) {
Expand Down
2 changes: 1 addition & 1 deletion src/js/internal/primordials.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const arrayToSafePromiseIterable = (promises, mapFn) =>
new Promise((a, b) => PromisePrototypeThen.$call(mapFn == null ? promise : mapFn(promise, i), a, b)),
),
);
const PromiseAll = Promise.all;
const PromiseAll = Promise.all.bind(Promise);
const PromiseResolve = Promise.$resolve.bind(Promise);
const SafePromiseAll = (promises, mapFn) => PromiseAll(arrayToSafePromiseIterable(promises, mapFn));
// Shared scheduler for SafePromiseAllReturnVoid/ReturnArrayLike: `returnVal`
Expand Down
2 changes: 1 addition & 1 deletion src/js/node/readline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const {

const internalGetStringWidth = $newCppFunction("stringWidth.cpp", "jsFunctionBunStringWidth", 1);

const PromiseReject = Promise.$reject;
const PromiseReject = Promise.$reject.bind(Promise);

var isWritable;

Expand Down
62 changes: 62 additions & 0 deletions test/js/node/readline/readline_promises.node.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { createTest } from "node-harness";
import { EventEmitter } from "node:events";
import readline from "node:readline";
import readlinePromises from "node:readline/promises";
import { promisify } from "node:util";
const { describe, it, expect, createDoneDotAll, createCallCheckCtx, assert } = createTest(import.meta.path);

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -93,3 +95,63 @@ describe("readline/promises.createInterface()", () => {
assert.strictEqual(rl.closed, true);
});
});

describe("readline question() with an aborted signal", () => {
async function rejection(promise: Promise<unknown>) {
return await promise.then(
() => {
throw new Error("question() resolved, expected it to reject");
},
(err: any) => err,
);
}

it("rejects instead of throwing synchronously", async () => {
const fi = new FakeInput();
using rl = readlinePromises.createInterface({ input: fi, output: fi });

const reason = new Error("cancelled before the prompt");
const promise = rl.question("Question?", { signal: AbortSignal.abort(reason) });
expect(promise).toBeInstanceOf(Promise);

const error = await rejection(promise);
expect({ name: error.name, code: error.code, cause: error.cause }).toEqual({
name: "AbortError",
code: "ABORT_ERR",
cause: reason,
});
// the prompt is never written when the signal is already aborted
expect(fi.output).toBe("");
});

it("rejects instead of throwing synchronously through util.promisify", async () => {
const fi = new FakeInput();
using rl = readline.createInterface({ input: fi, output: fi });
const question = promisify(rl.question);

const reason = new Error("cancelled before the prompt");
const promise = question.call(rl, "Question?", { signal: AbortSignal.abort(reason) });
expect(promise).toBeInstanceOf(Promise);

const error = await rejection(promise);
expect({ name: error.name, code: error.code, cause: error.cause }).toEqual({
name: "AbortError",
code: "ABORT_ERR",
cause: reason,
});
expect(fi.output).toBe("");
});

it("rejects when the signal aborts after the question was asked", async () => {
const fi = new FakeInput();
using rl = readlinePromises.createInterface({ input: fi, output: fi });

const controller = new AbortController();
const promise = rl.question("Question?", { signal: controller.signal });
expect(fi.output).toBe("Question?");
controller.abort();

const error = await rejection(promise);
expect({ name: error.name, code: error.code }).toEqual({ name: "AbortError", code: "ABORT_ERR" });
});
});
Loading