Migrate remaining editing.spec.ts from Cypress to Playwright (#11976)

* Migrate user-view.spec.ts from Cypress to Playwright

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add bot support & update screenshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add screenshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Use JSHandle

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Remove stale snapshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Migrate remaining editing.spec.ts from Cypress to Playwright

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* yay

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix tests

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix tests

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2023-11-30 10:18:18 +00:00 committed by GitHub
parent 07b7ee6111
commit 79daa1a63c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 262 additions and 248 deletions

View file

@ -16,13 +16,14 @@ limitations under the License.
import { type Locator, type Page } from "@playwright/test";
import type { IContent, ICreateRoomOpts, ISendEventResponse } from "matrix-js-sdk/src/matrix";
import { Settings } from "./settings";
import { Client } from "./client";
export class ElementAppPage {
public constructor(private readonly page: Page) {}
public settings = new Settings(this.page);
public client: Client = new Client(this.page);
/**
* Open the top left user menu, returning a Locator to the resulting context menu.
@ -47,20 +48,6 @@ export class ElementAppPage {
return this.settings.closeDialog();
}
/**
* Create a room with given options.
* @param options the options to apply when creating the room
* @return the ID of the newly created room
*/
public async createRoom(options: ICreateRoomOpts): Promise<string> {
return this.page.evaluate<Promise<string>, ICreateRoomOpts>(async (options) => {
return window.mxMatrixClientPeg
.get()
.createRoom(options)
.then((res) => res.room_id);
}, options);
}
/**
* Opens the given room by name. The room must be visible in the
* room list, but the room list may be folded horizontally, and the
@ -107,32 +94,4 @@ export class ElementAppPage {
await composer.getByRole("button", { name: "More options", exact: true }).click();
return this.page.getByRole("menu");
}
/**
* @param {string} roomId
* @param {string} threadId
* @param {string} eventType
* @param {Object} content
*/
public async sendEvent(
roomId: string,
threadId: string | null,
eventType: string,
content: IContent,
): Promise<ISendEventResponse> {
return this.page.evaluate<
Promise<ISendEventResponse>,
{
roomId: string;
threadId: string | null;
eventType: string;
content: IContent;
}
>(
async ({ roomId, threadId, eventType, content }) => {
return window.mxMatrixClientPeg.get().sendEvent(roomId, threadId, eventType, content);
},
{ roomId, threadId, eventType, content },
);
}
}

View file

@ -17,9 +17,10 @@ limitations under the License.
import { JSHandle, Page } from "@playwright/test";
import { uniqueId } from "lodash";
import type { MatrixClient, ISendEventResponse } from "matrix-js-sdk/src/matrix";
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
import type { AddSecretStorageKeyOpts } from "matrix-js-sdk/src/secret-storage";
import type { Credentials, HomeserverInstance } from "../plugins/homeserver";
import { Client } from "./client";
export interface CreateBotOpts {
/**
@ -59,27 +60,24 @@ const defaultCreateBotOptions = {
bootstrapCrossSigning: true,
} satisfies CreateBotOpts;
export class Bot {
private client: JSHandle<MatrixClient>;
export class Bot extends Client {
public credentials?: Credentials;
constructor(private page: Page, private homeserver: HomeserverInstance, private readonly opts: CreateBotOpts) {
constructor(page: Page, private homeserver: HomeserverInstance, private readonly opts: CreateBotOpts) {
super(page);
this.opts = Object.assign({}, defaultCreateBotOptions, opts);
}
public async start(): Promise<void> {
this.credentials = await this.getCredentials();
this.client = await this.setupBotClient();
}
private async getCredentials(): Promise<Credentials> {
if (this.credentials) return this.credentials;
const username = uniqueId(this.opts.userIdPrefix);
const password = uniqueId("password_");
console.log(`getBot: Create bot user ${username} with opts ${JSON.stringify(this.opts)}`);
return await this.homeserver.registerUser(username, password, this.opts.displayName);
this.credentials = await this.homeserver.registerUser(username, password, this.opts.displayName);
return this.credentials;
}
private async setupBotClient(): Promise<JSHandle<MatrixClient>> {
protected async getClientHandle(): Promise<JSHandle<MatrixClient>> {
return this.page.evaluateHandle(
async ({ homeserver, credentials, opts }) => {
const keys = {};
@ -123,7 +121,7 @@ export class Bot {
});
if (opts.autoAcceptInvites) {
cli.on((window as any).matrixcs.RoomMemberEvent.Membership, (event, member) => {
cli.on(window.matrixcs.RoomMemberEvent.Membership, (event, member) => {
if (member.membership === "invite" && member.userId === cli.getUserId()) {
cli.joinRoom(member.roomId);
}
@ -173,48 +171,9 @@ export class Bot {
},
{
homeserver: this.homeserver.config,
credentials: this.credentials,
credentials: await this.getCredentials(),
opts: this.opts,
},
);
}
/**
* Make this bot join a room by name
* @param roomName Name of the room to join
*/
public async joinRoomByName(roomName: string): Promise<void> {
await this.client.evaluate(
(client, { roomName }) => {
const room = client.getRooms().find((r) => r.getDefaultRoomName(client.getUserId()) === roomName);
if (room) {
return client.joinRoom(room.roomId);
}
throw new Error(`Bot room join failed. Cannot find room '${roomName}'`);
},
{
roomName,
},
);
}
/**
* Send a message as a bot into a room
* @param roomId ID of the room to join
* @param message the message body to send
*/
public async sendStringMessage(roomId: string, message: string): Promise<ISendEventResponse> {
return this.client.evaluate(
(client, { roomId, message }) => {
return client.sendMessage(roomId, {
msgtype: "m.text",
body: message,
});
},
{
roomId,
message,
},
);
}
}

149
playwright/pages/client.ts Normal file
View file

@ -0,0 +1,149 @@
/*
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.
*/
import { JSHandle, Page } from "@playwright/test";
import { PageFunctionOn } from "playwright-core/types/structs";
import type { IContent, ICreateRoomOpts, ISendEventResponse, MatrixClient, Room } from "matrix-js-sdk/src/matrix";
export class Client {
protected client: JSHandle<MatrixClient>;
protected getClientHandle(): Promise<JSHandle<MatrixClient>> {
return this.page.evaluateHandle(() => window.mxMatrixClientPeg.get());
}
public async prepareClient(): Promise<JSHandle<MatrixClient>> {
if (!this.client) {
this.client = await this.getClientHandle();
}
return this.client;
}
public constructor(protected readonly page: Page) {
page.on("framenavigated", async () => {
this.client = null;
});
}
public evaluate<R, Arg, O extends MatrixClient = MatrixClient>(
pageFunction: PageFunctionOn<O, Arg, R>,
arg: Arg,
): Promise<R>;
public evaluate<R, O extends MatrixClient = MatrixClient>(
pageFunction: PageFunctionOn<O, void, R>,
arg?: any,
): Promise<R>;
public async evaluate<T>(fn: (client: MatrixClient) => T, arg?: any): Promise<T> {
await this.prepareClient();
return this.client.evaluate(fn, arg);
}
/**
* @param roomId ID of the room to send the event into
* @param threadId ID of the thread to send into or null for main timeline
* @param eventType type of event to send
* @param content the event content to send
*/
public async sendEvent(
roomId: string,
threadId: string | null,
eventType: string,
content: IContent,
): Promise<ISendEventResponse> {
const client = await this.prepareClient();
return client.evaluate(
async (client, { roomId, threadId, eventType, content }) => {
return client.sendEvent(roomId, threadId, eventType, content);
},
{ roomId, threadId, eventType, content },
);
}
/**
* Send a message as a bot into a room
* @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> {
const client = await this.prepareClient();
return client.evaluate(
(client, { roomId, content }) => {
return client.sendMessage(roomId, content);
},
{
roomId,
content,
},
);
}
/**
* Create a room with given options.
* @param options the options to apply when creating the room
* @return the ID of the newly created room
*/
public async createRoom(options: ICreateRoomOpts): Promise<string> {
const client = await this.prepareClient();
return await client.evaluate(async (cli, options) => {
const resp = await cli.createRoom(options);
const roomId = resp.room_id;
if (!cli.getRoom(roomId)) {
await new Promise<void>((resolve) => {
const onRoom = (room: Room) => {
if (room.roomId === roomId) {
cli.off(window.matrixcs.ClientEvent.Room, onRoom);
resolve();
}
};
cli.on(window.matrixcs.ClientEvent.Room, onRoom);
});
}
return roomId;
}, options);
}
/**
* Joins the given room by alias or ID
* @param roomIdOrAlias the id or alias of the room to join
*/
public async joinRoom(roomIdOrAlias: string): Promise<void> {
const client = await this.prepareClient();
await client.evaluate(async (client, roomIdOrAlias) => {
return await client.joinRoom(roomIdOrAlias);
}, roomIdOrAlias);
}
/**
* Make this bot join a room by name
* @param roomName Name of the room to join
*/
public async joinRoomByName(roomName: string): Promise<void> {
const client = await this.prepareClient();
await client.evaluate(
(client, { roomName }) => {
const room = client.getRooms().find((r) => r.getDefaultRoomName(client.getUserId()) === roomName);
if (room) {
return client.joinRoom(room.roomId);
}
throw new Error(`Bot room join failed. Cannot find room '${roomName}'`);
},
{
roomName,
},
);
}
}