Skip to content

Commit 5d06c1e

Browse files
BridgeARtargos
authored andcommitted
assert: move AssertionError into own file
This moves the `assert` parts from `internal/errors` into an own file. `internal/errors` got bigger and bigger and it was difficult to keep a good overview of what was going on. While doing so it also removes the `internalAssert` function and just lazy loads `assert`. PR-URL: #20486 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
1 parent 01abed1 commit 5d06c1e

5 files changed

Lines changed: 301 additions & 306 deletions

File tree

‎lib/assert.js‎

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,12 @@ const {
2525
isDeepEqual,
2626
isDeepStrictEqual
2727
}=require('internal/util/comparisons');
28-
const{
29-
AssertionError,
30-
errorCache,
31-
codes: {
32-
ERR_AMBIGUOUS_ARGUMENT,
33-
ERR_INVALID_ARG_TYPE,
34-
ERR_INVALID_RETURN_VALUE
35-
}
36-
}=require('internal/errors');
28+
const{codes: {
29+
ERR_AMBIGUOUS_ARGUMENT,
30+
ERR_INVALID_ARG_TYPE,
31+
ERR_INVALID_RETURN_VALUE
32+
}}=require('internal/errors');
33+
const{ AssertionError, errorCache }=require('internal/assert');
3734
const{ openSync, closeSync, readSync }=require('fs');
3835
const{ inspect,types: { isPromise, isRegExp }}=require('util');
3936
const{EOL}=require('os');

