1// Originally from narwhal.js (http://narwhaljs.org) 2// Copyright (c) 2009 Thomas Robinson <280north.com> 3// 4// Permission is hereby granted, free of charge, to any person obtaining a copy 5// of this software and associated documentation files (the 'Software'), to 6// deal in the Software without restriction, including without limitation the 7// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8// sell copies of the Software, and to permit persons to whom the Software is 9// furnished to do so, subject to the following conditions: 10// 11// The above copyright notice and this permission notice shall be included in 12// all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 18// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 19// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 21'use strict'; 22 23const { 24 ArrayPrototypeIndexOf, 25 ArrayPrototypeJoin, 26 ArrayPrototypePush, 27 ArrayPrototypeShift, 28 ArrayPrototypeSlice, 29 Error, 30 ErrorCaptureStackTrace, 31 FunctionPrototypeBind, 32 NumberIsNaN, 33 ObjectAssign, 34 ObjectIs, 35 ObjectKeys, 36 ObjectPrototypeIsPrototypeOf, 37 ReflectApply, 38 RegExpPrototypeTest, 39 SafeMap, 40 String, 41 StringPrototypeCharCodeAt, 42 StringPrototypeIncludes, 43 StringPrototypeIndexOf, 44 StringPrototypeReplace, 45 StringPrototypeSlice, 46 StringPrototypeSplit, 47} = primordials; 48 49const { Buffer } = require('buffer'); 50const { 51 codes: { 52 ERR_AMBIGUOUS_ARGUMENT, 53 ERR_INVALID_ARG_TYPE, 54 ERR_INVALID_ARG_VALUE, 55 ERR_INVALID_RETURN_VALUE, 56 ERR_MISSING_ARGS, 57 }, 58 overrideStackTrace, 59} = require('internal/errors'); 60const AssertionError = require('internal/assert/assertion_error'); 61const { openSync, closeSync, readSync } = require('fs'); 62const { inspect } = require('internal/util/inspect'); 63const { isPromise, isRegExp } = require('internal/util/types'); 64const { EOL } = require('internal/constants'); 65const { NativeModule } = require('internal/bootstrap/loaders'); 66const { isError } = require('internal/util'); 67 68const errorCache = new SafeMap(); 69const CallTracker = require('internal/assert/calltracker'); 70 71let isDeepEqual; 72let isDeepStrictEqual; 73let parseExpressionAt; 74let findNodeAround; 75let decoder; 76 77function lazyLoadComparison() { 78 const comparison = require('internal/util/comparisons'); 79 isDeepEqual = comparison.isDeepEqual; 80 isDeepStrictEqual = comparison.isDeepStrictEqual; 81} 82 83// Escape control characters but not \n and \t to keep the line breaks and 84// indentation intact. 85// eslint-disable-next-line no-control-regex 86const escapeSequencesRegExp = /[\x00-\x08\x0b\x0c\x0e-\x1f]/g; 87const meta = [ 88 '\\u0000', '\\u0001', '\\u0002', '\\u0003', '\\u0004', 89 '\\u0005', '\\u0006', '\\u0007', '\\b', '', 90 '', '\\u000b', '\\f', '', '\\u000e', 91 '\\u000f', '\\u0010', '\\u0011', '\\u0012', '\\u0013', 92 '\\u0014', '\\u0015', '\\u0016', '\\u0017', '\\u0018', 93 '\\u0019', '\\u001a', '\\u001b', '\\u001c', '\\u001d', 94 '\\u001e', '\\u001f', 95]; 96 97const escapeFn = (str) => meta[StringPrototypeCharCodeAt(str, 0)]; 98 99let warned = false; 100 101// The assert module provides functions that throw 102// AssertionError's when particular conditions are not met. The 103// assert module must conform to the following interface. 104 105const assert = module.exports = ok; 106 107const NO_EXCEPTION_SENTINEL = {}; 108 109// All of the following functions must throw an AssertionError 110// when a corresponding condition is not met, with a message that 111// may be undefined if not provided. All assertion methods provide 112// both the actual and expected values to the assertion error for 113// display purposes. 114 115function innerFail(obj) { 116 if (obj.message instanceof Error) throw obj.message; 117 118 throw new AssertionError(obj); 119} 120 121/** 122 * @param {any} actual 123 * @param {any} expected 124 * @param {string | Error} [message] 125 * @param {string} [operator] 126 * @param {Function} [stackStartFn] 127 * @returns {never} 128 */ 129function fail(actual, expected, message, operator, stackStartFn) { 130 const argsLen = arguments.length; 131 132 let internalMessage = false; 133 if (actual == null && argsLen <= 1) { 134 internalMessage = true; 135 message = 'Failed'; 136 } else if (argsLen === 1) { 137 message = actual; 138 actual = undefined; 139 } else { 140 if (warned === false) { 141 warned = true; 142 process.emitWarning( 143 'assert.fail() with more than one argument is deprecated. ' + 144 'Please use assert.strictEqual() instead or only pass a message.', 145 'DeprecationWarning', 146 'DEP0094' 147 ); 148 } 149 if (argsLen === 2) 150 operator = '!='; 151 } 152 153 if (message instanceof Error) throw message; 154 155 const errArgs = { 156 actual, 157 expected, 158 operator: operator === undefined ? 'fail' : operator, 159 stackStartFn: stackStartFn || fail, 160 message 161 }; 162 const err = new AssertionError(errArgs); 163 if (internalMessage) { 164 err.generatedMessage = true; 165 } 166 throw err; 167} 168 169assert.fail = fail; 170 171// The AssertionError is defined in internal/error. 172assert.AssertionError = AssertionError; 173 174function findColumn(fd, column, code) { 175 if (code.length > column + 100) { 176 try { 177 return parseCode(code, column); 178 } catch { 179 // End recursion in case no code could be parsed. The expression should 180 // have been found after 2500 characters, so stop trying. 181 if (code.length - column > 2500) { 182 // eslint-disable-next-line no-throw-literal 183 throw null; 184 } 185 } 186 } 187 // Read up to 2500 bytes more than necessary in columns. That way we address 188 // multi byte characters and read enough data to parse the code. 189 const bytesToRead = column - code.length + 2500; 190 const buffer = Buffer.allocUnsafe(bytesToRead); 191 const bytesRead = readSync(fd, buffer, 0, bytesToRead); 192 code += decoder.write(buffer.slice(0, bytesRead)); 193 // EOF: fast path. 194 if (bytesRead < bytesToRead) { 195 return parseCode(code, column); 196 } 197 // Read potentially missing code. 198 return findColumn(fd, column, code); 199} 200 201function getCode(fd, line, column) { 202 let bytesRead = 0; 203 if (line === 0) { 204 // Special handle line number one. This is more efficient and simplifies the 205 // rest of the algorithm. Read more than the regular column number in bytes 206 // to prevent multiple reads in case multi byte characters are used. 207 return findColumn(fd, column, ''); 208 } 209 let lines = 0; 210 // Prevent blocking the event loop by limiting the maximum amount of 211 // data that may be read. 212 let maxReads = 32; // bytesPerRead * maxReads = 512 kb 213 const bytesPerRead = 16384; 214 // Use a single buffer up front that is reused until the call site is found. 215 let buffer = Buffer.allocUnsafe(bytesPerRead); 216 while (maxReads-- !== 0) { 217 // Only allocate a new buffer in case the needed line is found. All data 218 // before that can be discarded. 219 buffer = lines < line ? buffer : Buffer.allocUnsafe(bytesPerRead); 220 bytesRead = readSync(fd, buffer, 0, bytesPerRead); 221 // Read the buffer until the required code line is found. 222 for (let i = 0; i < bytesRead; i++) { 223 if (buffer[i] === 10 && ++lines === line) { 224 // If the end of file is reached, directly parse the code and return. 225 if (bytesRead < bytesPerRead) { 226 return parseCode(buffer.toString('utf8', i + 1, bytesRead), column); 227 } 228 // Check if the read code is sufficient or read more until the whole 229 // expression is read. Make sure multi byte characters are preserved 230 // properly by using the decoder. 231 const code = decoder.write(buffer.slice(i + 1, bytesRead)); 232 return findColumn(fd, column, code); 233 } 234 } 235 } 236} 237 238function parseCode(code, offset) { 239 // Lazy load acorn. 240 if (parseExpressionAt === undefined) { 241 const Parser = require('internal/deps/acorn/acorn/dist/acorn').Parser; 242 ({ findNodeAround } = require('internal/deps/acorn/acorn-walk/dist/walk')); 243 244 parseExpressionAt = FunctionPrototypeBind(Parser.parseExpressionAt, Parser); 245 } 246 let node; 247 let start = 0; 248 // Parse the read code until the correct expression is found. 249 do { 250 try { 251 node = parseExpressionAt(code, start, { ecmaVersion: 'latest' }); 252 start = node.end + 1 || start; 253 // Find the CallExpression in the tree. 254 node = findNodeAround(node, offset, 'CallExpression'); 255 } catch (err) { 256 // Unexpected token error and the like. 257 start += err.raisedAt || 1; 258 if (start > offset) { 259 // No matching expression found. This could happen if the assert 260 // expression is bigger than the provided buffer. 261 // eslint-disable-next-line no-throw-literal 262 throw null; 263 } 264 } 265 } while (node === undefined || node.node.end < offset); 266 267 return [ 268 node.node.start, 269 StringPrototypeReplace(StringPrototypeSlice(code, 270 node.node.start, node.node.end), 271 escapeSequencesRegExp, escapeFn), 272 ]; 273} 274 275function getErrMessage(message, fn) { 276 const tmpLimit = Error.stackTraceLimit; 277 // Make sure the limit is set to 1. Otherwise it could fail (<= 0) or it 278 // does to much work. 279 Error.stackTraceLimit = 1; 280 // We only need the stack trace. To minimize the overhead use an object 281 // instead of an error. 282 const err = {}; 283 ErrorCaptureStackTrace(err, fn); 284 Error.stackTraceLimit = tmpLimit; 285 286 overrideStackTrace.set(err, (_, stack) => stack); 287 const call = err.stack[0]; 288 289 const filename = call.getFileName(); 290 const line = call.getLineNumber() - 1; 291 let column = call.getColumnNumber() - 1; 292 let identifier; 293 let code; 294 295 if (filename) { 296 identifier = `${filename}${line}${column}`; 297 298 // Skip Node.js modules! 299 if (filename.endsWith('.js') && 300 NativeModule.exists(filename.slice(0, -3))) { 301 errorCache.set(identifier, undefined); 302 return; 303 } 304 } else { 305 const fn = call.getFunction(); 306 if (!fn) { 307 return message; 308 } 309 code = String(fn); 310 identifier = `${code}${line}${column}`; 311 } 312 313 if (errorCache.has(identifier)) { 314 return errorCache.get(identifier); 315 } 316 317 let fd; 318 try { 319 // Set the stack trace limit to zero. This makes sure unexpected token 320 // errors are handled faster. 321 Error.stackTraceLimit = 0; 322 323 if (filename) { 324 if (decoder === undefined) { 325 const { StringDecoder } = require('string_decoder'); 326 decoder = new StringDecoder('utf8'); 327 } 328 fd = openSync(filename, 'r', 0o666); 329 // Reset column and message. 330 ({ 0: column, 1: message } = getCode(fd, line, column)); 331 // Flush unfinished multi byte characters. 332 decoder.end(); 333 } else { 334 for (let i = 0; i < line; i++) { 335 code = StringPrototypeSlice(code, 336 StringPrototypeIndexOf(code, '\n') + 1); 337 } 338 ({ 0: column, 1: message } = parseCode(code, column)); 339 } 340 // Always normalize indentation, otherwise the message could look weird. 341 if (StringPrototypeIncludes(message, '\n')) { 342 if (EOL === '\r\n') { 343 message = StringPrototypeReplace(message, /\r\n/g, '\n'); 344 } 345 const frames = StringPrototypeSplit(message, '\n'); 346 message = ArrayPrototypeShift(frames); 347 for (const frame of frames) { 348 let pos = 0; 349 while (pos < column && (frame[pos] === ' ' || frame[pos] === '\t')) { 350 pos++; 351 } 352 message += `\n ${StringPrototypeSlice(frame, pos)}`; 353 } 354 } 355 message = `The expression evaluated to a falsy value:\n\n ${message}\n`; 356 // Make sure to always set the cache! No matter if the message is 357 // undefined or not 358 errorCache.set(identifier, message); 359 360 return message; 361 } catch { 362 // Invalidate cache to prevent trying to read this part again. 363 errorCache.set(identifier, undefined); 364 } finally { 365 // Reset limit. 366 Error.stackTraceLimit = tmpLimit; 367 if (fd !== undefined) 368 closeSync(fd); 369 } 370} 371 372function innerOk(fn, argLen, value, message) { 373 if (!value) { 374 let generatedMessage = false; 375 376 if (argLen === 0) { 377 generatedMessage = true; 378 message = 'No value argument passed to `assert.ok()`'; 379 } else if (message == null) { 380 generatedMessage = true; 381 message = getErrMessage(message, fn); 382 } else if (message instanceof Error) { 383 throw message; 384 } 385 386 const err = new AssertionError({ 387 actual: value, 388 expected: true, 389 message, 390 operator: '==', 391 stackStartFn: fn 392 }); 393 err.generatedMessage = generatedMessage; 394 throw err; 395 } 396} 397 398/** 399 * Pure assertion tests whether a value is truthy, as determined 400 * by !!value. 401 * @param {...any} args 402 * @returns {void} 403 */ 404function ok(...args) { 405 innerOk(ok, args.length, ...args); 406} 407assert.ok = ok; 408 409/** 410 * The equality assertion tests shallow, coercive equality with ==. 411 * @param {any} actual 412 * @param {any} expected 413 * @param {string | Error} [message] 414 * @returns {void} 415 */ 416/* eslint-disable no-restricted-properties */ 417assert.equal = function equal(actual, expected, message) { 418 if (arguments.length < 2) { 419 throw new ERR_MISSING_ARGS('actual', 'expected'); 420 } 421 // eslint-disable-next-line eqeqeq 422 if (actual != expected && (!NumberIsNaN(actual) || !NumberIsNaN(expected))) { 423 innerFail({ 424 actual, 425 expected, 426 message, 427 operator: '==', 428 stackStartFn: equal 429 }); 430 } 431}; 432 433/** 434 * The non-equality assertion tests for whether two objects are not 435 * equal with !=. 436 * @param {any} actual 437 * @param {any} expected 438 * @param {string | Error} [message] 439 * @returns {void} 440 */ 441assert.notEqual = function notEqual(actual, expected, message) { 442 if (arguments.length < 2) { 443 throw new ERR_MISSING_ARGS('actual', 'expected'); 444 } 445 // eslint-disable-next-line eqeqeq 446 if (actual == expected || (NumberIsNaN(actual) && NumberIsNaN(expected))) { 447 innerFail({ 448 actual, 449 expected, 450 message, 451 operator: '!=', 452 stackStartFn: notEqual 453 }); 454 } 455}; 456 457/** 458 * The deep equivalence assertion tests a deep equality relation. 459 * @param {any} actual 460 * @param {any} expected 461 * @param {string | Error} [message] 462 * @returns {void} 463 */ 464assert.deepEqual = function deepEqual(actual, expected, message) { 465 if (arguments.length < 2) { 466 throw new ERR_MISSING_ARGS('actual', 'expected'); 467 } 468 if (isDeepEqual === undefined) lazyLoadComparison(); 469 if (!isDeepEqual(actual, expected)) { 470 innerFail({ 471 actual, 472 expected, 473 message, 474 operator: 'deepEqual', 475 stackStartFn: deepEqual 476 }); 477 } 478}; 479 480/** 481 * The deep non-equivalence assertion tests for any deep inequality. 482 * @param {any} actual 483 * @param {any} expected 484 * @param {string | Error} [message] 485 * @returns {void} 486 */ 487assert.notDeepEqual = function notDeepEqual(actual, expected, message) { 488 if (arguments.length < 2) { 489 throw new ERR_MISSING_ARGS('actual', 'expected'); 490 } 491 if (isDeepEqual === undefined) lazyLoadComparison(); 492 if (isDeepEqual(actual, expected)) { 493 innerFail({ 494 actual, 495 expected, 496 message, 497 operator: 'notDeepEqual', 498 stackStartFn: notDeepEqual 499 }); 500 } 501}; 502/* eslint-enable */ 503 504/** 505 * The deep strict equivalence assertion tests a deep strict equality 506 * relation. 507 * @param {any} actual 508 * @param {any} expected 509 * @param {string | Error} [message] 510 * @returns {void} 511 */ 512assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { 513 if (arguments.length < 2) { 514 throw new ERR_MISSING_ARGS('actual', 'expected'); 515 } 516 if (isDeepEqual === undefined) lazyLoadComparison(); 517 if (!isDeepStrictEqual(actual, expected)) { 518 innerFail({ 519 actual, 520 expected, 521 message, 522 operator: 'deepStrictEqual', 523 stackStartFn: deepStrictEqual 524 }); 525 } 526}; 527 528/** 529 * The deep strict non-equivalence assertion tests for any deep strict 530 * inequality. 531 * @param {any} actual 532 * @param {any} expected 533 * @param {string | Error} [message] 534 * @returns {void} 535 */ 536assert.notDeepStrictEqual = notDeepStrictEqual; 537function notDeepStrictEqual(actual, expected, message) { 538 if (arguments.length < 2) { 539 throw new ERR_MISSING_ARGS('actual', 'expected'); 540 } 541 if (isDeepEqual === undefined) lazyLoadComparison(); 542 if (isDeepStrictEqual(actual, expected)) { 543 innerFail({ 544 actual, 545 expected, 546 message, 547 operator: 'notDeepStrictEqual', 548 stackStartFn: notDeepStrictEqual 549 }); 550 } 551} 552 553/** 554 * The strict equivalence assertion tests a strict equality relation. 555 * @param {any} actual 556 * @param {any} expected 557 * @param {string | Error} [message] 558 * @returns {void} 559 */ 560assert.strictEqual = function strictEqual(actual, expected, message) { 561 if (arguments.length < 2) { 562 throw new ERR_MISSING_ARGS('actual', 'expected'); 563 } 564 if (!ObjectIs(actual, expected)) { 565 innerFail({ 566 actual, 567 expected, 568 message, 569 operator: 'strictEqual', 570 stackStartFn: strictEqual 571 }); 572 } 573}; 574 575/** 576 * The strict non-equivalence assertion tests for any strict inequality. 577 * @param {any} actual 578 * @param {any} expected 579 * @param {string | Error} [message] 580 * @returns {void} 581 */ 582assert.notStrictEqual = function notStrictEqual(actual, expected, message) { 583 if (arguments.length < 2) { 584 throw new ERR_MISSING_ARGS('actual', 'expected'); 585 } 586 if (ObjectIs(actual, expected)) { 587 innerFail({ 588 actual, 589 expected, 590 message, 591 operator: 'notStrictEqual', 592 stackStartFn: notStrictEqual 593 }); 594 } 595}; 596 597class Comparison { 598 constructor(obj, keys, actual) { 599 for (const key of keys) { 600 if (key in obj) { 601 if (actual !== undefined && 602 typeof actual[key] === 'string' && 603 isRegExp(obj[key]) && 604 RegExpPrototypeTest(obj[key], actual[key])) { 605 this[key] = actual[key]; 606 } else { 607 this[key] = obj[key]; 608 } 609 } 610 } 611 } 612} 613 614function compareExceptionKey(actual, expected, key, message, keys, fn) { 615 if (!(key in actual) || !isDeepStrictEqual(actual[key], expected[key])) { 616 if (!message) { 617 // Create placeholder objects to create a nice output. 618 const a = new Comparison(actual, keys); 619 const b = new Comparison(expected, keys, actual); 620 621 const err = new AssertionError({ 622 actual: a, 623 expected: b, 624 operator: 'deepStrictEqual', 625 stackStartFn: fn 626 }); 627 err.actual = actual; 628 err.expected = expected; 629 err.operator = fn.name; 630 throw err; 631 } 632 innerFail({ 633 actual, 634 expected, 635 message, 636 operator: fn.name, 637 stackStartFn: fn 638 }); 639 } 640} 641 642function expectedException(actual, expected, message, fn) { 643 let generatedMessage = false; 644 let throwError = false; 645 646 if (typeof expected !== 'function') { 647 // Handle regular expressions. 648 if (isRegExp(expected)) { 649 const str = String(actual); 650 if (RegExpPrototypeTest(expected, str)) 651 return; 652 653 if (!message) { 654 generatedMessage = true; 655 message = 'The input did not match the regular expression ' + 656 `${inspect(expected)}. Input:\n\n${inspect(str)}\n`; 657 } 658 throwError = true; 659 // Handle primitives properly. 660 } else if (typeof actual !== 'object' || actual === null) { 661 const err = new AssertionError({ 662 actual, 663 expected, 664 message, 665 operator: 'deepStrictEqual', 666 stackStartFn: fn 667 }); 668 err.operator = fn.name; 669 throw err; 670 } else { 671 // Handle validation objects. 672 const keys = ObjectKeys(expected); 673 // Special handle errors to make sure the name and the message are 674 // compared as well. 675 if (expected instanceof Error) { 676 ArrayPrototypePush(keys, 'name', 'message'); 677 } else if (keys.length === 0) { 678 throw new ERR_INVALID_ARG_VALUE('error', 679 expected, 'may not be an empty object'); 680 } 681 if (isDeepEqual === undefined) lazyLoadComparison(); 682 for (const key of keys) { 683 if (typeof actual[key] === 'string' && 684 isRegExp(expected[key]) && 685 RegExpPrototypeTest(expected[key], actual[key])) { 686 continue; 687 } 688 compareExceptionKey(actual, expected, key, message, keys, fn); 689 } 690 return; 691 } 692 // Guard instanceof against arrow functions as they don't have a prototype. 693 // Check for matching Error classes. 694 } else if (expected.prototype !== undefined && actual instanceof expected) { 695 return; 696 } else if (ObjectPrototypeIsPrototypeOf(Error, expected)) { 697 if (!message) { 698 generatedMessage = true; 699 message = 'The error is expected to be an instance of ' + 700 `"${expected.name}". Received `; 701 if (isError(actual)) { 702 const name = (actual.constructor && actual.constructor.name) || 703 actual.name; 704 if (expected.name === name) { 705 message += 'an error with identical name but a different prototype.'; 706 } else { 707 message += `"${name}"`; 708 } 709 if (actual.message) { 710 message += `\n\nError message:\n\n${actual.message}`; 711 } 712 } else { 713 message += `"${inspect(actual, { depth: -1 })}"`; 714 } 715 } 716 throwError = true; 717 } else { 718 // Check validation functions return value. 719 const res = ReflectApply(expected, {}, [actual]); 720 if (res !== true) { 721 if (!message) { 722 generatedMessage = true; 723 const name = expected.name ? `"${expected.name}" ` : ''; 724 message = `The ${name}validation function is expected to return` + 725 ` "true". Received ${inspect(res)}`; 726 727 if (isError(actual)) { 728 message += `\n\nCaught error:\n\n${actual}`; 729 } 730 } 731 throwError = true; 732 } 733 } 734 735 if (throwError) { 736 const err = new AssertionError({ 737 actual, 738 expected, 739 message, 740 operator: fn.name, 741 stackStartFn: fn 742 }); 743 err.generatedMessage = generatedMessage; 744 throw err; 745 } 746} 747 748function getActual(fn) { 749 if (typeof fn !== 'function') { 750 throw new ERR_INVALID_ARG_TYPE('fn', 'Function', fn); 751 } 752 try { 753 fn(); 754 } catch (e) { 755 return e; 756 } 757 return NO_EXCEPTION_SENTINEL; 758} 759 760function checkIsPromise(obj) { 761 // Accept native ES6 promises and promises that are implemented in a similar 762 // way. Do not accept thenables that use a function as `obj` and that have no 763 // `catch` handler. 764 return isPromise(obj) || 765 (obj !== null && typeof obj === 'object' && 766 typeof obj.then === 'function' && 767 typeof obj.catch === 'function'); 768} 769 770async function waitForActual(promiseFn) { 771 let resultPromise; 772 if (typeof promiseFn === 'function') { 773 // Return a rejected promise if `promiseFn` throws synchronously. 774 resultPromise = promiseFn(); 775 // Fail in case no promise is returned. 776 if (!checkIsPromise(resultPromise)) { 777 throw new ERR_INVALID_RETURN_VALUE('instance of Promise', 778 'promiseFn', resultPromise); 779 } 780 } else if (checkIsPromise(promiseFn)) { 781 resultPromise = promiseFn; 782 } else { 783 throw new ERR_INVALID_ARG_TYPE( 784 'promiseFn', ['Function', 'Promise'], promiseFn); 785 } 786 787 try { 788 await resultPromise; 789 } catch (e) { 790 return e; 791 } 792 return NO_EXCEPTION_SENTINEL; 793} 794 795function expectsError(stackStartFn, actual, error, message) { 796 if (typeof error === 'string') { 797 if (arguments.length === 4) { 798 throw new ERR_INVALID_ARG_TYPE('error', 799 ['Object', 'Error', 'Function', 'RegExp'], 800 error); 801 } 802 if (typeof actual === 'object' && actual !== null) { 803 if (actual.message === error) { 804 throw new ERR_AMBIGUOUS_ARGUMENT( 805 'error/message', 806 `The error message "${actual.message}" is identical to the message.` 807 ); 808 } 809 } else if (actual === error) { 810 throw new ERR_AMBIGUOUS_ARGUMENT( 811 'error/message', 812 `The error "${actual}" is identical to the message.` 813 ); 814 } 815 message = error; 816 error = undefined; 817 } else if (error != null && 818 typeof error !== 'object' && 819 typeof error !== 'function') { 820 throw new ERR_INVALID_ARG_TYPE('error', 821 ['Object', 'Error', 'Function', 'RegExp'], 822 error); 823 } 824 825 if (actual === NO_EXCEPTION_SENTINEL) { 826 let details = ''; 827 if (error && error.name) { 828 details += ` (${error.name})`; 829 } 830 details += message ? `: ${message}` : '.'; 831 const fnType = stackStartFn.name === 'rejects' ? 'rejection' : 'exception'; 832 innerFail({ 833 actual: undefined, 834 expected: error, 835 operator: stackStartFn.name, 836 message: `Missing expected ${fnType}${details}`, 837 stackStartFn 838 }); 839 } 840 841 if (!error) 842 return; 843 844 expectedException(actual, error, message, stackStartFn); 845} 846 847function hasMatchingError(actual, expected) { 848 if (typeof expected !== 'function') { 849 if (isRegExp(expected)) { 850 const str = String(actual); 851 return RegExpPrototypeTest(expected, str); 852 } 853 throw new ERR_INVALID_ARG_TYPE( 854 'expected', ['Function', 'RegExp'], expected 855 ); 856 } 857 // Guard instanceof against arrow functions as they don't have a prototype. 858 if (expected.prototype !== undefined && actual instanceof expected) { 859 return true; 860 } 861 if (ObjectPrototypeIsPrototypeOf(Error, expected)) { 862 return false; 863 } 864 return ReflectApply(expected, {}, [actual]) === true; 865} 866 867function expectsNoError(stackStartFn, actual, error, message) { 868 if (actual === NO_EXCEPTION_SENTINEL) 869 return; 870 871 if (typeof error === 'string') { 872 message = error; 873 error = undefined; 874 } 875 876 if (!error || hasMatchingError(actual, error)) { 877 const details = message ? `: ${message}` : '.'; 878 const fnType = stackStartFn.name === 'doesNotReject' ? 879 'rejection' : 'exception'; 880 innerFail({ 881 actual, 882 expected: error, 883 operator: stackStartFn.name, 884 message: `Got unwanted ${fnType}${details}\n` + 885 `Actual message: "${actual && actual.message}"`, 886 stackStartFn 887 }); 888 } 889 throw actual; 890} 891 892/** 893 * Expects the function `promiseFn` to throw an error. 894 * @param {() => any} promiseFn 895 * @param {...any} [args] 896 * @returns {void} 897 */ 898assert.throws = function throws(promiseFn, ...args) { 899 expectsError(throws, getActual(promiseFn), ...args); 900}; 901 902/** 903 * Expects `promiseFn` function or its value to reject. 904 * @param {() => Promise<any>} promiseFn 905 * @param {...any} [args] 906 * @returns {Promise<void>} 907 */ 908assert.rejects = async function rejects(promiseFn, ...args) { 909 expectsError(rejects, await waitForActual(promiseFn), ...args); 910}; 911 912/** 913 * Asserts that the function `fn` does not throw an error. 914 * @param {() => any} fn 915 * @param {...any} [args] 916 * @returns {void} 917 */ 918assert.doesNotThrow = function doesNotThrow(fn, ...args) { 919 expectsNoError(doesNotThrow, getActual(fn), ...args); 920}; 921 922/** 923 * Expects `fn` or its value to not reject. 924 * @param {() => Promise<any>} fn 925 * @param {...any} [args] 926 * @returns {Promise<void>} 927 */ 928assert.doesNotReject = async function doesNotReject(fn, ...args) { 929 expectsNoError(doesNotReject, await waitForActual(fn), ...args); 930}; 931 932/** 933 * Throws `value` if the value is not `null` or `undefined`. 934 * @param {any} err 935 * @returns {void} 936 */ 937assert.ifError = function ifError(err) { 938 if (err !== null && err !== undefined) { 939 let message = 'ifError got unwanted exception: '; 940 if (typeof err === 'object' && typeof err.message === 'string') { 941 if (err.message.length === 0 && err.constructor) { 942 message += err.constructor.name; 943 } else { 944 message += err.message; 945 } 946 } else { 947 message += inspect(err); 948 } 949 950 const newErr = new AssertionError({ 951 actual: err, 952 expected: null, 953 operator: 'ifError', 954 message, 955 stackStartFn: ifError 956 }); 957 958 // Make sure we actually have a stack trace! 959 const origStack = err.stack; 960 961 if (typeof origStack === 'string') { 962 // This will remove any duplicated frames from the error frames taken 963 // from within `ifError` and add the original error frames to the newly 964 // created ones. 965 const tmp2 = StringPrototypeSplit(origStack, '\n'); 966 ArrayPrototypeShift(tmp2); 967 // Filter all frames existing in err.stack. 968 let tmp1 = StringPrototypeSplit(newErr.stack, '\n'); 969 for (const errFrame of tmp2) { 970 // Find the first occurrence of the frame. 971 const pos = ArrayPrototypeIndexOf(tmp1, errFrame); 972 if (pos !== -1) { 973 // Only keep new frames. 974 tmp1 = ArrayPrototypeSlice(tmp1, 0, pos); 975 break; 976 } 977 } 978 newErr.stack = 979 `${ArrayPrototypeJoin(tmp1, '\n')}\n${ArrayPrototypeJoin(tmp2, '\n')}`; 980 } 981 982 throw newErr; 983 } 984}; 985 986function internalMatch(string, regexp, message, fn) { 987 if (!isRegExp(regexp)) { 988 throw new ERR_INVALID_ARG_TYPE( 989 'regexp', 'RegExp', regexp 990 ); 991 } 992 const match = fn.name === 'match'; 993 if (typeof string !== 'string' || 994 RegExpPrototypeTest(regexp, string) !== match) { 995 if (message instanceof Error) { 996 throw message; 997 } 998 999 const generatedMessage = !message; 1000 1001 // 'The input was expected to not match the regular expression ' + 1002 message = message || (typeof string !== 'string' ? 1003 'The "string" argument must be of type string. Received type ' + 1004 `${typeof string} (${inspect(string)})` : 1005 (match ? 1006 'The input did not match the regular expression ' : 1007 'The input was expected to not match the regular expression ') + 1008 `${inspect(regexp)}. Input:\n\n${inspect(string)}\n`); 1009 const err = new AssertionError({ 1010 actual: string, 1011 expected: regexp, 1012 message, 1013 operator: fn.name, 1014 stackStartFn: fn 1015 }); 1016 err.generatedMessage = generatedMessage; 1017 throw err; 1018 } 1019} 1020 1021/** 1022 * Expects the `string` input to match the regular expression. 1023 * @param {string} string 1024 * @param {RegExp} regexp 1025 * @param {string | Error} [message] 1026 * @returns {void} 1027 */ 1028assert.match = function match(string, regexp, message) { 1029 internalMatch(string, regexp, message, match); 1030}; 1031 1032/** 1033 * Expects the `string` input not to match the regular expression. 1034 * @param {string} string 1035 * @param {RegExp} regexp 1036 * @param {string | Error} [message] 1037 * @returns {void} 1038 */ 1039assert.doesNotMatch = function doesNotMatch(string, regexp, message) { 1040 internalMatch(string, regexp, message, doesNotMatch); 1041}; 1042 1043assert.CallTracker = CallTracker; 1044 1045/** 1046 * Expose a strict only variant of assert. 1047 * @param {...any} args 1048 * @returns {void} 1049 */ 1050function strict(...args) { 1051 innerOk(strict, args.length, ...args); 1052} 1053 1054assert.strict = ObjectAssign(strict, assert, { 1055 equal: assert.strictEqual, 1056 deepEqual: assert.deepStrictEqual, 1057 notEqual: assert.notStrictEqual, 1058 notDeepEqual: assert.notDeepStrictEqual 1059}); 1060 1061assert.strict.strict = assert.strict; 1062