@@ -30,9 +30,13 @@ function getRandomInt(max) {
3030class CaptureScreenshot {
3131 driver
3232 test
33+ /** When true, teardown skips after-failed.png (named failure shot already taken in afterEach). */
34+ _suppressTeardownFailureShot
35+
3336 constructor ( webdriver , test ) {
3437 this . driver = webdriver
3538 this . test = test
39+ this . _suppressTeardownFailureShot = false
3640 }
3741
3842 async shot ( name ) {
@@ -47,6 +51,23 @@ class CaptureScreenshot {
4751 }
4852}
4953
54+ /**
55+ * Turn a Mocha test title into a single path segment (no timestamp).
56+ */
57+ function sanitizeScreenshotFileName ( title ) {
58+ if ( ! title || typeof title !== 'string' ) {
59+ return 'unnamed-test'
60+ }
61+ let s = title
62+ . replace ( / [ / \\ ? % * : | " < > ] / g, '-' )
63+ . replace ( / \s + / g, ' ' )
64+ . trim ( )
65+ if ( s . length > 200 ) {
66+ s = s . substring ( 0 , 200 )
67+ }
68+ return s . length > 0 ? s : 'unnamed-test'
69+ }
70+
5071module . exports = {
5172 log : ( message ) => {
5273 if ( debug ) console . log ( new Date ( ) + " " + message )
@@ -177,6 +198,25 @@ module.exports = {
177198 return new CaptureScreenshot ( d . driver , require ( 'path' ) . basename ( test ) )
178199 } ,
179200
201+ /**
202+ * Call from afterEach: if the test that just finished failed, save a PNG named after
203+ * its full title (parent describes + it), with no timestamp.
204+ */
205+ captureScreenshotIfFailed : async ( captureScreen , mochaContext ) => {
206+ if ( captureScreen == null || mochaContext == null || ! mochaContext . currentTest ) {
207+ return
208+ }
209+ const ct = mochaContext . currentTest
210+ if ( ct . isPassed ( ) || ct . pending ) {
211+ captureScreen . _suppressTeardownFailureShot = false
212+ return
213+ }
214+ const fullTitle = typeof ct . fullTitle === 'function' ? ct . fullTitle ( ) : ct . title
215+ const name = sanitizeScreenshotFileName ( fullTitle )
216+ await captureScreen . shot ( name )
217+ captureScreen . _suppressTeardownFailureShot = true
218+ } ,
219+
180220 doUntil : async ( doCallback , booleanCallback , delayMs = 1000 , message = "doUntil failed" ) => {
181221 let done = false
182222 let attempts = 10
@@ -317,8 +357,12 @@ module.exports = {
317357 driver . executeScript ( 'lambda-status=passed' )
318358 } else {
319359 if ( captureScreen != null ) {
320- console . log ( "Teardown failed . capture..." ) ;
321- await captureScreen . shot ( 'after-failed' ) ;
360+ if ( captureScreen . _suppressTeardownFailureShot ) {
361+ captureScreen . _suppressTeardownFailureShot = false
362+ } else {
363+ console . log ( "Teardown failed . capture..." ) ;
364+ await captureScreen . shot ( 'after-failed' ) ;
365+ }
322366 }
323367 driver . executeScript ( 'lambda-status=failed' )
324368 }
0 commit comments