22// allow time for that so we get a more specific error message
33jest . setTimeout ( 95000 ) ; // eslint-disable-line no-undef
44
5+ import { promises as fs } from 'fs' ;
6+ import path from 'path' ;
7+
58import bindAll from 'lodash.bindall' ;
69import webdriver from 'selenium-webdriver' ;
710
11+ import packageJson from '../../package.json' ;
12+
813const { Button, By, until} = webdriver ;
914
1015const USE_HEADLESS = process . env . USE_HEADLESS !== 'no' ;
@@ -14,6 +19,31 @@ const USE_HEADLESS = process.env.USE_HEADLESS !== 'no';
1419// The Jasmine default timeout is 30 seconds so make sure this is lower.
1520const DEFAULT_TIMEOUT_MILLISECONDS = 20 * 1000 ;
1621
22+ // There doesn't seem to be a way to ask Jest (or jest-junit) for its output directory. The idea here is that if we
23+ // change the way we define the output directory in package.json, move it to a separate config file, etc.,
24+ // a helpful error is better than a confusing error or silently dropping error output from CI.
25+ const testResultsDir = packageJson . jest . reporters
26+ . map ( r => r [ 1 ] . outputDirectory )
27+ . filter ( x => x ) [ 0 ] ;
28+ if ( ! testResultsDir ) {
29+ throw new Error ( 'Could not determine Jest test results directory' ) ;
30+ }
31+
32+ /**
33+ * Recursively check if this error or any errors in its causal chain have been "enhanced" by `enhanceError`.
34+ * @param {Error } error The error to check.
35+ * @returns {boolean } True if the error or any of its causes have been enhanced.
36+ */
37+ const isEnhancedError = error => {
38+ while ( error ) {
39+ if ( error . scratchEnhancedError ) {
40+ return true ;
41+ }
42+ error = error . cause ;
43+ }
44+ return false ;
45+ } ;
46+
1747/**
1848 * Add more debug information to an error:
1949 * - Merge a causal error into an outer error with valuable stack information
@@ -26,6 +56,7 @@ const DEFAULT_TIMEOUT_MILLISECONDS = 20 * 1000;
2656 * @returns {Promise<Error> } The outerError, with the cause embedded.
2757 */
2858const enhanceError = async ( outerError , cause , driver ) => {
59+ outerError . scratchEnhancedError = true ;
2960 if ( cause ) {
3061 // This is the official way to nest errors in modern Node.js, but Jest ignores this field.
3162 // It's here in case a future version uses it, or in case the caller does.
@@ -36,18 +67,27 @@ const enhanceError = async (outerError, cause, driver) => {
3667 } else {
3768 outerError . message += '\nCause: unknown' ;
3869 }
39- if ( driver ) {
40- const url = await driver . getCurrentUrl ( ) ;
41- const title = await driver . getTitle ( ) ;
42- const pageSource = await driver . getPageSource ( ) ;
70+ // Don't make a second copy of this debug info if an inner error has already done it,
71+ // especially since retrieving the browser log is a destructive operation.
72+ if ( driver && ! isEnhancedError ( cause ) ) {
73+ await fs . mkdir ( testResultsDir , { recursive : true } ) ;
74+ const errorInfoDir = await fs . mkdtemp ( `${ testResultsDir } /selenium-error-` ) ;
75+ outerError . message += `\nDebug info stored in: ${ errorInfoDir } ` ;
76+
77+ const pageInfoPath = path . join ( errorInfoDir , 'info.json' ) ;
78+ await fs . writeFile ( pageInfoPath , JSON . stringify ( {
79+ currentUrl : await driver . getCurrentUrl ( ) ,
80+ pageTitle : await driver . getTitle ( )
81+ } , null , 2 ) ) ;
82+
83+ const pageSourcePath = path . join ( errorInfoDir , 'page.html' ) ;
84+ await fs . writeFile ( pageSourcePath , await driver . getPageSource ( ) ) ;
85+
86+ const browserLogPath = path . join ( errorInfoDir , 'browser-log.txt' ) ;
4387 const browserLogEntries = await driver . manage ( )
4488 . logs ( )
4589 . get ( 'browser' ) ;
46- const browserLogText = browserLogEntries . map ( entry => entry . message ) . join ( '\n' ) ;
47- outerError . message += `\nBrowser URL: ${ url } ` ;
48- outerError . message += `\nBrowser title: ${ title } ` ;
49- outerError . message += `\nBrowser logs:\n*****\n${ browserLogText } \n*****\n` ;
50- outerError . message += `\nBrowser page source:\n*****\n${ pageSource } \n*****\n` ;
90+ await fs . writeFile ( browserLogPath , JSON . stringify ( browserLogEntries , null , 2 ) ) ;
5191 }
5292 return outerError ;
5393} ;
0 commit comments