@@ -19,6 +19,13 @@ let green = '';
1919let red = '' ;
2020let white = '' ;
2121
22+ const READABLE_OPERATOR = {
23+ deepStrictEqual : 'Input A expected to strictly deep-equal input B' ,
24+ notDeepStrictEqual : 'Input A expected to strictly not deep-equal input B' ,
25+ strictEqual : 'Input A expected to strictly equal input B' ,
26+ notStrictEqual : 'Input A expected to strictly not equal input B'
27+ } ;
28+
2229const {
2330 errmap,
2431 UV_EAI_MEMORY ,
@@ -40,10 +47,34 @@ function lazyInternalUtil() {
4047 return internalUtil ;
4148}
4249
50+ function copyError ( source ) {
51+ const keys = Object . keys ( source ) ;
52+ const target = Object . create ( Object . getPrototypeOf ( source ) ) ;
53+ for ( const key of keys ) {
54+ target [ key ] = source [ key ] ;
55+ }
56+ Object . defineProperty ( target , 'message' , { value : source . message } ) ;
57+ return target ;
58+ }
59+
4360function inspectValue ( val ) {
61+ // The util.inspect default values could be changed. This makes sure the
62+ // error messages contain the necessary information nevertheless.
4463 return util . inspect (
4564 val ,
46- { compact : false , customInspect : false }
65+ {
66+ compact : false ,
67+ customInspect : false ,
68+ depth : 1000 ,
69+ maxArrayLength : Infinity ,
70+ // Assert compares only enumerable properties (with a few exceptions).
71+ showHidden : false ,
72+ // Having a long line as error is better than wrapping the line for
73+ // comparison.
74+ breakLength : Infinity ,
75+ // Assert does not detect proxies currently.
76+ showProxy : false
77+ }
4778 ) . split ( '\n' ) ;
4879}
4980
@@ -226,8 +257,8 @@ function createErrDiff(actual, expected, operator) {
226257 if ( util === undefined ) util = require ( 'util' ) ;
227258 const actualLines = inspectValue ( actual ) ;
228259 const expectedLines = inspectValue ( expected ) ;
229- const msg = `Input A expected to ${ operator } input B:\n` +
230- `${ green } + expected${ white } ${ red } - actual${ white } ` ;
260+ const msg = READABLE_OPERATOR [ operator ] +
261+ `:\n ${ green } + expected${ white } ${ red } - actual${ white } ` ;
231262 const skippedMsg = ' ... Lines skipped' ;
232263
233264 // Remove all ending lines that match (this optimizes the output for
@@ -259,6 +290,7 @@ function createErrDiff(actual, expected, operator) {
259290
260291 const maxLines = Math . max ( actualLines . length , expectedLines . length ) ;
261292 var printedLines = 0 ;
293+ var identical = 0 ;
262294 for ( i = 0 ; i < maxLines ; i ++ ) {
263295 // Only extra expected lines exist
264296 const cur = i - lastPos ;
@@ -318,12 +350,38 @@ function createErrDiff(actual, expected, operator) {
318350 res += `\n ${ actualLines [ i ] } ` ;
319351 printedLines ++ ;
320352 }
353+ identical ++ ;
321354 }
322355 // Inspected object to big (Show ~20 rows max)
323356 if ( printedLines > 20 && i < maxLines - 2 ) {
324357 return `${ msg } ${ skippedMsg } \n${ res } \n...${ other } \n...` ;
325358 }
326359 }
360+
361+ // Strict equal with identical objects that are not identical by reference.
362+ if ( identical === maxLines ) {
363+ let base = 'Input object identical but not reference equal:' ;
364+
365+ if ( operator !== 'strictEqual' ) {
366+ // This code path should not be possible to reach.
367+ // The output is identical but it is not clear why.
368+ base = 'Input objects not identical:' ;
369+ }
370+
371+ // We have to get the result again. The lines were all removed before.
372+ const actualLines = inspectValue ( actual ) ;
373+
374+ // Only remove lines in case it makes sense to collapse those.
375+ // TODO: Accept env to always show the full error.
376+ if ( actualLines . length > 30 ) {
377+ actualLines [ 26 ] = '...' ;
378+ while ( actualLines . length > 27 ) {
379+ actualLines . pop ( ) ;
380+ }
381+ }
382+
383+ return `${ base } \n\n ${ actualLines . join ( '\n ' ) } \n` ;
384+ }
327385 return `${ msg } ${ skipped ? skippedMsg : '' } \n${ res } ${ other } ${ end } ` ;
328386}
329387
@@ -358,13 +416,15 @@ class AssertionError extends Error {
358416 }
359417 }
360418 if ( util === undefined ) util = require ( 'util' ) ;
419+ // Prevent the error stack from being visible by duplicating the error
420+ // in a very close way to the original in case both sides are actually
421+ // instances of Error.
361422 if ( typeof actual === 'object' && actual !== null &&
362- 'stack' in actual && actual instanceof Error ) {
363- actual = `${ actual . name } : ${ actual . message } ` ;
364- }
365- if ( typeof expected === 'object' && expected !== null &&
366- 'stack' in expected && expected instanceof Error ) {
367- expected = `${ expected . name } : ${ expected . message } ` ;
423+ typeof expected === 'object' && expected !== null &&
424+ 'stack' in actual && actual instanceof Error &&
425+ 'stack' in expected && expected instanceof Error ) {
426+ actual = copyError ( actual ) ;
427+ expected = copyError ( expected ) ;
368428 }
369429
370430 if ( errorDiff === 0 ) {
@@ -379,15 +439,23 @@ class AssertionError extends Error {
379439 // In case the objects are equal but the operator requires unequal, show
380440 // the first object and say A equals B
381441 const res = inspectValue ( actual ) ;
442+ const base = `Identical input passed to ${ operator } :` ;
382443
383- if ( res . length > 20 ) {
384- res [ 19 ] = '...' ;
385- while ( res . length > 20 ) {
444+ // Only remove lines in case it makes sense to collapse those.
445+ // TODO: Accept env to always show the full error.
446+ if ( res . length > 30 ) {
447+ res [ 26 ] = '...' ;
448+ while ( res . length > 27 ) {
386449 res . pop ( ) ;
387450 }
388451 }
389- // Only print a single object.
390- super ( `Identical input passed to ${ operator } :\n${ res . join ( '\n' ) } ` ) ;
452+
453+ // Only print a single input.
454+ if ( res . length === 1 ) {
455+ super ( `${ base } ${ res [ 0 ] } ` ) ;
456+ } else {
457+ super ( `${ base } \n\n ${ res . join ( '\n ' ) } \n` ) ;
458+ }
391459 } else {
392460 super ( createErrDiff ( actual , expected , operator ) ) ;
393461 }
0 commit comments