1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22'use strict'; 23require('../common'); 24const assert = require('assert'); 25const util = require('util'); 26const symbol = Symbol('foo'); 27 28assert.strictEqual(util.format(), ''); 29assert.strictEqual(util.format(''), ''); 30assert.strictEqual(util.format([]), '[]'); 31assert.strictEqual(util.format([0]), '[ 0 ]'); 32assert.strictEqual(util.format({}), '{}'); 33assert.strictEqual(util.format({ foo: 42 }), '{ foo: 42 }'); 34assert.strictEqual(util.format(null), 'null'); 35assert.strictEqual(util.format(true), 'true'); 36assert.strictEqual(util.format(false), 'false'); 37assert.strictEqual(util.format('test'), 'test'); 38 39// CHECKME this is for console.log() compatibility - but is it *right*? 40assert.strictEqual(util.format('foo', 'bar', 'baz'), 'foo bar baz'); 41 42// ES6 Symbol handling 43assert.strictEqual(util.format(symbol), 'Symbol(foo)'); 44assert.strictEqual(util.format('foo', symbol), 'foo Symbol(foo)'); 45assert.strictEqual(util.format('%s', symbol), 'Symbol(foo)'); 46assert.strictEqual(util.format('%j', symbol), 'undefined'); 47 48// Number format specifier 49assert.strictEqual(util.format('%d'), '%d'); 50assert.strictEqual(util.format('%d', 42.0), '42'); 51assert.strictEqual(util.format('%d', 42), '42'); 52assert.strictEqual(util.format('%d', '42'), '42'); 53assert.strictEqual(util.format('%d', '42.0'), '42'); 54assert.strictEqual(util.format('%d', 1.5), '1.5'); 55assert.strictEqual(util.format('%d', -0.5), '-0.5'); 56assert.strictEqual(util.format('%d', -0.0), '-0'); 57assert.strictEqual(util.format('%d', ''), '0'); 58assert.strictEqual(util.format('%d', ' -0.000'), '-0'); 59assert.strictEqual(util.format('%d', Symbol()), 'NaN'); 60assert.strictEqual(util.format('%d', Infinity), 'Infinity'); 61assert.strictEqual(util.format('%d', -Infinity), '-Infinity'); 62assert.strictEqual(util.format('%d %d', 42, 43), '42 43'); 63assert.strictEqual(util.format('%d %d', 42), '42 %d'); 64assert.strictEqual( 65 util.format('%d', 1180591620717411303424), 66 '1.1805916207174113e+21' 67); 68assert.strictEqual( 69 util.format('%d', 1180591620717411303424n), 70 '1180591620717411303424n' 71); 72assert.strictEqual( 73 util.format('%d %d', 1180591620717411303424n, 12345678901234567890123n), 74 '1180591620717411303424n 12345678901234567890123n' 75); 76 77{ 78 const { numericSeparator } = util.inspect.defaultOptions; 79 util.inspect.defaultOptions.numericSeparator = true; 80 81 assert.strictEqual( 82 util.format('%d', 1180591620717411303424), 83 '1.1805916207174113e+21' 84 ); 85 86 assert.strictEqual( 87 util.format( 88 // eslint-disable-next-line no-loss-of-precision 89 '%d %s %i', 118059162071741130342, 118059162071741130342, 123_123_123), 90 '118_059_162_071_741_140_000 118_059_162_071_741_140_000 123_123_123' 91 ); 92 93 assert.strictEqual( 94 util.format( 95 '%d %s', 96 1_180_591_620_717_411_303_424n, 97 12_345_678_901_234_567_890_123n 98 ), 99 '1_180_591_620_717_411_303_424n 12_345_678_901_234_567_890_123n' 100 ); 101 102 assert.strictEqual( 103 util.format('%i', 1_180_591_620_717_411_303_424n), 104 '1_180_591_620_717_411_303_424n' 105 ); 106 107 util.inspect.defaultOptions.numericSeparator = numericSeparator; 108} 109// Integer format specifier 110assert.strictEqual(util.format('%i'), '%i'); 111assert.strictEqual(util.format('%i', 42.0), '42'); 112assert.strictEqual(util.format('%i', 42), '42'); 113assert.strictEqual(util.format('%i', '42'), '42'); 114assert.strictEqual(util.format('%i', '42.0'), '42'); 115assert.strictEqual(util.format('%i', 1.5), '1'); 116assert.strictEqual(util.format('%i', -0.5), '-0'); 117assert.strictEqual(util.format('%i', ''), 'NaN'); 118assert.strictEqual(util.format('%i', Infinity), 'NaN'); 119assert.strictEqual(util.format('%i', -Infinity), 'NaN'); 120assert.strictEqual(util.format('%i', Symbol()), 'NaN'); 121assert.strictEqual(util.format('%i %i', 42, 43), '42 43'); 122assert.strictEqual(util.format('%i %i', 42), '42 %i'); 123assert.strictEqual( 124 util.format('%i', 1180591620717411303424), 125 '1' 126); 127assert.strictEqual( 128 util.format('%i', 1180591620717411303424n), 129 '1180591620717411303424n' 130); 131assert.strictEqual( 132 util.format('%i %i', 1180591620717411303424n, 12345678901234567890123n), 133 '1180591620717411303424n 12345678901234567890123n' 134); 135 136assert.strictEqual( 137 util.format('%d %i', 1180591620717411303424n, 12345678901234567890123n), 138 '1180591620717411303424n 12345678901234567890123n' 139); 140 141assert.strictEqual( 142 util.format('%i %d', 1180591620717411303424n, 12345678901234567890123n), 143 '1180591620717411303424n 12345678901234567890123n' 144); 145 146assert.strictEqual( 147 util.formatWithOptions( 148 { numericSeparator: true }, 149 '%i %d', 1180591620717411303424n, 12345678901234567890123n), 150 '1_180_591_620_717_411_303_424n 12_345_678_901_234_567_890_123n' 151); 152 153// Float format specifier 154assert.strictEqual(util.format('%f'), '%f'); 155assert.strictEqual(util.format('%f', 42.0), '42'); 156assert.strictEqual(util.format('%f', 42), '42'); 157assert.strictEqual(util.format('%f', '42'), '42'); 158assert.strictEqual(util.format('%f', '-0.0'), '-0'); 159assert.strictEqual(util.format('%f', '42.0'), '42'); 160assert.strictEqual(util.format('%f', 1.5), '1.5'); 161assert.strictEqual(util.format('%f', -0.5), '-0.5'); 162assert.strictEqual(util.format('%f', Math.PI), '3.141592653589793'); 163assert.strictEqual(util.format('%f', ''), 'NaN'); 164assert.strictEqual(util.format('%f', Symbol('foo')), 'NaN'); 165assert.strictEqual(util.format('%f', 5n), '5'); 166assert.strictEqual(util.format('%f', Infinity), 'Infinity'); 167assert.strictEqual(util.format('%f', -Infinity), '-Infinity'); 168assert.strictEqual(util.format('%f %f', 42, 43), '42 43'); 169assert.strictEqual(util.format('%f %f', 42), '42 %f'); 170 171// String format specifier 172assert.strictEqual(util.format('%s'), '%s'); 173assert.strictEqual(util.format('%s', undefined), 'undefined'); 174assert.strictEqual(util.format('%s', null), 'null'); 175assert.strictEqual(util.format('%s', 'foo'), 'foo'); 176assert.strictEqual(util.format('%s', 42), '42'); 177assert.strictEqual(util.format('%s', '42'), '42'); 178assert.strictEqual(util.format('%s', -0), '-0'); 179assert.strictEqual(util.format('%s', '-0.0'), '-0.0'); 180assert.strictEqual(util.format('%s %s', 42, 43), '42 43'); 181assert.strictEqual(util.format('%s %s', 42), '42 %s'); 182assert.strictEqual(util.format('%s', 42n), '42n'); 183assert.strictEqual(util.format('%s', Symbol('foo')), 'Symbol(foo)'); 184assert.strictEqual(util.format('%s', true), 'true'); 185assert.strictEqual(util.format('%s', { a: [1, 2, 3] }), '{ a: [Array] }'); 186assert.strictEqual(util.format('%s', { toString() { return 'Foo'; } }), 'Foo'); 187assert.strictEqual(util.format('%s', { toString: 5 }), '{ toString: 5 }'); 188assert.strictEqual(util.format('%s', () => 5), '() => 5'); 189assert.strictEqual(util.format('%s', Infinity), 'Infinity'); 190assert.strictEqual(util.format('%s', -Infinity), '-Infinity'); 191 192// String format specifier including `toString` properties on the prototype. 193{ 194 class Foo { toString() { return 'Bar'; } } 195 assert.strictEqual(util.format('%s', new Foo()), 'Bar'); 196 assert.strictEqual( 197 util.format('%s', Object.setPrototypeOf(new Foo(), null)), 198 '[Foo: null prototype] {}' 199 ); 200 global.Foo = Foo; 201 assert.strictEqual(util.format('%s', new Foo()), 'Bar'); 202 delete global.Foo; 203 class Bar { abc = true; } 204 assert.strictEqual(util.format('%s', new Bar()), 'Bar { abc: true }'); 205 class Foobar extends Array { aaa = true; } 206 assert.strictEqual( 207 util.format('%s', new Foobar(5)), 208 'Foobar(5) [ <5 empty items>, aaa: true ]' 209 ); 210 211 // Subclassing: 212 class B extends Foo {} 213 214 function C() {} 215 C.prototype.toString = function() { 216 return 'Custom'; 217 }; 218 219 function D() { 220 C.call(this); 221 } 222 D.prototype = Object.create(C.prototype); 223 224 assert.strictEqual( 225 util.format('%s', new B()), 226 'Bar' 227 ); 228 assert.strictEqual( 229 util.format('%s', new C()), 230 'Custom' 231 ); 232 assert.strictEqual( 233 util.format('%s', new D()), 234 'Custom' 235 ); 236 237 D.prototype.constructor = D; 238 assert.strictEqual( 239 util.format('%s', new D()), 240 'Custom' 241 ); 242 243 D.prototype.constructor = null; 244 assert.strictEqual( 245 util.format('%s', new D()), 246 'Custom' 247 ); 248 249 D.prototype.constructor = { name: 'Foobar' }; 250 assert.strictEqual( 251 util.format('%s', new D()), 252 'Custom' 253 ); 254 255 Object.defineProperty(D.prototype, 'constructor', { 256 get() { 257 throw new Error(); 258 }, 259 configurable: true 260 }); 261 assert.strictEqual( 262 util.format('%s', new D()), 263 'Custom' 264 ); 265 266 assert.strictEqual( 267 util.format('%s', Object.create(null)), 268 '[Object: null prototype] {}' 269 ); 270} 271 272// JSON format specifier 273assert.strictEqual(util.format('%j'), '%j'); 274assert.strictEqual(util.format('%j', 42), '42'); 275assert.strictEqual(util.format('%j', '42'), '"42"'); 276assert.strictEqual(util.format('%j %j', 42, 43), '42 43'); 277assert.strictEqual(util.format('%j %j', 42), '42 %j'); 278 279// Object format specifier 280const obj = { 281 foo: 'bar', 282 foobar: 1, 283 func: function() {} 284}; 285const nestedObj = { 286 foo: 'bar', 287 foobar: { 288 foo: 'bar', 289 func: function() {} 290 } 291}; 292const nestedObj2 = { 293 foo: 'bar', 294 foobar: 1, 295 func: [{ a: function() {} }] 296}; 297assert.strictEqual(util.format('%o'), '%o'); 298assert.strictEqual(util.format('%o', 42), '42'); 299assert.strictEqual(util.format('%o', 'foo'), '\'foo\''); 300assert.strictEqual( 301 util.format('%o', obj), 302 '{\n' + 303 ' foo: \'bar\',\n' + 304 ' foobar: 1,\n' + 305 ' func: <ref *1> [Function: func] {\n' + 306 ' [length]: 0,\n' + 307 ' [name]: \'func\',\n' + 308 ' [prototype]: { [constructor]: [Circular *1] }\n' + 309 ' }\n' + 310 '}'); 311assert.strictEqual( 312 util.format('%o', nestedObj2), 313 '{\n' + 314 ' foo: \'bar\',\n' + 315 ' foobar: 1,\n' + 316 ' func: [\n' + 317 ' {\n' + 318 ' a: <ref *1> [Function: a] {\n' + 319 ' [length]: 0,\n' + 320 ' [name]: \'a\',\n' + 321 ' [prototype]: { [constructor]: [Circular *1] }\n' + 322 ' }\n' + 323 ' },\n' + 324 ' [length]: 1\n' + 325 ' ]\n' + 326 '}'); 327assert.strictEqual( 328 util.format('%o', nestedObj), 329 '{\n' + 330 ' foo: \'bar\',\n' + 331 ' foobar: {\n' + 332 ' foo: \'bar\',\n' + 333 ' func: <ref *1> [Function: func] {\n' + 334 ' [length]: 0,\n' + 335 ' [name]: \'func\',\n' + 336 ' [prototype]: { [constructor]: [Circular *1] }\n' + 337 ' }\n' + 338 ' }\n' + 339 '}'); 340assert.strictEqual( 341 util.format('%o %o', obj, obj), 342 '{\n' + 343 ' foo: \'bar\',\n' + 344 ' foobar: 1,\n' + 345 ' func: <ref *1> [Function: func] {\n' + 346 ' [length]: 0,\n' + 347 ' [name]: \'func\',\n' + 348 ' [prototype]: { [constructor]: [Circular *1] }\n' + 349 ' }\n' + 350 '} {\n' + 351 ' foo: \'bar\',\n' + 352 ' foobar: 1,\n' + 353 ' func: <ref *1> [Function: func] {\n' + 354 ' [length]: 0,\n' + 355 ' [name]: \'func\',\n' + 356 ' [prototype]: { [constructor]: [Circular *1] }\n' + 357 ' }\n' + 358 '}'); 359assert.strictEqual( 360 util.format('%o %o', obj), 361 '{\n' + 362 ' foo: \'bar\',\n' + 363 ' foobar: 1,\n' + 364 ' func: <ref *1> [Function: func] {\n' + 365 ' [length]: 0,\n' + 366 ' [name]: \'func\',\n' + 367 ' [prototype]: { [constructor]: [Circular *1] }\n' + 368 ' }\n' + 369 '} %o'); 370 371assert.strictEqual(util.format('%O'), '%O'); 372assert.strictEqual(util.format('%O', 42), '42'); 373assert.strictEqual(util.format('%O', 'foo'), '\'foo\''); 374assert.strictEqual( 375 util.format('%O', obj), 376 '{ foo: \'bar\', foobar: 1, func: [Function: func] }'); 377assert.strictEqual( 378 util.format('%O', nestedObj), 379 '{ foo: \'bar\', foobar: { foo: \'bar\', func: [Function: func] } }'); 380assert.strictEqual( 381 util.format('%O %O', obj, obj), 382 '{ foo: \'bar\', foobar: 1, func: [Function: func] } ' + 383 '{ foo: \'bar\', foobar: 1, func: [Function: func] }'); 384assert.strictEqual( 385 util.format('%O %O', obj), 386 '{ foo: \'bar\', foobar: 1, func: [Function: func] } %O'); 387 388// Various format specifiers 389assert.strictEqual(util.format('%%s%s', 'foo'), '%sfoo'); 390assert.strictEqual(util.format('%s:%s'), '%s:%s'); 391assert.strictEqual(util.format('%s:%s', undefined), 'undefined:%s'); 392assert.strictEqual(util.format('%s:%s', 'foo'), 'foo:%s'); 393assert.strictEqual(util.format('%s:%i', 'foo'), 'foo:%i'); 394assert.strictEqual(util.format('%s:%f', 'foo'), 'foo:%f'); 395assert.strictEqual(util.format('%s:%s', 'foo', 'bar'), 'foo:bar'); 396assert.strictEqual(util.format('%s:%s', 'foo', 'bar', 'baz'), 'foo:bar baz'); 397assert.strictEqual(util.format('%%%s%%', 'hi'), '%hi%'); 398assert.strictEqual(util.format('%%%s%%%%', 'hi'), '%hi%%'); 399assert.strictEqual(util.format('%sbc%%def', 'a'), 'abc%def'); 400assert.strictEqual(util.format('%d:%d', 12, 30), '12:30'); 401assert.strictEqual(util.format('%d:%d', 12), '12:%d'); 402assert.strictEqual(util.format('%d:%d'), '%d:%d'); 403assert.strictEqual(util.format('%i:%i', 12, 30), '12:30'); 404assert.strictEqual(util.format('%i:%i', 12), '12:%i'); 405assert.strictEqual(util.format('%i:%i'), '%i:%i'); 406assert.strictEqual(util.format('%f:%f', 12, 30), '12:30'); 407assert.strictEqual(util.format('%f:%f', 12), '12:%f'); 408assert.strictEqual(util.format('%f:%f'), '%f:%f'); 409assert.strictEqual(util.format('o: %j, a: %j', {}, []), 'o: {}, a: []'); 410assert.strictEqual(util.format('o: %j, a: %j', {}), 'o: {}, a: %j'); 411assert.strictEqual(util.format('o: %j, a: %j'), 'o: %j, a: %j'); 412assert.strictEqual(util.format('o: %o, a: %O', {}, []), 'o: {}, a: []'); 413assert.strictEqual(util.format('o: %o, a: %o', {}), 'o: {}, a: %o'); 414assert.strictEqual(util.format('o: %O, a: %O'), 'o: %O, a: %O'); 415 416 417// Invalid format specifiers 418assert.strictEqual(util.format('a% b', 'x'), 'a% b x'); 419assert.strictEqual(util.format('percent: %d%, fraction: %d', 10, 0.1), 420 'percent: 10%, fraction: 0.1'); 421assert.strictEqual(util.format('abc%', 1), 'abc% 1'); 422 423// Additional arguments after format specifiers 424assert.strictEqual(util.format('%i', 1, 'number'), '1 number'); 425assert.strictEqual(util.format('%i', 1, () => {}), '1 [Function (anonymous)]'); 426 427// %c from https://console.spec.whatwg.org/ 428assert.strictEqual(util.format('%c'), '%c'); 429assert.strictEqual(util.format('%cab'), '%cab'); 430assert.strictEqual(util.format('%cab', 'color: blue'), 'ab'); 431assert.strictEqual(util.format('%cab', 'color: blue', 'c'), 'ab c'); 432 433{ 434 const o = {}; 435 o.o = o; 436 assert.strictEqual(util.format('%j', o), '[Circular]'); 437} 438 439{ 440 const o = { 441 toJSON() { 442 throw new Error('Not a circular object but still not serializable'); 443 } 444 }; 445 assert.throws(() => util.format('%j', o), 446 /^Error: Not a circular object but still not serializable$/); 447} 448 449// Errors 450const err = new Error('foo'); 451assert.strictEqual(util.format(err), err.stack); 452class CustomError extends Error { 453 constructor(msg) { 454 super(); 455 Object.defineProperty(this, 'message', 456 { value: msg, enumerable: false }); 457 Object.defineProperty(this, 'name', 458 { value: 'CustomError', enumerable: false }); 459 Error.captureStackTrace(this, CustomError); 460 } 461} 462const customError = new CustomError('bar'); 463assert.strictEqual(util.format(customError), customError.stack); 464// Doesn't capture stack trace 465function BadCustomError(msg) { 466 Error.call(this); 467 Object.defineProperty(this, 'message', 468 { value: msg, enumerable: false }); 469 Object.defineProperty(this, 'name', 470 { value: 'BadCustomError', enumerable: false }); 471} 472Object.setPrototypeOf(BadCustomError.prototype, Error.prototype); 473Object.setPrototypeOf(BadCustomError, Error); 474assert.strictEqual(util.format(new BadCustomError('foo')), 475 '[BadCustomError: foo]'); 476 477// The format of arguments should not depend on type of the first argument 478assert.strictEqual(util.format('1', '1'), '1 1'); 479assert.strictEqual(util.format(1, '1'), '1 1'); 480assert.strictEqual(util.format('1', 1), '1 1'); 481assert.strictEqual(util.format(1, -0), '1 -0'); 482assert.strictEqual(util.format('1', () => {}), '1 [Function (anonymous)]'); 483assert.strictEqual(util.format(1, () => {}), '1 [Function (anonymous)]'); 484assert.strictEqual(util.format('1', "'"), "1 '"); 485assert.strictEqual(util.format(1, "'"), "1 '"); 486assert.strictEqual(util.format('1', 'number'), '1 number'); 487assert.strictEqual(util.format(1, 'number'), '1 number'); 488assert.strictEqual(util.format(5n), '5n'); 489assert.strictEqual(util.format(5n, 5n), '5n 5n'); 490 491// Check `formatWithOptions`. 492assert.strictEqual( 493 util.formatWithOptions( 494 { colors: true }, 495 true, undefined, Symbol(), 1, 5n, null, 'foobar' 496 ), 497 '\u001b[33mtrue\u001b[39m ' + 498 '\u001b[90mundefined\u001b[39m ' + 499 '\u001b[32mSymbol()\u001b[39m ' + 500 '\u001b[33m1\u001b[39m ' + 501 '\u001b[33m5n\u001b[39m ' + 502 '\u001b[1mnull\u001b[22m ' + 503 'foobar' 504); 505 506assert.strictEqual( 507 util.format(new SharedArrayBuffer(4)), 508 'SharedArrayBuffer { [Uint8Contents]: <00 00 00 00>, byteLength: 4 }' 509); 510 511assert.strictEqual( 512 util.formatWithOptions( 513 { colors: true, compact: 3 }, 514 '%s', [ 1, { a: true }] 515 ), 516 '[ 1, [Object] ]' 517); 518 519[ 520 undefined, 521 null, 522 false, 523 5n, 524 5, 525 'test', 526 Symbol(), 527].forEach((invalidOptions) => { 528 assert.throws(() => { 529 util.formatWithOptions(invalidOptions, { a: true }); 530 }, { 531 code: 'ERR_INVALID_ARG_TYPE', 532 message: /"inspectOptions".+object/ 533 }); 534}); 535