From 75abf03fedf60755b398f7336b3b26be92f33d22 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 3 Mar 2022 07:59:29 -0700 Subject: [PATCH] Improve end-to-end test logging by rendering JSHandles (#7959) Fixes https://github.com/vector-im/element-web/issues/13276 It's still not super pretty, but it works. --- test/end-to-end-tests/src/session.ts | 4 +-- test/end-to-end-tests/src/util.ts | 50 ++++++++++++++++++++++++++++ test/end-to-end-tests/start.ts | 2 +- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/test/end-to-end-tests/src/session.ts b/test/end-to-end-tests/src/session.ts index 145432f7ab..84f22c4b0c 100644 --- a/test/end-to-end-tests/src/session.ts +++ b/test/end-to-end-tests/src/session.ts @@ -19,7 +19,7 @@ import * as puppeteer from 'puppeteer'; import { Logger } from './logger'; import { LogBuffer } from './logbuffer'; -import { delay } from './util'; +import { delay, serializeLog } from './util'; const DEFAULT_TIMEOUT = 20000; @@ -35,7 +35,7 @@ export class ElementSession { constructor(readonly browser: puppeteer.Browser, readonly page: puppeteer.Page, readonly username: string, readonly elementServer: string, readonly hsUrl: string) { this.consoleLog = new LogBuffer(page, "console", - async (msg: puppeteer.ConsoleMessage) => Promise.resolve(`${msg.text()}\n`)); + async (msg: puppeteer.ConsoleMessage) => `${await serializeLog(msg)}\n`); this.networkLog = new LogBuffer(page, "requestfinished", async (req: puppeteer.HTTPRequest) => { const type = req.resourceType(); diff --git a/test/end-to-end-tests/src/util.ts b/test/end-to-end-tests/src/util.ts index cfb00394a9..1c4e78933a 100644 --- a/test/end-to-end-tests/src/util.ts +++ b/test/end-to-end-tests/src/util.ts @@ -15,6 +15,9 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { ConsoleMessage } from "puppeteer"; +import { padEnd } from "lodash"; + import { ElementSession } from "./session"; export const range = function(start: number, amount: number, step = 1): Array { @@ -51,3 +54,50 @@ export async function applyConfigChange(session: ElementSession, config: any): P } }, config); } + +export async function serializeLog(msg: ConsoleMessage): Promise { + // 9 characters padding is somewhat arbitrary ("warning".length + some) + let s = `${padEnd(msg.type(), 9, ' ')}| ${msg.text()} `; // trailing space is intentional + const args = msg.args(); + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + const val = await arg.jsonValue(); + + // We handle strings a bit differently because the `jsonValue` will be in a weird-looking + // shape ("JSHandle:words are here"). Weirdly, `msg.text()` also catches text nodes that + // we can't with our parsing, so we trust that it's correct whenever we can. + if (typeof val === 'string') { + if (i === 0) { + // if it's a string, just ignore it because it should have already been caught + // by the `msg.text()` in the initial `s` construction. + continue; + } + + // evaluate the arg as a string by running it through the page context + s += `${await arg.evaluate(a => a.toString())} `; // trailing space is intentional + continue; + } + + // Try and parse the value as an error object first (which will be an empty JSON + // object). Otherwise, parse the object to a string. + // + // Note: we have to run the checks against the object in the page context, so call + // evaluate instead of just doing it ourselves. + const stringyArg: string = await arg.evaluate((argInContext: any) => { + if (argInContext.stack || (argInContext instanceof Error)) { + // probably an error - toString it and append any properties which might not be + // caught. For example, on HTTP errors the JSON stringification will capture the + // status code. + // + // String format is a bit weird, but basically we're trying to get away from the + // stack trace so the context doesn't blend in but is otherwise indented correctly. + return `${argInContext.toString()}\n\n Error context: ${JSON.stringify(argInContext)}`; + } + + // not an error, as far as we're concerned - return it as human-readable JSON + return JSON.stringify(argInContext, null, 4); + }); + s += `${stringyArg} `; // trailing space is intentional + } + return s; +} diff --git a/test/end-to-end-tests/start.ts b/test/end-to-end-tests/start.ts index b346f9165d..4660ee01b0 100644 --- a/test/end-to-end-tests/start.ts +++ b/test/end-to-end-tests/start.ts @@ -112,7 +112,7 @@ async function runTests() { /** * TODO: temporary only use one user session data */ - performanceEntries = JSON.parse(measurements); + performanceEntries = JSON.parse(measurements ?? "[]"); return session.close(); })); if (performanceEntries?.length > 0) {