Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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