@@ -7,7 +7,25 @@ const assert = require('node:assert/strict');
77const { pathToFileURL } = require ( 'node:url' ) ;
88const { hostname } = require ( 'node:os' ) ;
99
10- const stackFramesRegexp = / (?< = \n ) ( \s + ) ( ( .+ ?) \s + \( ) ? (?: \( ? ( .+ ?) : ( \d + ) (?: : ( \d + ) ) ? ) \) ? ( \s + \{ ) ? ( \[ \d + m ) ? ( \n | $ ) / g;
10+ /* eslint-disable @stylistic/js/max-len,no-control-regex */
11+ /**
12+ * Group 1: Line start (including color codes and escapes)
13+ * Group 2: Function name
14+ * Group 3: Filename
15+ * Group 4: Line number
16+ * Group 5: Column number
17+ * Group 6: Line end (including color codes and `{` which indicates the start of an error object details)
18+ */
19+ // Mappings: (g1 ) (g2 ) (g3 ) (g4 ) (g5 ) (g6 )
20+ const internalStackFramesRegexp = / (?< = \n ) ( \s * (?: \x1b ? \[ \d + m \s + ) ? (?: a t \s ) ? ) (?: ( .+ ?) \s + \( ) ? (?: ( n o d e : .+ ?) : ( \d + ) (?: : ( \d + ) ) ? ) \) ? ( (?: \x1b ? \[ \d + m ) ? \s * { ? \n | $ ) / g;
21+ /**
22+ * Group 1: Filename
23+ * Group 2: Line number
24+ * Group 3: Line end and source code line
25+ */
26+ const internalErrorSourceLines = / (?< = \n | ^ ) ( n o d e : .+ ?) : ( \d + ) ( \n .* \n \s * \^ (?: \n | $ ) ) / g;
27+ /* eslint-enable @stylistic/js/max-len,no-control-regex */
28+
1129const windowNewlineRegexp = / \r / g;
1230
1331// Replaces the current Node.js executable version strings with a
@@ -17,14 +35,33 @@ function replaceNodeVersion(str) {
1735 return str . replaceAll ( process . version , '<node-version>' ) ;
1836}
1937
20- function replaceStackTrace ( str , replacement = '$1*$7$8\n' ) {
21- return str . replace ( stackFramesRegexp , replacement ) ;
38+ // Collapse consecutive identical lines containing the keyword into
39+ // one single line. The `str` should have been processed by `replaceWindowsLineEndings`.
40+ function foldIdenticalLines ( str , keyword ) {
41+ const lines = str . split ( '\n' ) ;
42+ const folded = lines . filter ( ( line , idx ) => {
43+ if ( idx === 0 ) {
44+ return true ;
45+ }
46+ if ( line . includes ( keyword ) && line === lines [ idx - 1 ] ) {
47+ return false ;
48+ }
49+ return true ;
50+ } ) ;
51+ return folded . join ( '\n' ) ;
2252}
2353
54+ const kInternalFrame = '<node-internal-frames>' ;
55+ // Replace non-internal frame `at TracingChannel.traceSync (node:diagnostics_channel:328:14)`
56+ // as well as `at node:internal/main/run_main_module:33:47` with `at <node-internal-frames>`.
57+ // Also replaces error source line like:
58+ // node:internal/mod.js:44
59+ // throw err;
60+ // ^
2461function replaceInternalStackTrace ( str ) {
25- // Replace non-internal frame `at TracingChannel.traceSync (node:diagnostics_channel:328:14)`
26- // as well as `at node:internal/main/run_main_module:33:47` with `*`.
27- return str . replaceAll ( / ( \W + ) . * [ ( \s ] n o d e : . * / g , '$1*' ) ;
62+ const result = str . replaceAll ( internalErrorSourceLines , `$1:<line>$3` )
63+ . replaceAll ( internalStackFramesRegexp , `$1 ${ kInternalFrame } $6` ) ;
64+ return foldIdenticalLines ( result , kInternalFrame ) ;
2865}
2966
3067// Replaces Windows line endings with posix line endings for unified snapshots
@@ -73,6 +110,11 @@ function transformProjectRoot(replacement = '<project-root>') {
73110 } ;
74111}
75112
113+ // Replaces tmpdirs created by `test/common/tmpdir.js`.
114+ function transformTmpDir ( str ) {
115+ return str . replaceAll ( / \/ \. t m p \. \d + \/ / g, '/<tmpdir>/' ) ;
116+ }
117+
76118function transform ( ...args ) {
77119 return ( str ) => args . reduce ( ( acc , fn ) => fn ( acc ) , str ) ;
78120}
@@ -143,33 +185,24 @@ function replaceTestDuration(str) {
143185}
144186
145187const root = path . resolve ( __dirname , '..' , '..' ) ;
146- const color = '(\\[\\d+m)' ;
147- const stackTraceBasePath = new RegExp ( `${ color } \\(${ RegExp . escape ( root ) } /?${ color } (.*)${ color } \\)` , 'g' ) ;
148-
149188function replaceSpecDuration ( str ) {
150189 return str
151190 . replaceAll ( / [ 0 - 9 . ] + m s / g, '*ms' )
152- . replaceAll ( / d u r a t i o n _ m s [ 0 - 9 . ] + / g, 'duration_ms *' )
153- . replace ( stackTraceBasePath , '$3' ) ;
191+ . replaceAll ( / d u r a t i o n _ m s [ 0 - 9 . ] + / g, 'duration_ms *' ) ;
154192}
155193
156194function replaceJunitDuration ( str ) {
157195 return str
158196 . replaceAll ( / t i m e = " [ 0 - 9 . ] + " / g, 'time="*"' )
159197 . replaceAll ( / d u r a t i o n _ m s [ 0 - 9 . ] + / g, 'duration_ms *' )
160198 . replaceAll ( `hostname="${ hostname ( ) } "` , 'hostname="HOSTNAME"' )
161- . replaceAll ( / f i l e = " [ ^ " ] * " / g, 'file="*"' )
162- . replace ( stackTraceBasePath , '$3' ) ;
199+ . replaceAll ( / f i l e = " [ ^ " ] * " / g, 'file="*"' ) ;
163200}
164201
165202function removeWindowsPathEscaping ( str ) {
166203 return common . isWindows ? str . replaceAll ( / \\ \\ / g, '\\' ) : str ;
167204}
168205
169- function replaceTestLocationLine ( str ) {
170- return str . replaceAll ( / ( j s : ) ( \d + ) ( : \d + ) / g, '$1(LINE)$3' ) ;
171- }
172-
173206// The Node test coverage returns results for all files called by the test. This
174207// will make the output file change if files like test/common/index.js change.
175208// This transform picks only the first line and then the lines from the test
@@ -188,9 +221,11 @@ function pickTestFileFromLcov(str) {
188221}
189222
190223// Transforms basic patterns like:
191- // - platform specific path and line endings,
192- // - line trailing spaces,
193- // - executable specific path and versions.
224+ // - platform specific path and line endings
225+ // - line trailing spaces
226+ // - executable specific path and versions
227+ // - project root path and tmpdir
228+ // - node internal stack frames
194229const basicTransform = transform (
195230 replaceWindowsLineEndings ,
196231 replaceTrailingSpaces ,
@@ -199,29 +234,25 @@ const basicTransform = transform(
199234 replaceNodeVersion ,
200235 generalizeExeName ,
201236 replaceWarningPid ,
237+ transformProjectRoot ( ) ,
238+ transformTmpDir ,
239+ replaceInternalStackTrace ,
202240) ;
203241
204242const defaultTransform = transform (
205243 basicTransform ,
206- replaceStackTrace ,
207- transformProjectRoot ( ) ,
208244 replaceTestDuration ,
209- replaceTestLocationLine ,
210245) ;
211246const specTransform = transform (
212247 replaceSpecDuration ,
213248 basicTransform ,
214- replaceStackTrace ,
215249) ;
216250const junitTransform = transform (
217251 replaceJunitDuration ,
218252 basicTransform ,
219- replaceStackTrace ,
220253) ;
221254const lcovTransform = transform (
222255 basicTransform ,
223- replaceStackTrace ,
224- transformProjectRoot ( ) ,
225256 pickTestFileFromLcov ,
226257) ;
227258
@@ -240,7 +271,6 @@ module.exports = {
240271 assertSnapshot,
241272 getSnapshotPath,
242273 replaceNodeVersion,
243- replaceStackTrace,
244274 replaceInternalStackTrace,
245275 replaceWindowsLineEndings,
246276 replaceWindowsPaths,
0 commit comments