From f09ec2cf2648e8287fb5f4bbd445f34bd117ad13 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Wed, 18 Oct 2023 11:52:07 +0100 Subject: [PATCH] Cypress: distinguish logs from bot clients (#11770) When we instantiate a `MatrixClient` for a "bot" user, give it a custom `Logger` which will add a prefix to any logs written by that client. --- cypress/support/bot.ts | 50 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/cypress/support/bot.ts b/cypress/support/bot.ts index c1e1e9221b..4273b65a23 100644 --- a/cypress/support/bot.ts +++ b/cypress/support/bot.ts @@ -16,13 +16,16 @@ limitations under the License. /// +import * as loglevel from "loglevel"; + import type { ISendEventResponse, MatrixClient, Room } from "matrix-js-sdk/src/matrix"; import type { GeneratedSecretStorageKey } from "matrix-js-sdk/src/crypto-api"; import type { AddSecretStorageKeyOpts } from "matrix-js-sdk/src/secret-storage"; import { HomeserverInstance } from "../plugins/utils/homeserver"; import { Credentials } from "./homeserver"; -import Chainable = Cypress.Chainable; import { collapseLastLogGroup } from "./log"; +import type { Logger } from "matrix-js-sdk/src/logger"; +import Chainable = Cypress.Chainable; interface CreateBotOpts { /** @@ -140,6 +143,8 @@ function setupBotClient( // extra timeout, as this sometimes takes a while { timeout: 30_000 }, async (win): Promise => { + const logger = getLogger(win, `cypress bot ${credentials.userId}`); + const keys = {}; const getCrossSigningKey = (type: string) => { @@ -176,6 +181,7 @@ function setupBotClient( store: new win.matrixcs.MemoryStore(), scheduler: new win.matrixcs.MatrixScheduler(), cryptoStore: new win.matrixcs.MemoryCryptoStore(), + logger: logger, cryptoCallbacks, }); @@ -323,3 +329,45 @@ Cypress.Commands.add( ); }, ); + +/** Get a Logger implementation based on `loglevel` with the given logger name */ +function getLogger(win: Cypress.AUTWindow, loggerName: string): Logger { + const logger = loglevel.getLogger(loggerName); + + // If this is the first time this logger has been returned, turn it into a `Logger` and set the default level + if (!("extend" in logger)) { + logger["extend"] = (namespace: string) => getLogger(win, loggerName + ":" + namespace); + logger.methodFactory = makeLogMethodFactory(win); + logger.setLevel(loglevel.levels.DEBUG); + } + + return logger as unknown as Logger; +} + +/** + * Helper for getLogger: a factory for loglevel method factories. + */ +function makeLogMethodFactory(win: Cypress.AUTWindow): loglevel.MethodFactory { + function methodFactory( + methodName: loglevel.LogLevelNames, + level: loglevel.LogLevelNumbers, + loggerName: string | symbol, + ): loglevel.LoggingMethod { + // here's the actual log method, which implements `Logger.info`, `Logger.debug`, etc. + return function (first: any, ...rest): void { + // include the logger name in the output... + first = `\x1B[31m[${loggerName.toString()}]\x1B[m ${first.toString()}`; + + // ... and delegate to the corresponding method in the console of the application under test. + // Doing so (rather than using the global `console`) ensures that the output is collected + // by the `cypress-terminal-report` plugin. + const console = win.console; + if (methodName in console) { + console[methodName](first, ...rest); + } else { + console.log(first, ...rest); + } + }; + } + return methodFactory; +}