Files
bun.sh/test/js/node/util/node-inspect-tests/parallel/util-format.test.js
Yash Singh bd2176ffb0 fix: format specifier without characters in between (#9209)
* fix: format specifier without spaces in between, resolves #9208

* chore: test in console-log.test.ts
2024-03-04 17:46:19 -08:00

495 lines
19 KiB
JavaScript

// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import assert from "assert";
import util from "util";
const symbol = Symbol("foo");
test("no assertion failures", () => {
assert.strictEqual(util.format(), "");
assert.strictEqual(util.format(""), "");
assert.strictEqual(util.format([]), "[]");
assert.strictEqual(util.format([0]), "[ 0 ]");
assert.strictEqual(util.format({}), "{}");
assert.strictEqual(util.format({ foo: 42 }), "{ foo: 42 }");
assert.strictEqual(util.format(null), "null");
assert.strictEqual(util.format(true), "true");
assert.strictEqual(util.format(false), "false");
assert.strictEqual(util.format("test"), "test");
// CHECKME this is for console.log() compatibility - but is it *right*?
assert.strictEqual(util.format("foo", "bar", "baz"), "foo bar baz");
// ES6 Symbol handling
assert.strictEqual(util.format(symbol), "Symbol(foo)");
assert.strictEqual(util.format("foo", symbol), "foo Symbol(foo)");
assert.strictEqual(util.format("%s", symbol), "Symbol(foo)");
assert.strictEqual(util.format("%j", symbol), "undefined");
// Number format specifier
assert.strictEqual(util.format("%d"), "%d");
assert.strictEqual(util.format("%d", 42.0), "42");
assert.strictEqual(util.format("%d", 42), "42");
assert.strictEqual(util.format("%d", "42"), "42");
assert.strictEqual(util.format("%d", "42.0"), "42");
assert.strictEqual(util.format("%d", 1.5), "1.5");
assert.strictEqual(util.format("%d", -0.5), "-0.5");
assert.strictEqual(util.format("%d", -0.0), "-0");
assert.strictEqual(util.format("%d", ""), "0");
assert.strictEqual(util.format("%d", " -0.000"), "-0");
assert.strictEqual(util.format("%d", Symbol()), "NaN");
assert.strictEqual(util.format("%d", Infinity), "Infinity");
assert.strictEqual(util.format("%d", -Infinity), "-Infinity");
assert.strictEqual(util.format("%d %d", 42, 43), "42 43");
assert.strictEqual(util.format("%d%d", 42, 43), "4243");
assert.strictEqual(util.format("%d %d", 42), "42 %d");
assert.strictEqual(util.format("%d%d", 42), "42%d");
assert.strictEqual(util.format("%d", 1180591620717411303424), "1.1805916207174113e+21");
assert.strictEqual(util.format("%d", 1180591620717411303424n), "1180591620717411303424n");
assert.strictEqual(
util.format("%d %d", 1180591620717411303424n, 12345678901234567890123n),
"1180591620717411303424n 12345678901234567890123n",
);
{
const { numericSeparator } = util.inspect.defaultOptions;
util.inspect.defaultOptions.numericSeparator = true;
assert.strictEqual(util.format("%d", 1180591620717411303424), "1.1805916207174113e+21");
assert.strictEqual(
util.format("%d %s %i", 118059162071741130342, 118059162071741130342, 123_123_123),
"118_059_162_071_741_140_000 118_059_162_071_741_140_000 123_123_123",
);
assert.strictEqual(
util.format("%d %s", 1_180_591_620_717_411_303_424n, 12_345_678_901_234_567_890_123n),
"1_180_591_620_717_411_303_424n 12_345_678_901_234_567_890_123n",
);
assert.strictEqual(util.format("%i", 1_180_591_620_717_411_303_424n), "1_180_591_620_717_411_303_424n");
util.inspect.defaultOptions.numericSeparator = numericSeparator;
}
// Integer format specifier
assert.strictEqual(util.format("%i"), "%i");
assert.strictEqual(util.format("%i", 42.0), "42");
assert.strictEqual(util.format("%i", 42), "42");
assert.strictEqual(util.format("%i", "42"), "42");
assert.strictEqual(util.format("%i", "42.0"), "42");
assert.strictEqual(util.format("%i", 1.5), "1");
assert.strictEqual(util.format("%i", -0.5), "-0");
assert.strictEqual(util.format("%i", ""), "NaN");
assert.strictEqual(util.format("%i", Infinity), "NaN");
assert.strictEqual(util.format("%i", -Infinity), "NaN");
assert.strictEqual(util.format("%i", Symbol()), "NaN");
assert.strictEqual(util.format("%i %i", 42, 43), "42 43");
assert.strictEqual(util.format("%i %i", 42), "42 %i");
assert.strictEqual(util.format("%i", 1180591620717411303424), "1");
assert.strictEqual(util.format("%i", 1180591620717411303424n), "1180591620717411303424n");
assert.strictEqual(
util.format("%i %i", 1180591620717411303424n, 12345678901234567890123n),
"1180591620717411303424n 12345678901234567890123n",
);
assert.strictEqual(
util.format("%d %i", 1180591620717411303424n, 12345678901234567890123n),
"1180591620717411303424n 12345678901234567890123n",
);
assert.strictEqual(
util.format("%i %d", 1180591620717411303424n, 12345678901234567890123n),
"1180591620717411303424n 12345678901234567890123n",
);
assert.strictEqual(
util.formatWithOptions({ numericSeparator: true }, "%i %d", 1180591620717411303424n, 12345678901234567890123n),
"1_180_591_620_717_411_303_424n 12_345_678_901_234_567_890_123n",
);
// Float format specifier
assert.strictEqual(util.format("%f"), "%f");
assert.strictEqual(util.format("%f", 42.0), "42");
assert.strictEqual(util.format("%f", 42), "42");
assert.strictEqual(util.format("%f", "42"), "42");
assert.strictEqual(util.format("%f", "-0.0"), "-0");
assert.strictEqual(util.format("%f", "42.0"), "42");
assert.strictEqual(util.format("%f", 1.5), "1.5");
assert.strictEqual(util.format("%f", -0.5), "-0.5");
assert.strictEqual(util.format("%f", Math.PI), "3.141592653589793");
assert.strictEqual(util.format("%f", ""), "NaN");
assert.strictEqual(util.format("%f", Symbol("foo")), "NaN");
assert.strictEqual(util.format("%f", 5n), "5");
assert.strictEqual(util.format("%f", Infinity), "Infinity");
assert.strictEqual(util.format("%f", -Infinity), "-Infinity");
assert.strictEqual(util.format("%f %f", 42, 43), "42 43");
assert.strictEqual(util.format("%f %f", 42), "42 %f");
// String format specifier
assert.strictEqual(util.format("%s"), "%s");
assert.strictEqual(util.format("%s", undefined), "undefined");
assert.strictEqual(util.format("%s", null), "null");
assert.strictEqual(util.format("%s", "foo"), "foo");
assert.strictEqual(util.format("%s", 42), "42");
assert.strictEqual(util.format("%s", "42"), "42");
assert.strictEqual(util.format("%s", -0), "-0");
assert.strictEqual(util.format("%s", "-0.0"), "-0.0");
assert.strictEqual(util.format("%s %s", 42, 43), "42 43");
assert.strictEqual(util.format("%s%s", 42, 43), "4243");
assert.strictEqual(util.format("%s %s", 42), "42 %s");
assert.strictEqual(util.format("%s%s", 42), "42%s");
assert.strictEqual(util.format("%s", 42n), "42n");
assert.strictEqual(util.format("%s", Symbol("foo")), "Symbol(foo)");
assert.strictEqual(util.format("%s", true), "true");
assert.strictEqual(util.format("%s", { a: [1, 2, 3] }), "{ a: [Array] }");
assert.strictEqual(
util.format("%s", {
toString() {
return "Foo";
},
}),
"Foo",
);
assert.strictEqual(util.format("%s", { toString: 5 }), "{ toString: 5 }");
assert.strictEqual(
util.format("%s", () => 5),
"() => 5",
);
assert.strictEqual(util.format("%s", Infinity), "Infinity");
assert.strictEqual(util.format("%s", -Infinity), "-Infinity");
// String format specifier including `toString` properties on the prototype.
{
class Foo {
toString() {
return "Bar";
}
}
assert.strictEqual(util.format("%s", new Foo()), "Bar");
// TODO: null prototypes
// assert.strictEqual(
// util.format('%s', Object.setPrototypeOf(new Foo(), null)),
// '[Foo: null prototype] {}'
// );
global.Foo = Foo;
assert.strictEqual(util.format("%s", new Foo()), "Bar");
delete global.Foo;
class Bar {
abc = true;
}
assert.strictEqual(util.format("%s", new Bar()), "Bar { abc: true }");
class Foobar extends Array {
aaa = true;
}
assert.strictEqual(util.format("%s", new Foobar(5)), "Foobar(5) [ <5 empty items>, aaa: true ]");
// Subclassing:
class B extends Foo {}
function C() {}
C.prototype.toString = function () {
return "Custom";
};
function D() {
C.call(this);
}
D.prototype = { __proto__: C.prototype };
assert.strictEqual(util.format("%s", new B()), "Bar");
assert.strictEqual(util.format("%s", new C()), "Custom");
assert.strictEqual(util.format("%s", new D()), "Custom");
D.prototype.constructor = D;
assert.strictEqual(util.format("%s", new D()), "Custom");
D.prototype.constructor = null;
assert.strictEqual(util.format("%s", new D()), "Custom");
D.prototype.constructor = { name: "Foobar" };
assert.strictEqual(util.format("%s", new D()), "Custom");
Object.defineProperty(D.prototype, "constructor", {
get() {
throw new Error();
},
configurable: true,
});
assert.strictEqual(util.format("%s", new D()), "Custom");
assert.strictEqual(util.format("%s", { __proto__: null }), "[Object: null prototype] {}");
}
// JSON format specifier
assert.strictEqual(util.format("%j"), "%j");
assert.strictEqual(util.format("%j", 42), "42");
assert.strictEqual(util.format("%j", "42"), '"42"');
assert.strictEqual(util.format("%j %j", 42, 43), "42 43");
assert.strictEqual(util.format("%j%j", 42, null), "42null");
assert.strictEqual(util.format("%j %j", 42), "42 %j");
// Object format specifier
const obj = {
foo: "bar",
foobar: 1,
func: function () {},
};
const nestedObj = {
foo: "bar",
foobar: {
foo: "bar",
func: function () {},
},
};
const nestedObj2 = {
foo: "bar",
foobar: 1,
func: [{ a: function () {} }],
};
assert.strictEqual(util.format("%o"), "%o");
assert.strictEqual(util.format("%o", 42), "42");
assert.strictEqual(util.format("%o", "foo"), "'foo'");
assert.strictEqual(
util.format("%o", obj),
"{\n" +
" foo: 'bar',\n" +
" foobar: 1,\n" +
" func: <ref *1> [Function: func] {\n" +
" [length]: 0,\n" +
" [name]: 'func',\n" +
" [prototype]: { [constructor]: [Circular *1] }\n" +
" }\n" +
"}",
);
assert.strictEqual(
util.format("%o", nestedObj2),
"{\n" +
" foo: 'bar',\n" +
" foobar: 1,\n" +
" func: [\n" +
" {\n" +
" a: <ref *1> [Function: a] {\n" +
" [length]: 0,\n" +
" [name]: 'a',\n" +
" [prototype]: { [constructor]: [Circular *1] }\n" +
" }\n" +
" },\n" +
" [length]: 1\n" +
" ]\n" +
"}",
);
assert.strictEqual(
util.format("%o", nestedObj),
"{\n" +
" foo: 'bar',\n" +
" foobar: {\n" +
" foo: 'bar',\n" +
" func: <ref *1> [Function: func] {\n" +
" [length]: 0,\n" +
" [name]: 'func',\n" +
" [prototype]: { [constructor]: [Circular *1] }\n" +
" }\n" +
" }\n" +
"}",
);
assert.strictEqual(
util.format("%o %o", obj, obj),
"{\n" +
" foo: 'bar',\n" +
" foobar: 1,\n" +
" func: <ref *1> [Function: func] {\n" +
" [prototype]: { [constructor]: [Circular *1] },\n" +
" [name]: 'func',\n" +
" [length]: 0\n" +
" }\n" +
"} {\n" +
" foo: 'bar',\n" +
" foobar: 1,\n" +
" func: <ref *1> [Function: func] {\n" +
" [prototype]: { [constructor]: [Circular *1] },\n" +
" [name]: 'func',\n" +
" [length]: 0\n" +
" }\n" +
"}",
);
assert.strictEqual(
util.format("%o %o", obj),
"{\n" +
" foo: 'bar',\n" +
" foobar: 1,\n" +
" func: <ref *1> [Function: func] {\n" +
" [prototype]: { [constructor]: [Circular *1] },\n" +
" [name]: 'func',\n" +
" [length]: 0\n" +
" }\n" +
"} %o",
);
assert.strictEqual(util.format("%O"), "%O");
assert.strictEqual(util.format("%O", 42), "42");
assert.strictEqual(util.format("%O", "foo"), "'foo'");
assert.strictEqual(util.format("%O", obj), "{ foo: 'bar', foobar: 1, func: [Function: func] }");
assert.strictEqual(util.format("%O", nestedObj), "{ foo: 'bar', foobar: { foo: 'bar', func: [Function: func] } }");
assert.strictEqual(
util.format("%O %O", obj, obj),
"{ foo: 'bar', foobar: 1, func: [Function: func] } " + "{ foo: 'bar', foobar: 1, func: [Function: func] }",
);
assert.strictEqual(util.format("%O %O", obj), "{ foo: 'bar', foobar: 1, func: [Function: func] } %O");
// Various format specifiers
assert.strictEqual(util.format("%%s%s", "foo"), "%sfoo");
assert.strictEqual(util.format("%s:%s"), "%s:%s");
assert.strictEqual(util.format("%s:%s", undefined), "undefined:%s");
assert.strictEqual(util.format("%s:%s", "foo"), "foo:%s");
assert.strictEqual(util.format("%s:%i", "foo"), "foo:%i");
assert.strictEqual(util.format("%s:%f", "foo"), "foo:%f");
assert.strictEqual(util.format("%s:%s", "foo", "bar"), "foo:bar");
assert.strictEqual(util.format("%s:%s", "foo", "bar", "baz"), "foo:bar baz");
assert.strictEqual(util.format("%%%s%%", "hi"), "%hi%");
assert.strictEqual(util.format("%%%s%%%%", "hi"), "%hi%%");
assert.strictEqual(util.format("%sbc%%def", "a"), "abc%def");
assert.strictEqual(util.format("%d:%d", 12, 30), "12:30");
assert.strictEqual(util.format("%d:%d", 12), "12:%d");
assert.strictEqual(util.format("%d:%d"), "%d:%d");
assert.strictEqual(util.format("%i:%i", 12, 30), "12:30");
assert.strictEqual(util.format("%i:%i", 12), "12:%i");
assert.strictEqual(util.format("%i:%i"), "%i:%i");
assert.strictEqual(util.format("%f:%f", 12, 30), "12:30");
assert.strictEqual(util.format("%f:%f", 12), "12:%f");
assert.strictEqual(util.format("%f:%f"), "%f:%f");
assert.strictEqual(util.format("o: %j, a: %j", {}, []), "o: {}, a: []");
assert.strictEqual(util.format("o: %j, a: %j", {}), "o: {}, a: %j");
assert.strictEqual(util.format("o: %j, a: %j"), "o: %j, a: %j");
assert.strictEqual(util.format("o: %o, a: %O", {}, []), "o: {}, a: []");
assert.strictEqual(util.format("o: %o, a: %o", {}), "o: {}, a: %o");
assert.strictEqual(util.format("o: %O, a: %O"), "o: %O, a: %O");
// Invalid format specifiers
assert.strictEqual(util.format("a% b", "x"), "a% b x");
assert.strictEqual(util.format("percent: %d%, fraction: %d", 10, 0.1), "percent: 10%, fraction: 0.1");
assert.strictEqual(util.format("abc%", 1), "abc% 1");
// Additional arguments after format specifiers
assert.strictEqual(util.format("%i", 1, "number"), "1 number");
assert.strictEqual(
util.format("%i", 1, () => {}),
"1 [Function (anonymous)]",
);
// %c from https://console.spec.whatwg.org/
assert.strictEqual(util.format("%c"), "%c");
assert.strictEqual(util.format("%cab"), "%cab");
assert.strictEqual(util.format("%cab", "color: blue"), "ab");
assert.strictEqual(util.format("%cab", "color: blue", "c"), "ab c");
{
const o = {};
o.o = o;
assert.strictEqual(util.format("%j", o), "[Circular]");
}
{
const o = {
toJSON() {
throw new Error("Not a circular object but still not serializable");
},
};
assert.throws(() => util.format("%j", o), /^Error: Not a circular object but still not serializable$/);
}
// Errors
const err = new Error("foo");
assert(util.format(err).startsWith(err.stack), `Expected "${util.format(err)}" to start with "${err.stack}"`);
class CustomError extends Error {
constructor(msg) {
super();
Object.defineProperty(this, "message", { value: msg, enumerable: false });
Object.defineProperty(this, "name", { value: "CustomError", enumerable: false });
Error.captureStackTrace(this, CustomError);
}
}
const customError = new CustomError("bar");
assert.strictEqual(util.format(customError), customError.stack.replace(/^Error/, "Custom$&")); //! temp bug workaround
// Doesn't capture stack trace
function BadCustomError(msg) {
Error.call(this);
Object.defineProperty(this, "message", { value: msg, enumerable: false });
Object.defineProperty(this, "name", { value: "BadCustomError", enumerable: false });
}
Object.setPrototypeOf(BadCustomError.prototype, Error.prototype);
Object.setPrototypeOf(BadCustomError, Error);
assert.strictEqual(util.format(new BadCustomError("foo")), "[BadCustomError: foo]");
// The format of arguments should not depend on type of the first argument
assert.strictEqual(util.format("1", "1"), "1 1");
assert.strictEqual(util.format(1, "1"), "1 1");
assert.strictEqual(util.format("1", 1), "1 1");
assert.strictEqual(util.format(1, -0), "1 -0");
assert.strictEqual(
util.format("1", () => {}),
"1 [Function (anonymous)]",
);
assert.strictEqual(
util.format(1, () => {}),
"1 [Function (anonymous)]",
);
assert.strictEqual(util.format("1", "'"), "1 '");
assert.strictEqual(util.format(1, "'"), "1 '");
assert.strictEqual(util.format("1", "number"), "1 number");
assert.strictEqual(util.format(1, "number"), "1 number");
assert.strictEqual(util.format(5n), "5n");
assert.strictEqual(util.format(5n, 5n), "5n 5n");
// Check `formatWithOptions`.
assert.strictEqual(
util.formatWithOptions({ colors: true }, true, undefined, Symbol(), 1, 5n, null, "foobar"),
"\u001b[33mtrue\u001b[39m " +
"\u001b[90mundefined\u001b[39m " +
"\u001b[32mSymbol()\u001b[39m " +
"\u001b[33m1\u001b[39m " +
"\u001b[33m5n\u001b[39m " +
"\u001b[1mnull\u001b[22m " +
"foobar",
);
assert.strictEqual(
util.format(new SharedArrayBuffer(4)),
"SharedArrayBuffer { [Uint8Contents]: <00 00 00 00>, byteLength: 4 }",
);
assert.strictEqual(util.formatWithOptions({ colors: true, compact: 3 }, "%s", [1, { a: true }]), "[ 1, [Object] ]");
[undefined, null, false, 5n, 5, "test", Symbol()].forEach(invalidOptions => {
assert.throws(
() => {
util.formatWithOptions(invalidOptions, { a: true });
},
{
code: "ERR_INVALID_ARG_TYPE",
message: /"inspectOptions".+object/,
},
);
});
});