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// Integer format specifier 78assert.strictEqual(util.format('%i'), '%i'); 79assert.strictEqual(util.format('%i', 42.0), '42'); 80assert.strictEqual(util.format('%i', 42), '42'); 81assert.strictEqual(util.format('%i', '42'), '42'); 82assert.strictEqual(util.format('%i', '42.0'), '42'); 83assert.strictEqual(util.format('%i', 1.5), '1'); 84assert.strictEqual(util.format('%i', -0.5), '-0'); 85assert.strictEqual(util.format('%i', ''), 'NaN'); 86assert.strictEqual(util.format('%i', Infinity), 'NaN'); 87assert.strictEqual(util.format('%i', -Infinity), 'NaN'); 88assert.strictEqual(util.format('%i', Symbol()), 'NaN'); 89assert.strictEqual(util.format('%i %i', 42, 43), '42 43'); 90assert.strictEqual(util.format('%i %i', 42), '42 %i'); 91assert.strictEqual( 92 util.format('%i', 1180591620717411303424), 93 '1' 94); 95assert.strictEqual( 96 util.format('%i', 1180591620717411303424n), 97 '1180591620717411303424n' 98); 99assert.strictEqual( 100 util.format('%i %i', 1180591620717411303424n, 12345678901234567890123n), 101 '1180591620717411303424n 12345678901234567890123n' 102); 103 104assert.strictEqual( 105 util.format('%d %i', 1180591620717411303424n, 12345678901234567890123n), 106 '1180591620717411303424n 12345678901234567890123n' 107); 108 109assert.strictEqual( 110 util.format('%i %d', 1180591620717411303424n, 12345678901234567890123n), 111 '1180591620717411303424n 12345678901234567890123n' 112); 113 114// Float format specifier 115assert.strictEqual(util.format('%f'), '%f'); 116assert.strictEqual(util.format('%f', 42.0), '42'); 117assert.strictEqual(util.format('%f', 42), '42'); 118assert.strictEqual(util.format('%f', '42'), '42'); 119assert.strictEqual(util.format('%f', '-0.0'), '-0'); 120assert.strictEqual(util.format('%f', '42.0'), '42'); 121assert.strictEqual(util.format('%f', 1.5), '1.5'); 122assert.strictEqual(util.format('%f', -0.5), '-0.5'); 123assert.strictEqual(util.format('%f', Math.PI), '3.141592653589793'); 124assert.strictEqual(util.format('%f', ''), 'NaN'); 125assert.strictEqual(util.format('%f', Symbol('foo')), 'NaN'); 126assert.strictEqual(util.format('%f', 5n), '5'); 127assert.strictEqual(util.format('%f', Infinity), 'Infinity'); 128assert.strictEqual(util.format('%f', -Infinity), '-Infinity'); 129assert.strictEqual(util.format('%f %f', 42, 43), '42 43'); 130assert.strictEqual(util.format('%f %f', 42), '42 %f'); 131 132// String format specifier 133assert.strictEqual(util.format('%s'), '%s'); 134assert.strictEqual(util.format('%s', undefined), 'undefined'); 135assert.strictEqual(util.format('%s', null), 'null'); 136assert.strictEqual(util.format('%s', 'foo'), 'foo'); 137assert.strictEqual(util.format('%s', 42), '42'); 138assert.strictEqual(util.format('%s', '42'), '42'); 139assert.strictEqual(util.format('%s', -0), '-0'); 140assert.strictEqual(util.format('%s', '-0.0'), '-0.0'); 141assert.strictEqual(util.format('%s %s', 42, 43), '42 43'); 142assert.strictEqual(util.format('%s %s', 42), '42 %s'); 143assert.strictEqual(util.format('%s', 42n), '42n'); 144assert.strictEqual(util.format('%s', Symbol('foo')), 'Symbol(foo)'); 145assert.strictEqual(util.format('%s', true), 'true'); 146assert.strictEqual(util.format('%s', { a: [1, 2, 3] }), '{ a: [Array] }'); 147assert.strictEqual(util.format('%s', { toString() { return 'Foo'; } }), 'Foo'); 148assert.strictEqual(util.format('%s', { toString: 5 }), '{ toString: 5 }'); 149assert.strictEqual(util.format('%s', () => 5), '() => 5'); 150assert.strictEqual(util.format('%s', Infinity), 'Infinity'); 151assert.strictEqual(util.format('%s', -Infinity), '-Infinity'); 152 153// String format specifier including `toString` properties on the prototype. 154{ 155 class Foo { toString() { return 'Bar'; } } 156 assert.strictEqual(util.format('%s', new Foo()), 'Bar'); 157 assert.strictEqual( 158 util.format('%s', Object.setPrototypeOf(new Foo(), null)), 159 '[Foo: null prototype] {}' 160 ); 161 global.Foo = Foo; 162 assert.strictEqual(util.format('%s', new Foo()), 'Bar'); 163 delete global.Foo; 164 class Bar { abc = true; } 165 assert.strictEqual(util.format('%s', new Bar()), 'Bar { abc: true }'); 166 class Foobar extends Array { aaa = true; } 167 assert.strictEqual( 168 util.format('%s', new Foobar(5)), 169 'Foobar(5) [ <5 empty items>, aaa: true ]' 170 ); 171 172 // Subclassing: 173 class B extends Foo {} 174 175 function C() {} 176 C.prototype.toString = function() { 177 return 'Custom'; 178 }; 179 180 function D() { 181 C.call(this); 182 } 183 D.prototype = Object.create(C.prototype); 184 185 assert.strictEqual( 186 util.format('%s', new B()), 187 'Bar' 188 ); 189 assert.strictEqual( 190 util.format('%s', new C()), 191 'Custom' 192 ); 193 assert.strictEqual( 194 util.format('%s', new D()), 195 'Custom' 196 ); 197 198 D.prototype.constructor = D; 199 assert.strictEqual( 200 util.format('%s', new D()), 201 'Custom' 202 ); 203 204 D.prototype.constructor = null; 205 assert.strictEqual( 206 util.format('%s', new D()), 207 'Custom' 208 ); 209 210 D.prototype.constructor = { name: 'Foobar' }; 211 assert.strictEqual( 212 util.format('%s', new D()), 213 'Custom' 214 ); 215 216 Object.defineProperty(D.prototype, 'constructor', { 217 get() { 218 throw new Error(); 219 }, 220 configurable: true 221 }); 222 assert.strictEqual( 223 util.format('%s', new D()), 224 'Custom' 225 ); 226 227 assert.strictEqual( 228 util.format('%s', Object.create(null)), 229 '[Object: null prototype] {}' 230 ); 231} 232 233// JSON format specifier 234assert.strictEqual(util.format('%j'), '%j'); 235assert.strictEqual(util.format('%j', 42), '42'); 236assert.strictEqual(util.format('%j', '42'), '"42"'); 237assert.strictEqual(util.format('%j %j', 42, 43), '42 43'); 238assert.strictEqual(util.format('%j %j', 42), '42 %j'); 239 240// Object format specifier 241const obj = { 242 foo: 'bar', 243 foobar: 1, 244 func: function() {} 245}; 246const nestedObj = { 247 foo: 'bar', 248 foobar: { 249 foo: 'bar', 250 func: function() {} 251 } 252}; 253const nestedObj2 = { 254 foo: 'bar', 255 foobar: 1, 256 func: [{ a: function() {} }] 257}; 258assert.strictEqual(util.format('%o'), '%o'); 259assert.strictEqual(util.format('%o', 42), '42'); 260assert.strictEqual(util.format('%o', 'foo'), '\'foo\''); 261assert.strictEqual( 262 util.format('%o', obj), 263 '{\n' + 264 ' foo: \'bar\',\n' + 265 ' foobar: 1,\n' + 266 ' func: <ref *1> [Function: func] {\n' + 267 ' [length]: 0,\n' + 268 ' [name]: \'func\',\n' + 269 ' [prototype]: { [constructor]: [Circular *1] }\n' + 270 ' }\n' + 271 '}'); 272assert.strictEqual( 273 util.format('%o', nestedObj2), 274 '{\n' + 275 ' foo: \'bar\',\n' + 276 ' foobar: 1,\n' + 277 ' func: [\n' + 278 ' {\n' + 279 ' a: <ref *1> [Function: a] {\n' + 280 ' [length]: 0,\n' + 281 ' [name]: \'a\',\n' + 282 ' [prototype]: { [constructor]: [Circular *1] }\n' + 283 ' }\n' + 284 ' },\n' + 285 ' [length]: 1\n' + 286 ' ]\n' + 287 '}'); 288assert.strictEqual( 289 util.format('%o', nestedObj), 290 '{\n' + 291 ' foo: \'bar\',\n' + 292 ' foobar: {\n' + 293 ' foo: \'bar\',\n' + 294 ' func: <ref *1> [Function: func] {\n' + 295 ' [length]: 0,\n' + 296 ' [name]: \'func\',\n' + 297 ' [prototype]: { [constructor]: [Circular *1] }\n' + 298 ' }\n' + 299 ' }\n' + 300 '}'); 301assert.strictEqual( 302 util.format('%o %o', obj, obj), 303 '{\n' + 304 ' foo: \'bar\',\n' + 305 ' foobar: 1,\n' + 306 ' func: <ref *1> [Function: func] {\n' + 307 ' [length]: 0,\n' + 308 ' [name]: \'func\',\n' + 309 ' [prototype]: { [constructor]: [Circular *1] }\n' + 310 ' }\n' + 311 '} {\n' + 312 ' foo: \'bar\',\n' + 313 ' foobar: 1,\n' + 314 ' func: <ref *1> [Function: func] {\n' + 315 ' [length]: 0,\n' + 316 ' [name]: \'func\',\n' + 317 ' [prototype]: { [constructor]: [Circular *1] }\n' + 318 ' }\n' + 319 '}'); 320assert.strictEqual( 321 util.format('%o %o', obj), 322 '{\n' + 323 ' foo: \'bar\',\n' + 324 ' foobar: 1,\n' + 325 ' func: <ref *1> [Function: func] {\n' + 326 ' [length]: 0,\n' + 327 ' [name]: \'func\',\n' + 328 ' [prototype]: { [constructor]: [Circular *1] }\n' + 329 ' }\n' + 330 '} %o'); 331 332assert.strictEqual(util.format('%O'), '%O'); 333assert.strictEqual(util.format('%O', 42), '42'); 334assert.strictEqual(util.format('%O', 'foo'), '\'foo\''); 335assert.strictEqual( 336 util.format('%O', obj), 337 '{ foo: \'bar\', foobar: 1, func: [Function: func] }'); 338assert.strictEqual( 339 util.format('%O', nestedObj), 340 '{ foo: \'bar\', foobar: { foo: \'bar\', func: [Function: func] } }'); 341assert.strictEqual( 342 util.format('%O %O', obj, obj), 343 '{ foo: \'bar\', foobar: 1, func: [Function: func] } ' + 344 '{ foo: \'bar\', foobar: 1, func: [Function: func] }'); 345assert.strictEqual( 346 util.format('%O %O', obj), 347 '{ foo: \'bar\', foobar: 1, func: [Function: func] } %O'); 348 349// Various format specifiers 350assert.strictEqual(util.format('%%s%s', 'foo'), '%sfoo'); 351assert.strictEqual(util.format('%s:%s'), '%s:%s'); 352assert.strictEqual(util.format('%s:%s', undefined), 'undefined:%s'); 353assert.strictEqual(util.format('%s:%s', 'foo'), 'foo:%s'); 354assert.strictEqual(util.format('%s:%i', 'foo'), 'foo:%i'); 355assert.strictEqual(util.format('%s:%f', 'foo'), 'foo:%f'); 356assert.strictEqual(util.format('%s:%s', 'foo', 'bar'), 'foo:bar'); 357assert.strictEqual(util.format('%s:%s', 'foo', 'bar', 'baz'), 'foo:bar baz'); 358assert.strictEqual(util.format('%%%s%%', 'hi'), '%hi%'); 359assert.strictEqual(util.format('%%%s%%%%', 'hi'), '%hi%%'); 360assert.strictEqual(util.format('%sbc%%def', 'a'), 'abc%def'); 361assert.strictEqual(util.format('%d:%d', 12, 30), '12:30'); 362assert.strictEqual(util.format('%d:%d', 12), '12:%d'); 363assert.strictEqual(util.format('%d:%d'), '%d:%d'); 364assert.strictEqual(util.format('%i:%i', 12, 30), '12:30'); 365assert.strictEqual(util.format('%i:%i', 12), '12:%i'); 366assert.strictEqual(util.format('%i:%i'), '%i:%i'); 367assert.strictEqual(util.format('%f:%f', 12, 30), '12:30'); 368assert.strictEqual(util.format('%f:%f', 12), '12:%f'); 369assert.strictEqual(util.format('%f:%f'), '%f:%f'); 370assert.strictEqual(util.format('o: %j, a: %j', {}, []), 'o: {}, a: []'); 371assert.strictEqual(util.format('o: %j, a: %j', {}), 'o: {}, a: %j'); 372assert.strictEqual(util.format('o: %j, a: %j'), 'o: %j, a: %j'); 373assert.strictEqual(util.format('o: %o, a: %O', {}, []), 'o: {}, a: []'); 374assert.strictEqual(util.format('o: %o, a: %o', {}), 'o: {}, a: %o'); 375assert.strictEqual(util.format('o: %O, a: %O'), 'o: %O, a: %O'); 376 377 378// Invalid format specifiers 379assert.strictEqual(util.format('a% b', 'x'), 'a% b x'); 380assert.strictEqual(util.format('percent: %d%, fraction: %d', 10, 0.1), 381 'percent: 10%, fraction: 0.1'); 382assert.strictEqual(util.format('abc%', 1), 'abc% 1'); 383 384// Additional arguments after format specifiers 385assert.strictEqual(util.format('%i', 1, 'number'), '1 number'); 386assert.strictEqual(util.format('%i', 1, () => {}), '1 [Function (anonymous)]'); 387 388// %c from https://console.spec.whatwg.org/ 389assert.strictEqual(util.format('%c'), '%c'); 390assert.strictEqual(util.format('%cab'), '%cab'); 391assert.strictEqual(util.format('%cab', 'color: blue'), 'ab'); 392assert.strictEqual(util.format('%cab', 'color: blue', 'c'), 'ab c'); 393 394{ 395 const o = {}; 396 o.o = o; 397 assert.strictEqual(util.format('%j', o), '[Circular]'); 398} 399 400{ 401 const o = { 402 toJSON() { 403 throw new Error('Not a circular object but still not serializable'); 404 } 405 }; 406 assert.throws(() => util.format('%j', o), 407 /^Error: Not a circular object but still not serializable$/); 408} 409 410// Errors 411const err = new Error('foo'); 412assert.strictEqual(util.format(err), err.stack); 413class CustomError extends Error { 414 constructor(msg) { 415 super(); 416 Object.defineProperty(this, 'message', 417 { value: msg, enumerable: false }); 418 Object.defineProperty(this, 'name', 419 { value: 'CustomError', enumerable: false }); 420 Error.captureStackTrace(this, CustomError); 421 } 422} 423const customError = new CustomError('bar'); 424assert.strictEqual(util.format(customError), customError.stack); 425// Doesn't capture stack trace 426function BadCustomError(msg) { 427 Error.call(this); 428 Object.defineProperty(this, 'message', 429 { value: msg, enumerable: false }); 430 Object.defineProperty(this, 'name', 431 { value: 'BadCustomError', enumerable: false }); 432} 433Object.setPrototypeOf(BadCustomError.prototype, Error.prototype); 434Object.setPrototypeOf(BadCustomError, Error); 435assert.strictEqual(util.format(new BadCustomError('foo')), 436 '[BadCustomError: foo]'); 437 438// The format of arguments should not depend on type of the first argument 439assert.strictEqual(util.format('1', '1'), '1 1'); 440assert.strictEqual(util.format(1, '1'), '1 1'); 441assert.strictEqual(util.format('1', 1), '1 1'); 442assert.strictEqual(util.format(1, -0), '1 -0'); 443assert.strictEqual(util.format('1', () => {}), '1 [Function (anonymous)]'); 444assert.strictEqual(util.format(1, () => {}), '1 [Function (anonymous)]'); 445assert.strictEqual(util.format('1', "'"), "1 '"); 446assert.strictEqual(util.format(1, "'"), "1 '"); 447assert.strictEqual(util.format('1', 'number'), '1 number'); 448assert.strictEqual(util.format(1, 'number'), '1 number'); 449assert.strictEqual(util.format(5n), '5n'); 450assert.strictEqual(util.format(5n, 5n), '5n 5n'); 451 452// Check `formatWithOptions`. 453assert.strictEqual( 454 util.formatWithOptions( 455 { colors: true }, 456 true, undefined, Symbol(), 1, 5n, null, 'foobar' 457 ), 458 '\u001b[33mtrue\u001b[39m ' + 459 '\u001b[90mundefined\u001b[39m ' + 460 '\u001b[32mSymbol()\u001b[39m ' + 461 '\u001b[33m1\u001b[39m ' + 462 '\u001b[33m5n\u001b[39m ' + 463 '\u001b[1mnull\u001b[22m ' + 464 'foobar' 465); 466 467assert.strictEqual( 468 util.format(new SharedArrayBuffer(4)), 469 'SharedArrayBuffer { [Uint8Contents]: <00 00 00 00>, byteLength: 4 }' 470); 471 472assert.strictEqual( 473 util.formatWithOptions( 474 { colors: true, compact: 3 }, 475 '%s', [ 1, { a: true }] 476 ), 477 '[ 1, [Object] ]' 478); 479 480[ 481 undefined, 482 null, 483 false, 484 5n, 485 5, 486 'test', 487 Symbol(), 488].forEach((invalidOptions) => { 489 assert.throws(() => { 490 util.formatWithOptions(invalidOptions, { a: true }); 491 }, { 492 code: 'ERR_INVALID_ARG_TYPE', 493 message: /"inspectOptions".+object/ 494 }); 495}); 496