‎lib/internal/assert.js‎

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
'use strict';
2+
3+
const{ inspect }=require('util');
4+
const{codes: {
5+
ERR_INVALID_ARG_TYPE
6+
}}=require('internal/errors');
7+
8+
letblue='';
9+
letgreen='';
10+
letred='';
11+
letwhite='';
12+
13+
constREADABLE_OPERATOR={
14+
deepStrictEqual: 'Input A expected to strictly deep-equal input B',
15+
notDeepStrictEqual: 'Input A expected to strictly not deep-equal input B',
16+
strictEqual: 'Input A expected to strictly equal input B',
17+
notStrictEqual: 'Input A expected to strictly not equal input B'
18+
};
19+
20+
functioncopyError(source){
21+
constkeys=Object.keys(source);
22+
consttarget=Object.create(Object.getPrototypeOf(source));
23+
for(constkeyofkeys){
24+
target[key]=source[key];
25+
}
26+
Object.defineProperty(target,'message',{value: source.message});
27+
returntarget;
28+
}
29+
30+
functioninspectValue(val){
31+
// The util.inspect default values could be changed. This makes sure the
32+
// error messages contain the necessary information nevertheless.
33+
returninspect(
34+
val,
35+
{
36+
compact: false,
37+
customInspect: false,
38+
depth: 1000,
39+
maxArrayLength: Infinity,
40+
// Assert compares only enumerable properties (with a few exceptions).
41+
showHidden: false,
42+
// Having a long line as error is better than wrapping the line for
43+
// comparison.
44+
breakLength: Infinity,
45+
// Assert does not detect proxies currently.
46+
showProxy: false
47+
}
48+
).split('\n');
49+
}
50+
51+
functioncreateErrDiff(actual,expected,operator){
52+
varother='';
53+
varres='';
54+
varlastPos=0;
55+
varend='';
56+
varskipped=false;
57+
constactualLines=inspectValue(actual);
58+
constexpectedLines=inspectValue(expected);
59+
constmsg=READABLE_OPERATOR[operator]+
60+
`:\n${green}+ expected${white}${red}- actual${white}`;
61+
constskippedMsg=` ${blue}...${white} Lines skipped`;
62+
63+
// Remove all ending lines that match (this optimizes the output for
64+
// readability by reducing the number of total changed lines).
65+
vara=actualLines[actualLines.length-1];
66+
varb=expectedLines[expectedLines.length-1];
67+
vari=0;
68+
while(a===b){
69+
if(i++<2){
70+
end=`\n ${a}${end}`;
71+
}else{
72+
other=a;
73+
}
74+
actualLines.pop();
75+
expectedLines.pop();
76+
if(actualLines.length===0||expectedLines.length===0)
77+
break;
78+
a=actualLines[actualLines.length-1];
79+
b=expectedLines[expectedLines.length-1];
80+
}
81+
if(i>3){
82+
end=`\n${blue}...${white}${end}`;
83+
skipped=true;
84+
}
85+
if(other!==''){
86+
end=`\n ${other}${end}`;
87+
other='';
88+
}
89+
90+
constmaxLines=Math.max(actualLines.length,expectedLines.length);
91+
varprintedLines=0;
92+
varidentical=0;
93+
for(i=0;i<maxLines;i++){
94+
// Only extra expected lines exist
95+
constcur=i-lastPos;
96+
if(actualLines.length<i+1){
97+
if(cur>1&&i>2){
98+
if(cur>4){
99+
res+=`\n${blue}...${white}`;
100+
skipped=true;
101+
}elseif(cur>3){
102+
res+=`\n ${expectedLines[i-2]}`;
103+
printedLines++;
104+
}
105+
res+=`\n ${expectedLines[i-1]}`;
106+
printedLines++;
107+
}
108+
lastPos=i;
109+
other+=`\n${green}+${white}${expectedLines[i]}`;
110+
printedLines++;
111+
// Only extra actual lines exist
112+
}elseif(expectedLines.length<i+1){
113+
if(cur>1&&i>2){
114+
if(cur>4){
115+
res+=`\n${blue}...${white}`;
116+
skipped=true;
117+
}elseif(cur>3){
118+
res+=`\n ${actualLines[i-2]}`;
119+
printedLines++;
120+
}
121+
res+=`\n ${actualLines[i-1]}`;
122+
printedLines++;
123+
}
124+
lastPos=i;
125+
res+=`\n${red}-${white}${actualLines[i]}`;
126+
printedLines++;
127+
// Lines diverge
128+
}elseif(actualLines[i]!==expectedLines[i]){
129+
if(cur>1&&i>2){
130+
if(cur>4){
131+
res+=`\n${blue}...${white}`;
132+
skipped=true;
133+
}elseif(cur>3){
134+
res+=`\n ${actualLines[i-2]}`;
135+
printedLines++;
136+
}
137+
res+=`\n ${actualLines[i-1]}`;
138+
printedLines++;
139+
}
140+
lastPos=i;
141+
res+=`\n${red}-${white}${actualLines[i]}`;
142+
other+=`\n${green}+${white}${expectedLines[i]}`;
143+
printedLines+=2;
144+
// Lines are identical
145+
}else{
146+
res+=other;
147+
other='';
148+
if(cur===1||i===0){
149+
res+=`\n ${actualLines[i]}`;
150+
printedLines++;
151+
}
152+
identical++;
153+
}
154+
// Inspected object to big (Show ~20 rows max)
155+
if(printedLines>20&&i<maxLines-2){
156+
return`${msg}${skippedMsg}\n${res}\n${blue}...${white}${other}\n`+
157+
`${blue}...${white}`;
158+
}
159+
}
160+
161+
// Strict equal with identical objects that are not identical by reference.
162+
if(identical===maxLines){
163+
// E.g., assert.deepStrictEqual(Symbol(), Symbol())
164+
constbase=operator==='strictEqual' ?
165+
'Input objects identical but not reference equal:' :
166+
'Input objects not identical:';
167+
168+
// We have to get the result again. The lines were all removed before.
169+
constactualLines=inspectValue(actual);
170+
171+
// Only remove lines in case it makes sense to collapse those.
172+
// TODO: Accept env to always show the full error.
173+
if(actualLines.length>30){
174+
actualLines[26]=`${blue}...${white}`;
175+
while(actualLines.length>27){
176+
actualLines.pop();
177+
}
178+
}
179+
180+
return`${base}\n\n${actualLines.join('\n')}\n`;
181+
}
182+
return`${msg}${skipped ? skippedMsg : ''}\n${res}${other}${end}`;
183+
}
184+
185+
classAssertionErrorextendsError{
186+
constructor(options){
187+
if(typeofoptions!=='object'||options===null){
188+
thrownewERR_INVALID_ARG_TYPE('options','Object',options);
189+
}
190+
var{
191+
actual,
192+
expected,
193+
message,
194+
operator,
195+
stackStartFn
196+
}=options;
197+
198+
if(message!=null){
199+
super(message);
200+
}else{
201+
if(process.stdout.isTTY){
202+
// Reset on each call to make sure we handle dynamically set environment
203+
// variables correct.
204+
if(process.stdout.getColorDepth()!==1){
205+
blue='\u001b[34m';
206+
green='\u001b[32m';
207+
white='\u001b[39m';
208+
red='\u001b[31m';
209+
}else{
210+
blue='';
211+
green='';
212+
white='';
213+
red='';
214+
}
215+
}
216+
// Prevent the error stack from being visible by duplicating the error
217+
// in a very close way to the original in case both sides are actually
218+
// instances of Error.
219+
if(typeofactual==='object'&&actual!==null&&
220+
typeofexpected==='object'&&expected!==null&&
221+
'stack'inactual&&actualinstanceofError&&
222+
'stack'inexpected&&expectedinstanceofError){
223+
actual=copyError(actual);
224+
expected=copyError(expected);
225+
}
226+
227+
if(operator==='deepStrictEqual'||operator==='strictEqual'){
228+
super(createErrDiff(actual,expected,operator));
229+
}elseif(operator==='notDeepStrictEqual'||
230+
operator==='notStrictEqual'){
231+
// In case the objects are equal but the operator requires unequal, show
232+
// the first object and say A equals B
233+
constres=inspectValue(actual);
234+
constbase=`Identical input passed to ${operator}:`;
235+
236+
// Only remove lines in case it makes sense to collapse those.
237+
// TODO: Accept env to always show the full error.
238+
if(res.length>30){
239+
res[26]=`${blue}...${white}`;
240+
while(res.length>27){
241+
res.pop();
242+
}
243+
}
244+
245+
// Only print a single input.
246+
if(res.length===1){
247+
super(`${base}${res[0]}`);
248+
}else{
249+
super(`${base}\n\n${res.join('\n')}\n`);
250+
}
251+
}else{
252+
letres=inspect(actual);
253+
letother=inspect(expected);
254+
if(res.length>128)
255+
res=`${res.slice(0,125)}...`;
256+
if(other.length>128)
257+
other=`${other.slice(0,125)}...`;
258+
super(`${res}${operator}${other}`);
259+
}
260+
}
261+
262+
this.generatedMessage=!message;
263+
this.name='AssertionError [ERR_ASSERTION]';
264+
this.code='ERR_ASSERTION';
265+
this.actual=actual;
266+
this.expected=expected;
267+
this.operator=operator;
268+
Error.captureStackTrace(this,stackStartFn);
269+
}
270+
}
271+
272+
module.exports={
273+
AssertionError,
274+
errorCache: newMap()
275+
};

0 commit comments

Comments
 (0)