• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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