@@ -215,6 +215,35 @@ export async function debugPause() {
215215 await new Promise ( ( ) => { } ) ;
216216}
217217
218+ /**
219+ * Screenshot the app as it stands when a test fails.
220+ *
221+ * @remarks Must opt-in by setting the screenshot directory with `SLOBS_FAIL_SCREENSHOT_DIR`, so a normal test run
222+ * does not capture screenshots for every test. The file is named after the test (`<kebab-test-name>.png`) limited to 80 chars.
223+ *
224+ * Called from `afterEach.always` before the teardown stops the app, which is the last moment the failing UI still exists.
225+ * This should never throw so a failed capture does not register as real failure.
226+ */
227+ async function saveFailureScreenshot ( t : TExecutionContext ) {
228+ const dir = process . env . SLOBS_FAIL_SCREENSHOT_DIR ;
229+ if ( ! dir ) return ;
230+
231+ try {
232+ const imgTitle = t . title
233+ . toLowerCase ( )
234+ . replace ( / [ ^ a - z 0 - 9 ] + / g, '-' )
235+ . replace ( / ^ - + | - + $ / g, '' )
236+ . slice ( 0 , 80 ) ;
237+
238+ fs . mkdirSync ( dir , { recursive : true } ) ;
239+ const filePath = path . join ( dir , `${ imgTitle } .png` ) ;
240+ await t . context . app . client . saveScreenshot ( filePath ) ;
241+ console . log ( `Failure screenshot: ${ filePath } ` ) ;
242+ } catch ( e : unknown ) {
243+ console . log ( `Could not capture a failure screenshot for test ${ t . title } :` , e ) ;
244+ }
245+ }
246+
218247export function useWebdriver ( options : ITestRunnerOptions = { } ) {
219248 // tslint:disable-next-line:no-parameter-reassignment TODO
220249 options = Object . assign ( { } , DEFAULT_OPTIONS , options ) ;
@@ -456,6 +485,10 @@ export function useWebdriver(options: ITestRunnerOptions = {}) {
456485
457486 test . afterEach . always ( async t => {
458487 await checkErrorsInLogFile ( t ) ;
488+
489+ // Capture the failing UI before the teardown below stops the app. Off unless SLOBS_FAIL_SCREENSHOT_DIR is set.
490+ if ( ! testPassed && appIsRunning ) await saveFailureScreenshot ( t ) ;
491+
459492 if ( ! testPassed && options . pauseIfFailed ) {
460493 console . log ( 'Test execution has been paused due `pauseIfFailed` enabled' ) ;
461494 await sleep ( ALMOST_INFINITY ) ;
0 commit comments