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'; 23const common = require('../common'); 24const assert = require('assert'); 25const util = require('util'); 26 27const { 28 hijackStdout, 29 hijackStderr, 30 restoreStdout, 31 restoreStderr 32} = require('../common/hijackstdio'); 33 34assert.ok(process.stdout.writable); 35assert.ok(process.stderr.writable); 36// Support legacy API 37if (common.isMainThread) { 38 assert.strictEqual(typeof process.stdout.fd, 'number'); 39 assert.strictEqual(typeof process.stderr.fd, 'number'); 40} 41 42common.expectWarning( 43 'Warning', 44 [ 45 ['Count for \'noLabel\' does not exist'], 46 ['No such label \'noLabel\' for console.timeLog()'], 47 ['No such label \'noLabel\' for console.timeEnd()'], 48 ['Count for \'default\' does not exist'], 49 ['No such label \'default\' for console.timeLog()'], 50 ['No such label \'default\' for console.timeEnd()'], 51 ['Label \'default\' already exists for console.time()'], 52 ['Label \'test\' already exists for console.time()'], 53 ] 54); 55 56console.countReset('noLabel'); 57console.timeLog('noLabel'); 58console.timeEnd('noLabel'); 59 60console.time('label'); 61console.timeEnd('label'); 62 63// Test using the default label 64// on console.time(), console.countReset(), console.timeLog(), console.timeEnd() 65console.countReset(); 66console.timeLog(); 67console.timeEnd(); 68 69console.time(); 70console.time(); 71console.timeLog(); 72console.timeEnd(); 73 74// Check that the `Error` is a `TypeError` but do not check the message as it 75// will be different in different JavaScript engines. 76assert.throws(() => console.time(Symbol('test')), 77 TypeError); 78assert.throws(() => console.timeEnd(Symbol('test')), 79 TypeError); 80 81 82// An Object with a custom inspect function. 83const custom_inspect = { foo: 'bar', [util.inspect.custom]: () => 'inspect' }; 84 85const strings = []; 86const errStrings = []; 87process.stdout.isTTY = false; 88hijackStdout(function(data) { 89 strings.push(data); 90}); 91process.stderr.isTTY = false; 92hijackStderr(function(data) { 93 errStrings.push(data); 94}); 95 96// Test console.log() goes to stdout 97console.log('foo'); 98console.log('foo', 'bar'); 99console.log('%s %s', 'foo', 'bar', 'hop'); 100console.log({ slashes: '\\\\' }); 101console.log(custom_inspect); 102 103// Test console.debug() goes to stdout 104console.debug('foo'); 105console.debug('foo', 'bar'); 106console.debug('%s %s', 'foo', 'bar', 'hop'); 107console.debug({ slashes: '\\\\' }); 108console.debug(custom_inspect); 109 110// Test console.info() goes to stdout 111console.info('foo'); 112console.info('foo', 'bar'); 113console.info('%s %s', 'foo', 'bar', 'hop'); 114console.info({ slashes: '\\\\' }); 115console.info(custom_inspect); 116 117// Test console.error() goes to stderr 118console.error('foo'); 119console.error('foo', 'bar'); 120console.error('%s %s', 'foo', 'bar', 'hop'); 121console.error({ slashes: '\\\\' }); 122console.error(custom_inspect); 123 124// Test console.warn() goes to stderr 125console.warn('foo'); 126console.warn('foo', 'bar'); 127console.warn('%s %s', 'foo', 'bar', 'hop'); 128console.warn({ slashes: '\\\\' }); 129console.warn(custom_inspect); 130 131// test console.dir() 132console.dir(custom_inspect); 133console.dir(custom_inspect, { showHidden: false }); 134console.dir({ foo: { bar: { baz: true } } }, { depth: 0 }); 135console.dir({ foo: { bar: { baz: true } } }, { depth: 1 }); 136 137// Test console.dirxml() 138console.dirxml(custom_inspect, custom_inspect); 139console.dirxml( 140 { foo: { bar: { baz: true } } }, 141 { foo: { bar: { quux: false } } }, 142 { foo: { bar: { quux: true } } } 143); 144 145// Test console.trace() 146console.trace('This is a %j %d', { formatted: 'trace' }, 10, 'foo'); 147 148// Test console.time() and console.timeEnd() output 149console.time('label'); 150console.timeEnd('label'); 151 152// Verify that Object.prototype properties can be used as labels 153console.time('__proto__'); 154console.timeEnd('__proto__'); 155console.time('constructor'); 156console.timeEnd('constructor'); 157console.time('hasOwnProperty'); 158console.timeEnd('hasOwnProperty'); 159 160// Verify that values are coerced to strings. 161console.time([]); 162console.timeEnd([]); 163console.time({}); 164console.timeEnd({}); 165// Repeat the object call to verify that everything really worked. 166console.time({}); 167console.timeEnd({}); 168console.time(null); 169console.timeEnd(null); 170console.time(undefined); 171console.timeEnd('default'); 172console.time('default'); 173console.timeEnd(); 174console.time(NaN); 175console.timeEnd(NaN); 176 177// Make sure calling time twice without timeEnd doesn't reset the timer. 178console.time('test'); 179const time = console._times.get('test'); 180setTimeout(() => { 181 console.time('test'); 182 assert.deepStrictEqual(console._times.get('test'), time); 183 console.timeEnd('test'); 184}, 1); 185 186console.time('log1'); 187console.timeLog('log1'); 188console.timeLog('log1', 'test'); 189console.timeLog('log1', {}, [1, 2, 3]); 190console.timeEnd('log1'); 191 192console.assert(false, '%s should', 'console.assert', 'not throw'); 193assert.strictEqual(errStrings[errStrings.length - 1], 194 'Assertion failed: console.assert should not throw\n'); 195 196console.assert(false); 197assert.strictEqual(errStrings[errStrings.length - 1], 'Assertion failed\n'); 198 199console.assert(true, 'this should not throw'); 200 201console.assert(true); 202 203assert.strictEqual(strings.length, process.stdout.writeTimes); 204assert.strictEqual(errStrings.length, process.stderr.writeTimes); 205restoreStdout(); 206restoreStderr(); 207 208// Verify that console.timeEnd() doesn't leave dead links 209const timesMapSize = console._times.size; 210console.time('label1'); 211console.time('label2'); 212console.time('label3'); 213console.timeEnd('label1'); 214console.timeEnd('label2'); 215console.timeEnd('label3'); 216assert.strictEqual(console._times.size, timesMapSize); 217 218const expectedStrings = [ 219 'foo', 'foo bar', 'foo bar hop', "{ slashes: '\\\\\\\\' }", 'inspect', 220]; 221 222for (const expected of expectedStrings) { 223 assert.strictEqual(strings.shift(), `${expected}\n`); 224 assert.strictEqual(errStrings.shift(), `${expected}\n`); 225} 226 227for (const expected of expectedStrings) { 228 assert.strictEqual(strings.shift(), `${expected}\n`); 229 assert.strictEqual(errStrings.shift(), `${expected}\n`); 230} 231 232for (const expected of expectedStrings) { 233 assert.strictEqual(strings.shift(), `${expected}\n`); 234} 235 236assert.strictEqual(strings.shift(), 237 "{\n foo: 'bar',\n [Symbol(nodejs.util.inspect.custom)]:" + 238 ' [Function: [nodejs.util.inspect.custom]]\n}\n'); 239assert.strictEqual(strings.shift(), 240 "{\n foo: 'bar',\n [Symbol(nodejs.util.inspect.custom)]:" + 241 ' [Function: [nodejs.util.inspect.custom]]\n}\n'); 242assert.ok(strings.shift().includes('foo: [Object]')); 243assert.strictEqual(strings.shift().includes('baz'), false); 244assert.strictEqual(strings.shift(), 'inspect inspect\n'); 245assert.ok(strings[0].includes('foo: { bar: { baz:')); 246assert.ok(strings[0].includes('quux')); 247assert.ok(strings.shift().includes('quux: true')); 248 249assert.ok(/^label: \d+(\.\d{1,3})?(ms|s)$/.test(strings.shift().trim())); 250assert.ok(/^__proto__: \d+(\.\d{1,3})?(ms|s)$/.test(strings.shift().trim())); 251assert.ok(/^constructor: \d+(\.\d{1,3})?(ms|s)$/.test(strings.shift().trim())); 252assert.ok(/^hasOwnProperty: \d+(\.\d{1,3})?(ms|s)$/.test(strings.shift().trim())); 253 254// Verify that console.time() coerces label values to strings as expected 255assert.ok(/^: \d+(\.\d{1,3})?(ms|s)$/.test(strings.shift().trim())); 256assert.ok(/^\[object Object\]: \d+(\.\d{1,3})?(ms|s)$/.test(strings.shift().trim())); 257assert.ok(/^\[object Object\]: \d+(\.\d{1,3})?(ms|s)$/.test(strings.shift().trim())); 258assert.ok(/^null: \d+(\.\d{1,3})?(ms|s)$/.test(strings.shift().trim())); 259assert.ok(/^default: \d+(\.\d{1,3})?(ms|s)$/.test(strings.shift().trim())); 260assert.ok(/^default: \d+(\.\d{1,3})?(ms|s)$/.test(strings.shift().trim())); 261assert.ok(/^NaN: \d+(\.\d{1,3})?(ms|s)$/.test(strings.shift().trim())); 262 263assert.ok(/^log1: \d+(\.\d{1,3})?(ms|s)$/.test(strings.shift().trim())); 264assert.ok(/^log1: \d+(\.\d{1,3})?(ms|s) test$/.test(strings.shift().trim())); 265assert.ok(/^log1: \d+(\.\d{1,3})?(ms|s) {} \[ 1, 2, 3 ]$/.test(strings.shift().trim())); 266assert.ok(/^log1: \d+(\.\d{1,3})?(ms|s)$/.test(strings.shift().trim())); 267 268// Make sure that we checked all strings 269assert.strictEqual(strings.length, 0); 270 271assert.strictEqual(errStrings.shift().split('\n').shift(), 272 'Trace: This is a {"formatted":"trace"} 10 foo'); 273 274// Hijack stderr to catch `process.emitWarning` which is using 275// `process.nextTick` 276hijackStderr(common.mustCall(function(data) { 277 restoreStderr(); 278 279 // stderr.write will catch sync error, so use `process.nextTick` here 280 process.nextTick(function() { 281 assert.strictEqual(data.includes('noLabel'), true); 282 }); 283})); 284