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