diff --git a/src/js/builtins/ConsoleObject.ts b/src/js/builtins/ConsoleObject.ts index a69076257668..292783d82b05 100644 --- a/src/js/builtins/ConsoleObject.ts +++ b/src/js/builtins/ConsoleObject.ts @@ -143,7 +143,9 @@ export function write(this: Console, input) { // TODO: probably could extract `getStringWidth`; probably make that a native function. note how it is copied from `readline.js` export function createConsoleConstructor(console: typeof globalThis.console) { const { inspect, formatWithOptions } = require("node:util"); - const { isBuffer } = require("node:buffer"); + const { + Buffer: { isBuffer }, + } = require("node:buffer"); const { isMapIterator, isSetIterator } = require("node:util/types"); const { validateObject, validateInteger, validateArray, validateOneOf } = require("internal/validators"); diff --git a/test/js/node/console/console.test.ts b/test/js/node/console/console.test.ts index 2913817f23c8..7cdd4c00332b 100644 --- a/test/js/node/console/console.test.ts +++ b/test/js/node/console/console.test.ts @@ -65,6 +65,123 @@ describe("console.Console", () => { }); }); +describe("console.Console#table", () => { + function table(tabularData: unknown, properties?: string[]) { + let out = ""; + const sink = () => + new Writable({ + decodeStrings: false, + write(chunk, _encoding, callback) { + out += chunk; + callback(); + }, + }); + const c = new Console({ stdout: sink(), stderr: sink(), colorMode: false }); + c.table(tabularData, properties); + expect(out).not.toBe(""); + return out; + } + + test("rows with an object cell", () => { + expect(table([{ a: { x: 1 } }, { a: 2 }])).toMatchInlineSnapshot(` + "┌─────────┬──────────┐ + │ (index) │ a │ + ├─────────┼──────────┤ + │ 0 │ { x: 1 } │ + │ 1 │ 2 │ + └─────────┴──────────┘ + " + `); + }); + + test("array rows with an object cell", () => { + expect(table([[1, { y: 2 }]])).toMatchInlineSnapshot(` + "┌─────────┬───┬──────────┐ + │ (index) │ 0 │ 1 │ + ├─────────┼───┼──────────┤ + │ 0 │ 1 │ { y: 2 } │ + └─────────┴───┴──────────┘ + " + `); + }); + + test("Map with an object key and an object value", () => { + expect( + table( + new Map([ + ["k1", { a: 1 }], + [{ k: 2 }, 2], + ]), + ), + ).toMatchInlineSnapshot(` + "┌───────────────────┬──────────┬──────────┐ + │ (iteration index) │ Key │ Values │ + ├───────────────────┼──────────┼──────────┤ + │ 0 │ 'k1' │ { a: 1 } │ + │ 1 │ { k: 2 } │ 2 │ + └───────────────────┴──────────┴──────────┘ + " + `); + }); + + test("Set with an object value", () => { + expect(table(new Set([1, "two", { x: 3 }]))).toMatchInlineSnapshot(` + "┌───────────────────┬──────────┐ + │ (iteration index) │ Values │ + ├───────────────────┼──────────┤ + │ 0 │ 1 │ + │ 1 │ 'two' │ + │ 2 │ { x: 3 } │ + └───────────────────┴──────────┘ + " + `); + }); + + test("Buffer cell", () => { + expect(table([{ a: Buffer.from([1, 2, 3]) }])).toMatchInlineSnapshot(` + "┌─────────┬───────────────────┐ + │ (index) │ a │ + ├─────────┼───────────────────┤ + │ 0 │ │ + └─────────┴───────────────────┘ + " + `); + }); + + test("properties filter with an object cell", () => { + expect(table([{ a: { x: 1 }, b: 2 }], ["a"])).toMatchInlineSnapshot(` + "┌─────────┬──────────┐ + │ (index) │ a │ + ├─────────┼──────────┤ + │ 0 │ { x: 1 } │ + └─────────┴──────────┘ + " + `); + }); + + test("object cell with more than two keys collapses to [Object]", () => { + expect(table([{ a: { p: 1, q: 2, r: 3 } }])).toMatchInlineSnapshot(` + "┌─────────┬──────────┐ + │ (index) │ a │ + ├─────────┼──────────┤ + │ 0 │ [Object] │ + └─────────┴──────────┘ + " + `); + }); + + test("primitive cells", () => { + expect(table([{ a: 1, b: "x" }])).toMatchInlineSnapshot(` + "┌─────────┬───┬─────┐ + │ (index) │ a │ b │ + ├─────────┼───┼─────┤ + │ 0 │ 1 │ 'x' │ + └─────────┴───┴─────┘ + " + `); + }); +}); + test("console._stdout", () => { // @ts-ignore expect(console._stdout).toBe(process.stdout);