Migrate remaining crypto tests from Cypress to Playwright (#12021)
* Fix bot MatrixClient being set up multiple times Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Migrate verification.spec.ts from Cypress to Playwright Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Migrate crypto.spec.ts from Cypress to Playwright Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Add screenshot Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Record trace on-first-retry Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Don't start client when not needed Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Add bot log prefixing Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Turns out we need these Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix crypto tests in rust crypto Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
0f42418b5c
commit
5104d53ddf
13 changed files with 1111 additions and 1259 deletions
|
@ -21,7 +21,7 @@ import { Client } from "./client";
|
|||
import { Labs } from "./labs";
|
||||
|
||||
export class ElementAppPage {
|
||||
public constructor(private readonly page: Page) {}
|
||||
public constructor(public readonly page: Page) {}
|
||||
|
||||
public labs = new Labs(this.page);
|
||||
public settings = new Settings(this.page);
|
||||
|
@ -91,6 +91,10 @@ export class ElementAppPage {
|
|||
.click();
|
||||
}
|
||||
|
||||
public async viewRoomById(roomId: string): Promise<void> {
|
||||
await this.page.goto(`/#/room/${roomId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the composer element
|
||||
* @param isRightPanel whether to select the right panel composer, otherwise the main timeline composer
|
||||
|
|
|
@ -18,8 +18,10 @@ import { JSHandle, Page } from "@playwright/test";
|
|||
import { uniqueId } from "lodash";
|
||||
|
||||
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import type { Logger } from "matrix-js-sdk/src/logger";
|
||||
import type { AddSecretStorageKeyOpts } from "matrix-js-sdk/src/secret-storage";
|
||||
import type { Credentials, HomeserverInstance } from "../plugins/homeserver";
|
||||
import type { GeneratedSecretStorageKey } from "matrix-js-sdk/src/crypto-api";
|
||||
import { Client } from "./client";
|
||||
|
||||
export interface CreateBotOpts {
|
||||
|
@ -60,14 +62,27 @@ const defaultCreateBotOptions = {
|
|||
bootstrapCrossSigning: true,
|
||||
} satisfies CreateBotOpts;
|
||||
|
||||
type ExtendedMatrixClient = MatrixClient & { __playwright_recovery_key: GeneratedSecretStorageKey };
|
||||
|
||||
export class Bot extends Client {
|
||||
public credentials?: Credentials;
|
||||
private handlePromise: Promise<JSHandle<ExtendedMatrixClient>>;
|
||||
|
||||
constructor(page: Page, private homeserver: HomeserverInstance, private readonly opts: CreateBotOpts) {
|
||||
super(page);
|
||||
this.opts = Object.assign({}, defaultCreateBotOptions, opts);
|
||||
}
|
||||
|
||||
public setCredentials(credentials: Credentials): void {
|
||||
if (this.credentials) throw new Error("Bot has already started");
|
||||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
public async getRecoveryKey(): Promise<GeneratedSecretStorageKey> {
|
||||
const client = await this.getClientHandle();
|
||||
return client.evaluate((cli) => cli.__playwright_recovery_key);
|
||||
}
|
||||
|
||||
private async getCredentials(): Promise<Credentials> {
|
||||
if (this.credentials) return this.credentials;
|
||||
// We want to pad the uniqueId but not the prefix
|
||||
|
@ -82,9 +97,36 @@ export class Bot extends Client {
|
|||
return this.credentials;
|
||||
}
|
||||
|
||||
protected async getClientHandle(): Promise<JSHandle<MatrixClient>> {
|
||||
return this.page.evaluateHandle(
|
||||
protected async getClientHandle(): Promise<JSHandle<ExtendedMatrixClient>> {
|
||||
if (this.handlePromise) return this.handlePromise;
|
||||
|
||||
this.handlePromise = this.page.evaluateHandle(
|
||||
async ({ homeserver, credentials, opts }) => {
|
||||
function getLogger(loggerName: string): Logger {
|
||||
const logger = {
|
||||
getChild: (namespace: string) => getLogger(`${loggerName}:${namespace}`),
|
||||
trace(...msg: any[]): void {
|
||||
console.trace(loggerName, ...msg);
|
||||
},
|
||||
debug(...msg: any[]): void {
|
||||
console.debug(loggerName, ...msg);
|
||||
},
|
||||
info(...msg: any[]): void {
|
||||
console.info(loggerName, ...msg);
|
||||
},
|
||||
warn(...msg: any[]): void {
|
||||
console.warn(loggerName, ...msg);
|
||||
},
|
||||
error(...msg: any[]): void {
|
||||
console.error(loggerName, ...msg);
|
||||
},
|
||||
} satisfies Logger;
|
||||
|
||||
return logger as unknown as Logger;
|
||||
}
|
||||
|
||||
const logger = getLogger(`cypress bot ${credentials.userId}`);
|
||||
|
||||
const keys = {};
|
||||
|
||||
const getCrossSigningKey = (type: string) => {
|
||||
|
@ -123,7 +165,8 @@ export class Bot extends Client {
|
|||
scheduler: new window.matrixcs.MatrixScheduler(),
|
||||
cryptoStore: new window.matrixcs.MemoryCryptoStore(),
|
||||
cryptoCallbacks,
|
||||
});
|
||||
logger,
|
||||
}) as ExtendedMatrixClient;
|
||||
|
||||
if (opts.autoAcceptInvites) {
|
||||
cli.on(window.matrixcs.RoomMemberEvent.Membership, (event, member) => {
|
||||
|
@ -180,5 +223,6 @@ export class Bot extends Client {
|
|||
opts: this.opts,
|
||||
},
|
||||
);
|
||||
return this.handlePromise;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import type {
|
|||
ReceiptType,
|
||||
IRoomDirectoryOptions,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { Credentials } from "../plugins/homeserver";
|
||||
|
||||
export class Client {
|
||||
protected client: JSHandle<MatrixClient>;
|
||||
|
@ -100,7 +101,14 @@ export class Client {
|
|||
* @param roomId ID of the room to send the message into
|
||||
* @param content the event content to send
|
||||
*/
|
||||
public async sendMessage(roomId: string, content: IContent): Promise<ISendEventResponse> {
|
||||
public async sendMessage(roomId: string, content: IContent | string): Promise<ISendEventResponse> {
|
||||
if (typeof content === "string") {
|
||||
content = {
|
||||
body: content,
|
||||
msgtype: "m.text",
|
||||
};
|
||||
}
|
||||
|
||||
const client = await this.prepareClient();
|
||||
return client.evaluate(
|
||||
(client, { roomId, content }) => {
|
||||
|
@ -177,13 +185,14 @@ export class Client {
|
|||
* Make this bot join a room by name
|
||||
* @param roomName Name of the room to join
|
||||
*/
|
||||
public async joinRoomByName(roomName: string): Promise<void> {
|
||||
public async joinRoomByName(roomName: string): Promise<string> {
|
||||
const client = await this.prepareClient();
|
||||
await client.evaluate(
|
||||
(client, { roomName }) => {
|
||||
return client.evaluate(
|
||||
async (client, { roomName }) => {
|
||||
const room = client.getRooms().find((r) => r.getDefaultRoomName(client.getUserId()) === roomName);
|
||||
if (room) {
|
||||
return client.joinRoom(room.roomId);
|
||||
await client.joinRoom(room.roomId);
|
||||
return room.roomId;
|
||||
}
|
||||
throw new Error(`Bot room join failed. Cannot find room '${roomName}'`);
|
||||
},
|
||||
|
@ -227,8 +236,29 @@ export class Client {
|
|||
|
||||
public async publicRooms(options?: IRoomDirectoryOptions): ReturnType<MatrixClient["publicRooms"]> {
|
||||
const client = await this.prepareClient();
|
||||
return await client.evaluate((client, options) => {
|
||||
return client.evaluate((client, options) => {
|
||||
return client.publicRooms(options);
|
||||
}, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Boostraps cross-signing.
|
||||
*/
|
||||
public async bootstrapCrossSigning(credentials: Credentials): Promise<void> {
|
||||
const client = await this.prepareClient();
|
||||
return client.evaluate(async (client, credentials) => {
|
||||
await client.getCrypto().bootstrapCrossSigning({
|
||||
authUploadDeviceSigningKeys: async (func) => {
|
||||
await func({
|
||||
type: "m.login.password",
|
||||
identifier: {
|
||||
type: "m.id.user",
|
||||
user: credentials.userId,
|
||||
},
|
||||
password: credentials.password,
|
||||
});
|
||||
},
|
||||
});
|
||||
}, credentials);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue