diff --git a/cypress/support/bot.ts b/cypress/support/bot.ts index 2799927916..770a89e818 100644 --- a/cypress/support/bot.ts +++ b/cypress/support/bot.ts @@ -20,6 +20,7 @@ import type { ISendEventResponse, MatrixClient, Room } from "matrix-js-sdk/src/m import { HomeserverInstance } from "../plugins/utils/homeserver"; import { Credentials } from "./homeserver"; import Chainable = Cypress.Chainable; +import { collapseLastLogGroup } from "./log"; interface CreateBotOpts { /** @@ -65,6 +66,7 @@ declare global { * @param opts create bot options */ getBot(homeserver: HomeserverInstance, opts: CreateBotOpts): Chainable; + /** * Returns a new Bot instance logged in as an existing user * @param homeserver the instance on which to register the bot user @@ -78,18 +80,21 @@ declare global { password: string, opts: CreateBotOpts, ): Chainable; + /** * Let a bot join a room * @param cli The bot's MatrixClient * @param roomId ID of the room to join */ botJoinRoom(cli: MatrixClient, roomId: string): Chainable; + /** * Let a bot join a room by name * @param cli The bot's MatrixClient * @param roomName Name of the room to join */ botJoinRoomByName(cli: MatrixClient, roomName: string): Chainable; + /** * Send a message as a bot into a room * @param cli The bot's MatrixClient @@ -173,15 +178,21 @@ Cypress.Commands.add("getBot", (homeserver: HomeserverInstance, opts: CreateBotO opts = Object.assign({}, defaultCreateBotOptions, opts); const username = Cypress._.uniqueId(opts.userIdPrefix); const password = Cypress._.uniqueId("password_"); + Cypress.log({ + name: "getBot", + message: `Create bot user ${username} with opts ${JSON.stringify(opts)}`, + groupStart: true, + }); return cy .registerUser(homeserver, username, password, opts.displayName) .then((credentials) => { - cy.log(`Registered bot user ${username} with displayname ${opts.displayName}`); return setupBotClient(homeserver, credentials, opts); }) .then((client): Chainable => { Object.assign(client, { __cypress_password: password }); - return cy.wrap(client as CypressBot); + Cypress.log({ groupEnd: true, emitOnly: true }); + collapseLastLogGroup(); + return cy.wrap(client as CypressBot, { log: false }); }); }); @@ -194,9 +205,21 @@ Cypress.Commands.add( opts: CreateBotOpts, ): Chainable => { opts = Object.assign({}, defaultCreateBotOptions, { bootstrapCrossSigning: false }, opts); - return cy.loginUser(homeserver, username, password).then((credentials) => { - return setupBotClient(homeserver, credentials, opts); + Cypress.log({ + name: "loginBot", + message: `log in as ${username} with opts ${JSON.stringify(opts)}`, + groupStart: true, }); + return cy + .loginUser(homeserver, username, password) + .then((credentials) => { + return setupBotClient(homeserver, credentials, opts); + }) + .then((res) => { + Cypress.log({ groupEnd: true, emitOnly: true }); + collapseLastLogGroup(); + cy.wrap(res, { log: false }); + }); }, ); diff --git a/cypress/support/log.ts b/cypress/support/log.ts new file mode 100644 index 0000000000..b8d0449da5 --- /dev/null +++ b/cypress/support/log.ts @@ -0,0 +1,45 @@ +/* +Copyright 2023 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/// + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Cypress { + // secret undocumented options to Cypress.log + interface LogConfig { + /** begin a new log group; remember to match with `groupEnd` */ + groupStart: boolean; + + /** end a log group that was previously started with `groupStart` */ + groupEnd: boolean; + + /** suppress regular output: useful for closing a log group without writing another log line */ + emitOnly: boolean; + } + } +} + +/** collapse the last open log group in the Cypress UI + * + * Credit to https://j1000.github.io/blog/2022/10/27/enhanced_cypress_logging.html + */ +export function collapseLastLogGroup() { + const openExpanders = window.top.document.getElementsByClassName("command-expander-is-open"); + const numExpanders = openExpanders.length; + const el = openExpanders[numExpanders - 1]; + if (el) el.parentElement.click(); +} diff --git a/cypress/support/login.ts b/cypress/support/login.ts index 308c1ea639..b830c23f1e 100644 --- a/cypress/support/login.ts +++ b/cypress/support/login.ts @@ -18,6 +18,7 @@ limitations under the License. import Chainable = Cypress.Chainable; import { HomeserverInstance } from "../plugins/utils/homeserver"; +import { collapseLastLogGroup } from "./log"; export interface UserCredentials { accessToken: string; @@ -100,6 +101,7 @@ Cypress.Commands.add( prelaunchFn?: () => void, userIdPrefix = "user_", ): Chainable => { + Cypress.log({ name: "initTestUser", groupStart: true }); // XXX: work around Cypress not clearing IDB between tests cy.window({ log: false }).then((win) => { win.indexedDB.databases()?.then((databases) => { @@ -140,6 +142,13 @@ Cypress.Commands.add( // wait for the app to load return cy.get(".mx_MatrixChat", { timeout: 30000 }); }) + .then(() => { + Cypress.log({ + groupEnd: true, + emitOnly: true, + }); + collapseLastLogGroup(); + }) .then(() => ({ password, username,