diff --git a/test/shared/mocha-extensions.ts b/test/shared/mocha-extensions.ts index 83ff14ee1c..a1ef4e4a78 100644 --- a/test/shared/mocha-extensions.ts +++ b/test/shared/mocha-extensions.ts @@ -32,16 +32,6 @@ export async function takeScreenshots(): Promise<{target?: string, frontend?: st } } -async function takeScreenshotsForError(): Promise<{target?: string, frontend?: string}> { - if (getEnvVar('LUCI_CONTEXT')) { - const screenshots = await takeScreenshots(); - console.error('Screenshots will be uploaded to luci-milo invocation.'); - return screenshots; - } - console.error('Screenshots skipped because we are not running under rdb; did you run `npm run auto-e2etest-rdb`?'); - return {}; -} - function wrapSuiteFunction(fn: (this: Mocha.Suite) => void) { return function(this: Mocha.Suite): void { const hookCreationHook = (hook: Mocha.Hook) => { @@ -144,7 +134,7 @@ async function timeoutHook(this: Mocha.Runnable, done: Mocha.Done|undefined, err } } if (err && !getEnvVar('DEBUG_TEST') && !(err instanceof ScreenshotError)) { - const {target, frontend} = await takeScreenshotsForError(); + const {target, frontend} = await takeScreenshots(); err = ScreenshotError.fromBase64Images(err, target, frontend); } if (done) { @@ -227,7 +217,7 @@ function wrapMochaCall( async function onError(this: unknown, err?: unknown) { const isTimeoutError = err instanceof Error && err.message?.includes(TIMEOUT_ERROR_MESSAGE); if (err && !getEnvVar('DEBUG_TEST') && !(err instanceof ScreenshotError) && !isTimeoutError) { - const {target, frontend} = await takeScreenshotsForError(); + const {target, frontend} = await takeScreenshots(); err = ScreenshotError.fromBase64Images(err, target, frontend); } done.call(this, err); diff --git a/test/shared/mocha-resultsdb-reporter.ts b/test/shared/mocha-resultsdb-reporter.ts index c1b632b604..3699861108 100644 --- a/test/shared/mocha-resultsdb-reporter.ts +++ b/test/shared/mocha-resultsdb-reporter.ts @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import * as fs from 'fs'; import * as Mocha from 'mocha'; +import * as path from 'path'; import { ScreenshotError, @@ -49,6 +51,12 @@ class ResultsDbReporter extends Mocha.reporters.Spec { // the rest of the HTML formatting (e.g.
 and 
). static readonly SUMMARY_LENGTH_CUTOFF = 3985; private suitePrefix?: string; + htmlResult: fs.WriteStream|undefined; + + localResultsPath() { + return !ResultsDb.available() && this.suitePrefix ? path.join(__dirname, '..', this.suitePrefix, 'results.html') : + undefined; + } constructor(runner: Mocha.Runner, options?: Mocha.MochaOptions) { super(runner, options); @@ -56,6 +64,12 @@ class ResultsDbReporter extends Mocha.reporters.Spec { // BUT, every module.exports is forwarded onto the options object. this.suitePrefix = (options as {suiteName: string} | undefined)?.suiteName; + const localResults = this.localResultsPath(); + + if (localResults) { + this.htmlResult = fs.createWriteStream(localResults, {}); + } + runner.on(EVENT_TEST_PASS, this.onTestPass.bind(this)); runner.on(EVENT_TEST_FAIL, this.onTestFail.bind(this)); runner.on(EVENT_TEST_RETRY, this.onTestFail.bind(this)); @@ -78,6 +92,16 @@ class ResultsDbReporter extends Mocha.reporters.Spec { } else { testResult.summaryHtml = `
${getErrorMessage(error).slice(0, ResultsDbReporter.SUMMARY_LENGTH_CUTOFF)}
`; } + if (this.htmlResult) { + this.htmlResult.write(testResult.summaryHtml); + if (testResult.artifacts) { + for (const screenshot in testResult.artifacts) { + this.htmlResult.write(`
${screenshot} screenshot:

`); + } + } + this.htmlResult.write('
'); + } ResultsDb.sendTestResult(testResult); } @@ -98,6 +122,14 @@ class ResultsDbReporter extends Mocha.reporters.Spec { tags: [{key: 'run', 'value': String(testRetry.currentRetry() + 1)}], }; } + + override epilogue() { + super.epilogue(); + const localResults = this.localResultsPath(); + if (this.failures.length > 0 && localResults) { + console.error(`Results have been written to file://${localResults}`); + } + } } exports = module.exports = ResultsDbReporter; diff --git a/test/shared/resultsdb.ts b/test/shared/resultsdb.ts index cb54beb5b8..5267626528 100644 --- a/test/shared/resultsdb.ts +++ b/test/shared/resultsdb.ts @@ -64,6 +64,11 @@ function getSinkData(): SinkData { return resolvedSinkData; } +export function available() { + const sinkData = getSinkData(); + return sinkData.url !== undefined; +} + // Call at the end of a test suite. Will send all `TestResult`s collected via // `recordTestResult` to the ResultSink endpoint (only if available). export function sendTestResult(results: TestResult): void {