[testing] Write screenshots to disk if rdb is not available

Bug: b:333423685
Change-Id: I7d866814d17860e0704d1fda12f4183d9bfdab56
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/5442779
Commit-Queue: Philip Pfaffe <pfaffe@chromium.org>
Reviewed-by: Simon Zünd <szuend@chromium.org>
Reviewed-by: Eric Leese <leese@chromium.org>
This commit is contained in:
Philip Pfaffe
2024-04-11 11:20:53 +00:00
committed by Devtools-frontend LUCI CQ
parent 9101ed587a
commit 5823e11eba
3 changed files with 39 additions and 12 deletions
+2 -12
View File
@@ -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);
+32
View File
@@ -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. <pre> and </pre>).
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 = `<pre>${getErrorMessage(error).slice(0, ResultsDbReporter.SUMMARY_LENGTH_CUTOFF)}</pre>`;
}
if (this.htmlResult) {
this.htmlResult.write(testResult.summaryHtml);
if (testResult.artifacts) {
for (const screenshot in testResult.artifacts) {
this.htmlResult.write(`<details><summary>${screenshot} screenshot:</summary><p><img src="${
testResult.artifacts[screenshot].filePath}"></img></p></details>`);
}
}
this.htmlResult.write('<hr>');
}
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;
+5
View File
@@ -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 {