Make apps/web/playwright comply with strict mode (#34403)

* Make apps/web/playwright comply with strict mode

Otherwise it sometimes fails to compile matrix-js-sdk as strict mode & noImplicitAny both impact how overloads are asserted

* Iterate
This commit is contained in:
Michael Telatynski
2026-07-30 09:07:03 +00:00
committed by GitHub
parent 4a18eac927
commit 42253a7ae5
84 changed files with 413 additions and 388 deletions
@@ -65,7 +65,7 @@ test.describe("Landmark navigation tests", () => {
await cli.invite(bobRoom.room_id, bob);
},
{
bob: bob.credentials.userId,
bob: bob.credentials!.userId,
},
);
@@ -118,7 +118,7 @@ test.describe("Landmark navigation tests", () => {
await cli.invite(bobRoom.room_id, bob);
},
{
bob: bob.credentials.userId,
bob: bob.credentials!.userId,
},
);
@@ -313,7 +313,9 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
await expect(tile.locator(".mx_ReplyChain")).toHaveCount(2);
// Assert that one line contains the user name
await expect(tile.locator(".mx_ReplyChain .mx_ReplyTile_sender").getByText(user.displayName)).toBeVisible();
await expect(
tile.locator(".mx_ReplyChain .mx_ReplyTile_sender").getByText(user.displayName!),
).toBeVisible();
// Assert that the other line contains the file button
await expect(tile.locator(".mx_ReplyChain .mx_MFileBody")).toBeVisible();
@@ -99,9 +99,12 @@ test.describe("HTML Export", () => {
// Send a bunch of messages to populate the room
for (let i = 1; i < 10; i++) {
const respone = await app.client.sendMessage(room.roomId, { body: `Testing ${i}`, msgtype: "m.text" });
const response = await app.client.sendMessage(room!.roomId, {
body: `Testing ${i}`,
msgtype: "m.text",
});
if (i == 1) {
await app.client.reactToMessage(room.roomId, null, respone.event_id, "🙃");
await app.client.reactToMessage(room!.roomId, null, response.event_id, "🙃");
}
}
@@ -184,7 +184,7 @@ test.describe("Composer", () => {
// Set up a private room so we have another user to mention
await app.client.createRoom({
is_direct: true,
invite: [bot.credentials.userId],
invite: [bot.credentials!.userId],
});
await app.viewRoomByName("Bob");
@@ -194,7 +194,7 @@ test.describe("Composer", () => {
// Note that we include the user ID here as the room tile is also an 'option' role
// with text 'Bob'
await page.getByRole("option", { name: `Bob ${bot.credentials.userId}` }).click();
await page.getByRole("option", { name: `Bob ${bot.credentials!.userId}` }).click();
await expect(composer.getByText("Bob")).toBeVisible();
await expect(composer).toMatchScreenshot("mention.png");
await composer.press("Enter");
+7 -7
View File
@@ -100,7 +100,7 @@ test.describe("Composer", () => {
// Set up a private room so we have another user to mention
await app.client.createRoom({
is_direct: true,
invite: [bob.credentials.userId],
invite: [bob.credentials!.userId],
});
await app.viewRoomByName("Bob");
@@ -113,11 +113,11 @@ test.describe("Composer", () => {
await expect(page.getByTestId("autocomplete-wrapper")).toBeEmpty();
// Entering the first letter of the other user's name opens the autocomplete...
await page.getByRole("textbox").pressSequentially(bob.credentials.displayName.slice(0, 1));
await page.getByRole("textbox").pressSequentially(bob.credentials!.displayName!.slice(0, 1));
// ...with the other user name visible, and clicking that username...
await page.getByTestId("autocomplete-wrapper").getByText(bob.credentials.displayName).click();
await page.getByTestId("autocomplete-wrapper").getByText(bob.credentials!.displayName!).click();
// ...inserts the username into the composer
const pill = page.getByRole("textbox").getByText(bob.credentials.displayName, { exact: false });
const pill = page.getByRole("textbox").getByText(bob.credentials!.displayName!, { exact: false });
await expect(pill).toHaveAttribute("contenteditable", "false");
await expect(pill).toHaveAttribute("data-mention-type", "user");
@@ -125,7 +125,7 @@ test.describe("Composer", () => {
await page.getByRole("button", { name: "Send message" }).click();
// Typing an @, then other user's name, then trailing space closes the autocomplete
await page.getByRole("textbox").pressSequentially(`@${bob.credentials.displayName} `);
await page.getByRole("textbox").pressSequentially(`@${bob.credentials!.displayName!} `);
await expect(page.getByTestId("autocomplete-wrapper")).toBeEmpty();
// Send the message to clear the composer
@@ -134,7 +134,7 @@ test.describe("Composer", () => {
// Moving the cursor back to an "incomplete" mention opens the autocomplete
await page
.getByRole("textbox")
.pressSequentially(`initial text @${bob.credentials.displayName.slice(0, 1)} abc`);
.pressSequentially(`initial text @${bob.credentials!.displayName!.slice(0, 1)} abc`);
await expect(page.getByTestId("autocomplete-wrapper")).toBeEmpty();
// Move the cursor left by 4 to put it to: `@B| abc`, check autocomplete displays
await page.getByRole("textbox").press("ArrowLeft");
@@ -145,7 +145,7 @@ test.describe("Composer", () => {
// Selecting the autocomplete option using Enter inserts it into the composer
await page.getByRole("textbox").press("Enter");
const pill2 = page.getByRole("textbox").getByText(bob.credentials.displayName, { exact: false });
const pill2 = page.getByRole("textbox").getByText(bob.credentials!.displayName!, { exact: false });
await expect(pill2).toHaveAttribute("contenteditable", "false");
await expect(pill2).toHaveAttribute("data-mention-type", "user");
});
@@ -89,13 +89,13 @@ test.describe("Key backup reset from elsewhere", () => {
await page.getByRole("textbox", { name: "Name" }).fill("test room");
await page.getByRole("button", { name: "Create room" }).click();
const accessToken = await page.evaluate(() => window.mxMatrixClientPeg.get().getAccessToken());
const accessToken = await page.evaluate(() => window.mxMatrixClientPeg.get().getAccessToken()!);
const csAPI = new TestClientServerAPI(request, homeserver, accessToken);
const backupInfo = await csAPI.getCurrentBackupInfo();
await csAPI.deleteBackupVersion(backupInfo.version);
await csAPI.deleteBackupVersion(backupInfo!.version);
await page.getByRole("textbox", { name: "Send a message…" }).fill("/discardsession");
await page.getByRole("button", { name: "Send message" }).click();
+11 -11
View File
@@ -25,8 +25,8 @@ const checkDMRoom = async (page: Page) => {
const startDMWithBob = async (page: Page, bob: Bot) => {
await page.getByRole("navigation", { name: "Room list" }).getByRole("button", { name: "New conversation" }).click();
await page.getByRole("menuitem", { name: "Start chat" }).click();
await page.getByTestId("invite-dialog-input").fill(bob.credentials.userId);
await page.getByRole("option", { name: bob.credentials.displayName }).click();
await page.getByTestId("invite-dialog-input").fill(bob.credentials!.userId);
await page.getByRole("option", { name: bob.credentials!.displayName! }).click();
await expect(page.getByTestId("invite-dialog-input-wrapper").getByText("Bob")).toBeVisible();
await page.getByRole("button", { name: "Go" }).click();
@@ -82,14 +82,14 @@ test.describe("Cryptography", function () {
* @param keyType
*/
async function verifyKey(app: ElementAppPage, keyType: "master" | "self_signing" | "user_signing") {
const accountData: { encrypted: Record<string, Record<string, string>> } = await app.client.evaluate(
const accountData = await app.client.evaluate(
(cli, keyType) => cli.getAccountDataFromServer(`m.cross_signing.${keyType}`),
keyType,
);
expect(accountData.encrypted).toBeDefined();
const keys = Object.keys(accountData.encrypted);
const key = accountData.encrypted[keys[0]];
expect(accountData?.encrypted).toBeDefined();
const keys = Object.keys(accountData!.encrypted);
const key = accountData!.encrypted[keys[0]];
expect(key.ciphertext).toBeDefined();
expect(key.iv).toBeDefined();
expect(key.mac).toBeDefined();
@@ -119,9 +119,9 @@ test.describe("Cryptography", function () {
async function fetchMasterKey() {
return await test.step("Fetch master key from server", async () => {
const k = await app.client.evaluate(async (cli) => {
const userId = cli.getUserId();
const userId = cli.getSafeUserId();
const keys = await cli.downloadKeysForUsers([userId]);
return Object.values(keys.master_keys[userId].keys)[0];
return Object.values(keys.master_keys![userId].keys)[0];
});
console.log(`fetchMasterKey: ${k}`);
return k;
@@ -138,7 +138,7 @@ test.describe("Cryptography", function () {
await encryptionTab.getByRole("button", { name: "Continue" }).click();
// Enter the password
await page.getByPlaceholder("Password").fill(aliceCredentials.password);
await page.getByPlaceholder("Password").fill(aliceCredentials.password!);
await page.getByRole("button", { name: "Continue" }).click();
await expect(async () => {
@@ -163,7 +163,7 @@ test.describe("Cryptography", function () {
await encryptionTab.getByRole("button", { name: "Continue" }).click();
// Enter the password
await page.getByPlaceholder("Password").fill(aliceCredentials.password);
await page.getByPlaceholder("Password").fill(aliceCredentials.password!);
await page.getByRole("button", { name: "Continue" }).click();
// Key storage should now be enabled
@@ -209,7 +209,7 @@ test.describe("Cryptography", function () {
await autoJoin(bob);
// we need to have a room with the other user present, so we can open the verification panel
await createSharedRoomWithUser(app, bob.credentials.userId);
await createSharedRoomWithUser(app, bob.credentials!.userId);
await verify(app, bob);
});
});
@@ -46,7 +46,7 @@ test.describe("Device dehydration, on a MAS-enabled homeserver", () => {
await autoJoin(bob);
// Create an encrypted room, and wait for Bob to join it.
const testRoomId = await createSharedEncryptedRoomWithUser(app, bob.credentials.userId);
const testRoomId = await createSharedEncryptedRoomWithUser(app, bob.credentials!.userId);
// Even though Alice has seen Bob's join event, Bob may not have done so yet. Wait for the sync to arrive.
await bob.awaitRoomMembership(testRoomId);
@@ -77,7 +77,7 @@ test.describe("Dehydration", () => {
// Set up cross-signing and recovery
const { botClient } = await createBot(page, homeserver, credentials);
// ... and dehydration
await botClient.evaluate(async (client) => await client.getCrypto().startDehydration());
await botClient.evaluate(async (client) => await client.getCrypto()!.startDehydration());
const initialDehydratedDeviceIds = await getDehydratedDeviceIds(botClient);
expect(initialDehydratedDeviceIds.length).toBe(1);
@@ -93,7 +93,7 @@ test.describe("Dehydration", () => {
page.getByRole("heading", { name: "Are you sure you want to reset your digital identity?" }),
).toBeVisible();
await page.getByRole("button", { name: "Continue", exact: true }).click();
await page.getByPlaceholder("Password").fill(credentials.password);
await page.getByPlaceholder("Password").fill(credentials.password!);
await page.getByRole("button", { name: "Continue" }).click();
// And set up recovery
@@ -141,7 +141,7 @@ test.describe("Dehydration", () => {
await autoJoin(bob);
// create an encrypted room, and wait for Bob to join it.
const testRoomId = await createSharedEncryptedRoomWithUser(app, bob.credentials.userId);
const testRoomId = await createSharedEncryptedRoomWithUser(app, bob.credentials!.userId);
// Even though Alice has seen Bob's join event, Bob may not have done so yet. Wait for the sync to arrive.
await bob.awaitRoomMembership(testRoomId);
@@ -171,9 +171,9 @@ test.describe("Dehydration", () => {
async function getDehydratedDeviceIds(client: Client): Promise<string[]> {
return await client.evaluate(async (client) => {
const userId = client.getUserId();
const devices = await client.getCrypto().getUserDeviceInfo([userId]);
return Array.from(devices.get(userId).values())
const userId = client.getSafeUserId();
const devices = await client.getCrypto()!.getUserDeviceInfo([userId]);
return Array.from(devices.get(userId)!.values())
.filter((d) => d.dehydrated)
.map((d) => d.deviceId);
});
@@ -169,8 +169,8 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => {
await app.client.evaluate(async (cli, aliceBotCredentials) => {
const deviceStatus = await cli
.getCrypto()!
.getDeviceVerificationStatus(aliceBotCredentials.userId, aliceBotCredentials.deviceId);
if (!deviceStatus.isVerified()) {
.getDeviceVerificationStatus(aliceBotCredentials!.userId, aliceBotCredentials!.deviceId);
if (!deviceStatus!.isVerified()) {
throw new Error("Bot device was not verified after QR code verification");
}
}, aliceBotClient.credentials);
@@ -193,14 +193,14 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => {
);
test("Verify device with Recovery Key during login", async ({ page, app, credentials, homeserver }) => {
const recoveryKey = (await aliceBotClient.getRecoveryKey()).encodedPrivateKey;
const recoveryKey = (await aliceBotClient.getRecoveryKey()).encodedPrivateKey!;
await logIntoElement(page, credentials);
await enterRecoveryKeyAndCheckVerified(page, app, recoveryKey);
});
test("Verify device with Recovery Key from settings", async ({ page, app, credentials }) => {
const recoveryKey = (await aliceBotClient.getRecoveryKey()).encodedPrivateKey;
const recoveryKey = (await aliceBotClient.getRecoveryKey()).encodedPrivateKey!;
await logIntoElement(page, credentials);
@@ -277,7 +277,7 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => {
await authPage.getByRole("button", { name: "I'll verify later" }).click();
await page.waitForSelector(".mx_MatrixChat");
const elementDeviceId = await page.evaluate(() => window.mxMatrixClientPeg.get().getDeviceId());
const elementDeviceId = await page.evaluate(() => window.mxMatrixClientPeg.get().getDeviceId()!);
/* Create an encrypted room so the "Verify this device" toast appears */
await app.client.createRoom({
@@ -301,7 +301,7 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => {
/* Check the toast for the incoming request */
const toast = await getToast(page, "Verification requested");
// it should contain the device ID of the requesting device
await expect(toast.getByText(`${aliceBotClient.credentials.deviceId} from `)).toBeVisible();
await expect(toast.getByText(`${aliceBotClient.credentials!.deviceId} from `)).toBeVisible();
// Accept
await toast.getByRole("button", { name: "Start verification" }).click();
@@ -311,7 +311,7 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => {
/* on the bot side, wait for the verifier to exist ... */
const verifier = await awaitVerifier(botVerificationRequest);
// ... confirm ...
void botVerificationRequest.evaluate((verificationRequest) => verificationRequest.verifier.verify());
void botVerificationRequest.evaluate((verificationRequest) => verificationRequest.verifier!.verify());
// ... and then check the emoji match
await doTwoWaySasVerification(page, verifier);
@@ -337,7 +337,7 @@ async function readQrCode(base: Locator) {
>(async (img) => {
// draw the image on a canvas
const myCanvas = new OffscreenCanvas(img.width, img.height);
const ctx = myCanvas.getContext("2d");
const ctx = myCanvas.getContext("2d")!;
ctx.drawImage(img, 0, 0);
// read the image data
@@ -352,5 +352,5 @@ async function readQrCode(base: Locator) {
// now we can decode the QR code.
const result = jsQR(new Uint8ClampedArray(imageData.buffer), imageData.width, imageData.height);
return new Uint8Array(result.binaryData);
return new Uint8Array(result!.binaryData);
}
@@ -40,7 +40,7 @@ test.describe("Cryptography", function () {
await autoJoin(bob);
// create an encrypted room, and wait for Bob to join it.
testRoomId = await createSharedEncryptedRoomWithUser(app, bob.credentials.userId);
testRoomId = await createSharedEncryptedRoomWithUser(app, bob.credentials!.userId);
// Even though Alice has seen Bob's join event, Bob may not have done so yet. Wait for the sync to arrive.
await bob.awaitRoomMembership(testRoomId);
@@ -128,7 +128,7 @@ test.describe("Cryptography", function () {
await bobSecondDevice.evaluate((cli) => cli.logout(true));
// wait for the logout to propagate.
await waitForDevices(app, bob.credentials.userId, 1);
await waitForDevices(app, bob.credentials!.userId, 1);
// close and reopen the room, to get the shield to update.
await app.viewRoomByName("Bob");
@@ -245,7 +245,7 @@ test.describe("Cryptography", function () {
// Workaround for https://github.com/element-hq/element-web/issues/28640:
// make sure that Alice has seen Bob's identity before she goes offline. We do this by opening
// his user info.
await waitForDevices(app, bob.credentials.userId, 1);
await waitForDevices(app, bob.credentials!.userId, 1);
// Our app is blocked from syncing while Bob sends his messages.
await app.client.network.goOffline();
@@ -286,7 +286,7 @@ test.describe("Cryptography", function () {
// Bob logs in a new device and resets cross-signing
const bobSecondDevice = await createSecondBotDevice(page, homeserver, bob);
await bootstrapCrossSigningForClient(await bobSecondDevice.prepareClient(), bob.credentials, true);
await bootstrapCrossSigningForClient(await bobSecondDevice.prepareClient(), bob.credentials!, true);
/* should show an error for a message from a previously verified device */
await bobSecondDevice.sendMessage(testRoomId, "test encrypted from user that was previously verified");
@@ -29,14 +29,14 @@ test.describe("Invisible cryptography", () => {
await autoJoin(bob);
// create an encrypted room
const testRoomId = await createSharedEncryptedRoomWithUser(app, bob.credentials.userId);
const testRoomId = await createSharedEncryptedRoomWithUser(app, bob.credentials!.userId);
// Verify Bob
await verify(app, bob);
// Bob logs in a new device and resets cross-signing
const bobSecondDevice = await createSecondBotDevice(page, homeserver, bob);
await bootstrapCrossSigningForClient(await bobSecondDevice.prepareClient(), bob.credentials, true);
await bootstrapCrossSigningForClient(await bobSecondDevice.prepareClient(), bob.credentials!, true);
/* should show an error for a message from a previously verified device */
await bobSecondDevice.sendMessage(testRoomId, "test encrypted from user that was previously verified");
@@ -41,8 +41,8 @@ test.describe("migration", { tag: "@no-webkit" }, function () {
// When the progress bar first loads, it should have a high max (one per megolm session to import), and
// a relatively low value.
const progressBar = page.getByRole("progressbar");
const initialProgress = parseFloat(await progressBar.getAttribute("value"));
const initialMax = parseFloat(await progressBar.getAttribute("max"));
const initialProgress = parseFloat((await progressBar.getAttribute("value"))!);
const initialMax = parseFloat((await progressBar.getAttribute("max"))!);
expect(initialMax).toBeGreaterThan(4000);
expect(initialProgress).toBeGreaterThanOrEqual(0);
expect(initialProgress).toBeLessThanOrEqual(500);
@@ -53,8 +53,8 @@ test.describe("migration", { tag: "@no-webkit" }, function () {
async () => {
const progressBar = page.getByRole("progressbar");
return (
(parseFloat(await progressBar.getAttribute("value")) * 100.0) /
parseFloat(await progressBar.getAttribute("max"))
(parseFloat((await progressBar.getAttribute("value"))!) * 100.0) /
parseFloat((await progressBar.getAttribute("max"))!)
);
},
{ timeout: 60000 },
@@ -30,7 +30,7 @@ test.describe("Key storage out of sync toast", () => {
recoveryKey = res.recoveryKey;
await logIntoElement(page, credentials);
await verifyAfterLogin(page, recoveryKey.encodedPrivateKey);
await verifyAfterLogin(page, recoveryKey.encodedPrivateKey!);
await deleteCachedSecrets(page);
});
@@ -43,7 +43,7 @@ test.describe("Key storage out of sync toast", () => {
await page.getByRole("button", { name: "Enter recovery key" }).click();
await page.getByRole("textbox", { name: "Recovery Key" }).fill(recoveryKey.encodedPrivateKey);
await page.getByRole("textbox", { name: "Recovery Key" }).fill(recoveryKey.encodedPrivateKey!);
await page.getByRole("button", { name: "Continue" }).click();
await expect(page.getByRole("button", { name: "Enter recovery key" })).not.toBeVisible();
@@ -63,7 +63,7 @@ test.describe("Key storage out of sync toast", () => {
});
test.describe("'Turn on key storage' toast", () => {
let botClient: Bot | undefined;
let botClient: Bot;
test.beforeEach(async ({ page, homeserver, credentials }) => {
// Set up all crypto stuff. Key storage defaults to on.
@@ -73,7 +73,7 @@ test.describe("'Turn on key storage' toast", () => {
botClient = res.botClient;
await logIntoElement(page, credentials);
await verifyAfterLogin(page, recoveryKey.encodedPrivateKey);
await verifyAfterLogin(page, recoveryKey.encodedPrivateKey!);
// We won't be prompted for crypto setup unless we have an e2e room, so make one
await page
@@ -39,7 +39,7 @@ test.describe("User verification", () => {
user: aliceCredentials,
room: { roomId: dmRoomId },
}) => {
await waitForDevices(app, bob.credentials.userId, 1);
await waitForDevices(app, bob.credentials!.userId, 1);
await expect(page.getByRole("button", { name: "Avatar" })).toBeVisible();
const avatar = page.getByRole("button", { name: "Avatar" });
await avatar.click();
@@ -47,14 +47,14 @@ test.describe("User verification", () => {
// once Alice has joined, Bob starts the verification
const bobVerificationRequest = await bob.evaluateHandle(
async (client, { dmRoomId, aliceCredentials }) => {
const room = client.getRoom(dmRoomId);
const room = client.getRoom(dmRoomId)!;
while (room.getMember(aliceCredentials.userId)?.membership !== "join") {
await new Promise((resolve) => {
room.once(window.matrixcs.RoomStateEvent.Members, resolve);
});
}
return client.getCrypto().requestVerificationDM(aliceCredentials.userId, dmRoomId);
return client.getCrypto()!.requestVerificationDM(aliceCredentials.userId, dmRoomId);
},
{ dmRoomId, aliceCredentials },
);
@@ -62,7 +62,7 @@ test.describe("User verification", () => {
// there should also be a toast
const toast = await getToast(page, "Verification requested");
// it should contain the details of the requesting user
await expect(toast.getByText(`Bob (${bob.credentials.userId})`)).toBeVisible();
await expect(toast.getByText(`Bob (${bob.credentials!.userId})`)).toBeVisible();
// Accept
await toast.getByRole("button", { name: "Verify User" }).click();
@@ -93,7 +93,7 @@ test.describe("User verification", () => {
user: aliceCredentials,
room: { roomId: dmRoomId },
}) => {
await waitForDevices(app, bob.credentials.userId, 1);
await waitForDevices(app, bob.credentials!.userId, 1);
await expect(page.getByRole("button", { name: "Avatar" })).toBeVisible();
const avatar = page.getByRole("button", { name: "Avatar" });
await avatar.click();
@@ -101,14 +101,14 @@ test.describe("User verification", () => {
// once Alice has joined, Bob starts the verification
const bobVerificationRequest = await bob.evaluateHandle(
async (client, { dmRoomId, aliceCredentials }) => {
const room = client.getRoom(dmRoomId);
const room = client.getRoom(dmRoomId)!;
while (room.getMember(aliceCredentials.userId)?.membership !== "join") {
await new Promise((resolve) => {
room.once(window.matrixcs.RoomStateEvent.Members, resolve);
});
}
return client.getCrypto().requestVerificationDM(aliceCredentials.userId, dmRoomId);
return client.getCrypto()!.requestVerificationDM(aliceCredentials.userId, dmRoomId);
},
{ dmRoomId, aliceCredentials },
);
+18 -16
View File
@@ -51,7 +51,7 @@ export async function createBot(
botClient.setCredentials(credentials);
// Backup is prepared in the background. Poll until it is ready.
const botClientHandle = await botClient.prepareClient();
let expectedBackupVersion: string;
let expectedBackupVersion: string | null;
await expect
.poll(async () => {
expectedBackupVersion = await botClientHandle.evaluate((cli) =>
@@ -63,7 +63,7 @@ export async function createBot(
const recoveryKey = await botClient.getRecoveryKey();
return { botClient, recoveryKey, expectedBackupVersion };
return { botClient, recoveryKey, expectedBackupVersion: expectedBackupVersion! };
}
/**
@@ -98,13 +98,15 @@ export async function waitForVerificationRequest(client: Client): Promise<JSHand
export function handleSasVerification(verifier: JSHandle<Verifier>): Promise<EmojiMapping[]> {
return verifier.evaluate((verifier) => {
const event = verifier.getShowSasCallbacks();
if (event) return event.sas.emoji;
if (event) {
return event.sas.emoji!;
}
return new Promise<EmojiMapping[]>((resolve) => {
const onShowSas = (event: ShowSasCallbacks) => {
verifier.off("show_sas" as VerifierEvent, onShowSas);
void event.confirm();
resolve(event.sas.emoji);
resolve(event.sas.emoji!);
};
verifier.on("show_sas" as VerifierEvent, onShowSas);
@@ -117,24 +119,24 @@ export function handleSasVerification(verifier: JSHandle<Verifier>): Promise<Emo
*/
export async function checkDeviceIsCrossSigned(app: ElementAppPage): Promise<void> {
const { userId, deviceId, keys } = await app.client.evaluate(async (cli: MatrixClient) => {
const deviceId = cli.getDeviceId();
const userId = cli.getUserId();
const deviceId = cli.getDeviceId()!;
const userId = cli.getSafeUserId();
const keys = await cli.downloadKeysForUsers([userId]);
return { userId, deviceId, keys };
});
// there should be three cross-signing keys
expect(keys.master_keys[userId]).toHaveProperty("keys");
expect(keys.self_signing_keys[userId]).toHaveProperty("keys");
expect(keys.user_signing_keys[userId]).toHaveProperty("keys");
expect(keys.master_keys![userId]).toHaveProperty("keys");
expect(keys.self_signing_keys![userId]).toHaveProperty("keys");
expect(keys.user_signing_keys![userId]).toHaveProperty("keys");
// and the device should be signed by the self-signing key
const selfSigningKeyId = Object.keys(keys.self_signing_keys[userId].keys)[0];
const selfSigningKeyId = Object.keys(keys.self_signing_keys![userId].keys)[0];
expect(keys.device_keys[userId][deviceId]).toBeDefined();
const myDeviceSignatures = keys.device_keys[userId][deviceId].signatures[userId];
const myDeviceSignatures = keys.device_keys[userId][deviceId].signatures![userId];
expect(myDeviceSignatures[selfSigningKeyId]).toBeDefined();
}
@@ -190,7 +192,7 @@ export async function checkDeviceIsConnectedKeyBackup(
// We have a key backup
expect(backupInfo).toBeDefined();
// The key backup version is as expected
expect(backupInfo.version).toBe(expectedBackupVersion);
expect(backupInfo!.version).toBe(expectedBackupVersion);
// The active backup version is as expected
expect(activeBackupVersion).toBe(expectedBackupVersion);
// The backup key is stored in 4S
@@ -211,7 +213,7 @@ export async function logIntoElement(page: Page, credentials: Credentials) {
await page.goto("/#/login");
await page.getByRole("textbox", { name: "Username" }).fill(credentials.userId);
await page.getByPlaceholder("Password").fill(credentials.password);
await page.getByPlaceholder("Password").fill(credentials.password!);
await page.getByRole("button", { name: "Sign in" }).click();
}
@@ -379,7 +381,7 @@ export async function completeCreateSecretStorageDialog(
// the step is quite quick, and playwright can miss it, so we can't test for it.
if (opts && Object.hasOwn(opts, "accountPassword")) {
await expect(currentDialogLocator.getByRole("heading", { name: "Setting up keys" })).toBeVisible();
await page.getByPlaceholder("Password").fill(opts!.accountPassword);
await page.getByPlaceholder("Password").fill(opts!.accountPassword!);
await currentDialogLocator.getByRole("button", { name: "Continue" }).click();
}
@@ -595,7 +597,7 @@ export async function createSecondBotDevice(page: Page, homeserver: HomeserverIn
bootstrapSecretStorage: false,
bootstrapCrossSigning: false,
});
bobSecondDevice.setCredentials(await homeserver.loginUser(bob.credentials.userId, bob.credentials.password));
bobSecondDevice.setCredentials(await homeserver.loginUser(bob.credentials!.userId, bob.credentials!.password!));
await bobSecondDevice.prepareClient();
return bobSecondDevice;
}
@@ -638,7 +640,7 @@ export async function waitForDevices(
for (let i = 0; i < 10; ++i) {
const userDeviceMap = await cli.getCrypto()?.getUserDeviceInfo([userId], true);
const deviceMap = userDeviceMap?.get(userId);
if (deviceMap.size === expectedNumberOfDevices) return true;
if (deviceMap?.size === expectedNumberOfDevices) return true;
await new Promise((r) => setTimeout(r, 500));
}
return false;
@@ -362,7 +362,7 @@ test.describe("Editing", () => {
const messageTile = page.locator(`[data-event-id="${originalEventId}"]`);
// at this point, the edit event should still be unknown
const timeline = await app.client.evaluate(
(cli, { testRoomId, editEventId }) => cli.getRoom(testRoomId).getTimelineForEvent(editEventId),
(cli, { testRoomId, editEventId }) => cli.getRoom(testRoomId)!.getTimelineForEvent(editEventId),
{ testRoomId, editEventId },
);
expect(timeline).toBeNull();
@@ -94,7 +94,7 @@ test.describe("Rageshakes", () => {
if (request.method() !== "POST") {
throw Error("Expected POST");
}
const fields = formDataParser(request.postData(), await request.headerValue("Content-Type"));
const fields = formDataParser(request.postData()!, await request.headerValue("Content-Type"));
expect(fields.text).toEqual(
"These are some notes\n\nIssue: https://github.com/element-hq/element-web/12345",
);
@@ -56,7 +56,7 @@ test.describe("Forgot Password", () => {
"renders email verification dialog properly",
{ tag: "@screenshot" },
async ({ page, homeserver, credentials }) => {
const user = await homeserver.registerUser(credentials.username, credentials.password);
const user = await homeserver.registerUser(credentials.username, credentials.password!);
await homeserver.setThreepid(user.userId, "email", email);
@@ -73,8 +73,8 @@ test.describe("Forgot Password", () => {
await page.getByRole("button", { name: "Next" }).click();
await page.getByRole("textbox", { name: "New Password", exact: true }).fill(credentials.password);
await page.getByRole("textbox", { name: "Confirm new password", exact: true }).fill(credentials.password);
await page.getByRole("textbox", { name: "New Password", exact: true }).fill(credentials.password!);
await page.getByRole("textbox", { name: "Confirm new password", exact: true }).fill(credentials.password!);
await page.getByRole("button", { name: "Reset password" }).click();
@@ -148,18 +148,23 @@ test.describe("Integration Manager: Kick", () => {
test("should kick the target", async ({ page, app, bot: targetUser, room }) => {
await app.viewRoomByName(ROOM_NAME);
await app.client.inviteUser(room.roomId, targetUser.credentials.userId);
await app.client.inviteUser(room.roomId, targetUser.credentials!.userId);
await expect(page.getByText(`${BOT_DISPLAY_NAME} joined the room`)).toBeVisible();
await openIntegrationManager(app);
await sendActionFromIntegrationManager(page, integrationManagerUrl, room.roomId, targetUser.credentials.userId);
await sendActionFromIntegrationManager(
page,
integrationManagerUrl,
room.roomId,
targetUser.credentials!.userId,
);
await closeIntegrationManager(page, integrationManagerUrl);
await expectKickedMessage(page, true);
});
test("should not kick the target if lacking permissions", async ({ page, app, user, bot: targetUser, room }) => {
await app.viewRoomByName(ROOM_NAME);
await app.client.inviteUser(room.roomId, targetUser.credentials.userId);
await app.client.inviteUser(room.roomId, targetUser.credentials!.userId);
await expect(page.getByText(`${BOT_DISPLAY_NAME} joined the room`)).toBeVisible();
await app.client.sendStateEvent(room.roomId, "m.room.power_levels", {
@@ -170,31 +175,46 @@ test.describe("Integration Manager: Kick", () => {
});
await openIntegrationManager(app);
await sendActionFromIntegrationManager(page, integrationManagerUrl, room.roomId, targetUser.credentials.userId);
await sendActionFromIntegrationManager(
page,
integrationManagerUrl,
room.roomId,
targetUser.credentials!.userId,
);
await closeIntegrationManager(page, integrationManagerUrl);
await expectKickedMessage(page, false);
});
test("should no-op if the target already left", async ({ page, app, bot: targetUser, room }) => {
await app.viewRoomByName(ROOM_NAME);
await app.client.inviteUser(room.roomId, targetUser.credentials.userId);
await app.client.inviteUser(room.roomId, targetUser.credentials!.userId);
await expect(page.getByText(`${BOT_DISPLAY_NAME} joined the room`)).toBeVisible();
await targetUser.leave(room.roomId);
await openIntegrationManager(app);
await sendActionFromIntegrationManager(page, integrationManagerUrl, room.roomId, targetUser.credentials.userId);
await sendActionFromIntegrationManager(
page,
integrationManagerUrl,
room.roomId,
targetUser.credentials!.userId,
);
await closeIntegrationManager(page, integrationManagerUrl);
await expectKickedMessage(page, false);
});
test("should no-op if the target was banned", async ({ page, app, bot: targetUser, room }) => {
await app.viewRoomByName(ROOM_NAME);
await app.client.inviteUser(room.roomId, targetUser.credentials.userId);
await app.client.inviteUser(room.roomId, targetUser.credentials!.userId);
await expect(page.getByText(`${BOT_DISPLAY_NAME} joined the room`)).toBeVisible();
await app.client.ban(room.roomId, targetUser.credentials.userId);
await app.client.ban(room.roomId, targetUser.credentials!.userId);
await openIntegrationManager(app);
await sendActionFromIntegrationManager(page, integrationManagerUrl, room.roomId, targetUser.credentials.userId);
await sendActionFromIntegrationManager(
page,
integrationManagerUrl,
room.roomId,
targetUser.credentials!.userId,
);
await closeIntegrationManager(page, integrationManagerUrl);
await expectKickedMessage(page, false);
});
@@ -203,7 +223,12 @@ test.describe("Integration Manager: Kick", () => {
await app.viewRoomByName(ROOM_NAME);
await openIntegrationManager(app);
await sendActionFromIntegrationManager(page, integrationManagerUrl, room.roomId, targetUser.credentials.userId);
await sendActionFromIntegrationManager(
page,
integrationManagerUrl,
room.roomId,
targetUser.credentials!.userId,
);
await closeIntegrationManager(page, integrationManagerUrl);
await expectKickedMessage(page, false);
});
@@ -55,13 +55,13 @@ test.describe("Invite dialog", function () {
await expect(other.locator(".mx_InviteDialog_identityServer")).not.toBeVisible();
await other.getByTestId("invite-dialog-input").fill(bot.credentials.userId);
await other.getByTestId("invite-dialog-input").fill(bot.credentials!.userId);
// Assert that notification about identity servers appears after typing userId
await expect(other.locator(".mx_InviteDialog_identityServer")).toBeVisible();
// Assert that the bot id is rendered properly
await expect(other.getByRole("option", { name: botName }).getByText(bot.credentials.userId)).toBeVisible();
await expect(other.getByRole("option", { name: botName }).getByText(bot.credentials!.userId)).toBeVisible();
await other.getByRole("option", { name: botName }).click();
@@ -112,9 +112,9 @@ test.describe("Invite dialog", function () {
// Take a snapshot of the invite dialog
await expect(page.locator(".mx_Dialog")).toMatchScreenshot("invite-dialog-dm-without-user.png");
await other.getByTestId("invite-dialog-input").fill(bot.credentials.userId);
await other.getByTestId("invite-dialog-input").fill(bot.credentials!.userId);
await expect(other.getByRole("option", { name: botName }).getByText(bot.credentials.userId)).toBeVisible();
await expect(other.getByRole("option", { name: botName }).getByText(bot.credentials!.userId)).toBeVisible();
await other.getByRole("option", { name: botName }).click();
await expect(other.getByTestId("invite-dialog-input-wrapper").getByText(botName)).toBeVisible();
@@ -80,8 +80,12 @@ test.describe("Lazy Loading", () => {
async function checkPaginatedDisplayNames(app: ElementAppPage, charlies: Bot[]) {
await app.timeline.scrollToTop();
for (const charly of charlies) {
await expect(await app.timeline.findEventTile(charly.credentials.displayName, charlyMsg1)).toBeAttached();
await expect(await app.timeline.findEventTile(charly.credentials.displayName, charlyMsg2)).toBeAttached();
await expect(
(await app.timeline.findEventTile(charly.credentials!.displayName!, charlyMsg1))!,
).toBeAttached();
await expect(
(await app.timeline.findEventTile(charly.credentials!.displayName!, charlyMsg2))!,
).toBeAttached();
}
}
@@ -99,13 +103,13 @@ test.describe("Lazy Loading", () => {
await expect(getMemberInMemberlist(page, "Alice")).toBeAttached();
await expect(getMemberInMemberlist(page, "Bob")).toBeAttached();
for (const charly of charlies) {
await expect(getMemberInMemberlist(page, charly.credentials.displayName)).toBeAttached();
await expect(getMemberInMemberlist(page, charly.credentials!.displayName!)).toBeAttached();
}
}
async function checkMemberListLacksCharlies(page: Page, charlies: Bot[]) {
for (const charly of charlies) {
await expect(getMemberInMemberlist(page, charly.credentials.displayName)).not.toBeAttached();
await expect(getMemberInMemberlist(page, charly.credentials!.displayName!)).not.toBeAttached();
}
}
@@ -33,8 +33,8 @@ test.describe("Collapsible Room list", () => {
const boundingBox = await leftPanelLocator.boundingBox();
// Move mouse 2px to the right of the left-panel, this should be region that the user drags to resize the panel.
const mouseX = boundingBox.x + boundingBox.width + 2;
const mouseY = boundingBox.y + boundingBox.height / 2;
const mouseX = boundingBox!.x + boundingBox!.width + 2;
const mouseY = boundingBox!.y + boundingBox!.height / 2;
await page.mouse.move(mouseX, mouseY);
await page.mouse.down();
@@ -50,12 +50,12 @@ test.describe("Collapsible Room list", () => {
// Contract the panel
let previousBoundingBox = await resize(page, -50);
let currentBoundingBox = await leftPanelLocator.boundingBox();
expect(currentBoundingBox.width).toBeCloseTo(previousBoundingBox.width - 50, 0);
expect(currentBoundingBox!.width).toBeCloseTo(previousBoundingBox!.width - 50, 0);
// Expand the panel
previousBoundingBox = await resize(page, 30);
currentBoundingBox = await leftPanelLocator.boundingBox();
expect(currentBoundingBox.width).toBeCloseTo(previousBoundingBox.width + 30, 0);
expect(currentBoundingBox!.width).toBeCloseTo(previousBoundingBox!.width + 30, 0);
});
test(
@@ -67,7 +67,7 @@ test.describe("Collapsible Room list", () => {
// Collapse the panel
await resize(page, -300);
let currentBoundingBox = await leftPanelLocator.boundingBox();
expect(currentBoundingBox.width).toStrictEqual(0);
expect(currentBoundingBox!.width).toStrictEqual(0);
// Expect te separator to be shown
const separator = page.getByRole("separator", { name: "Click or drag to expand" });
@@ -77,19 +77,19 @@ test.describe("Collapsible Room list", () => {
// Should be possible to expand by clicking on the separator
await separator.click();
currentBoundingBox = await leftPanelLocator.boundingBox();
expect(currentBoundingBox.width).toBeGreaterThan(365);
expect(currentBoundingBox!.width).toBeGreaterThan(365);
// Collapse the panel again
await resize(page, -300);
// Check that the panel can be expanded by dragging the separator
const separatorBoundingBox = await separator.boundingBox();
const mouseX = separatorBoundingBox.x + separatorBoundingBox.width / 2;
const mouseY = separatorBoundingBox.y + separatorBoundingBox.height / 2;
const mouseX = separatorBoundingBox!.x + separatorBoundingBox!.width / 2;
const mouseY = separatorBoundingBox!.y + separatorBoundingBox!.height / 2;
await page.mouse.move(mouseX, mouseY);
await page.mouse.down();
await page.mouse.move(mouseX + 400, mouseY);
expect(currentBoundingBox.width).toBeGreaterThan(365);
expect(currentBoundingBox!.width).toBeGreaterThan(365);
},
);
});
@@ -48,7 +48,7 @@ test.describe("Room list filters and sort", () => {
We will also send a simple message in this room.
*/
const oldRoomId = await app.client.createRoom({ name: "Old Room" });
await app.client.inviteUser(oldRoomId, bot.credentials.userId);
await app.client.inviteUser(oldRoomId, bot.credentials!.userId);
await bot.joinRoom(oldRoomId);
const response = await app.client.sendMessage(oldRoomId, "Hello!");
@@ -99,8 +99,8 @@ test.describe("Room list filters and sort", () => {
});
test.describe("Room list", () => {
let unReadDmId: string | undefined;
let unReadRoomId: string | undefined;
let unReadDmId: string;
let unReadRoomId: string;
test.beforeEach(async ({ page, app, bot, user }) => {
await app.client.createRoom({ name: "empty room" });
@@ -114,7 +114,7 @@ test.describe("Room list filters and sort", () => {
await bot.sendMessage(unReadDmId, "I am a robot. Beep.");
unReadRoomId = await app.client.createRoom({ name: "unread room" });
await app.client.inviteUser(unReadRoomId, bot.credentials.userId);
await app.client.inviteUser(unReadRoomId, bot.credentials!.userId);
await bot.joinRoom(unReadRoomId);
await bot.sendMessage(unReadRoomId, "I am a robot. Beep.");
@@ -135,7 +135,7 @@ test.describe("Room list filters and sort", () => {
});
const mentionRoomId = await app.client.createRoom({ name: "room with mention" });
await app.client.inviteUser(mentionRoomId, bot.credentials.userId);
await app.client.inviteUser(mentionRoomId, bot.credentials!.userId);
await bot.joinRoom(mentionRoomId);
const clientBot = await bot.prepareClient();
@@ -364,7 +364,7 @@ test.describe("Room list sections", () => {
const roomList = getRoomList(page);
// Invite the bot and have it send a message to generate an unread
await app.client.inviteUser(favouriteId, bot.credentials.userId);
await app.client.inviteUser(favouriteId, bot.credentials!.userId);
await bot.joinRoom(favouriteId);
await bot.sendMessage(favouriteId, "Hello from bot!");
@@ -391,7 +391,7 @@ test.describe("Room list sections", () => {
// A room with a mention, landing in the Chats section
const mentionId = await app.client.createRoom({ name: "mention room" });
await app.client.inviteUser(mentionId, bot.credentials.userId);
await app.client.inviteUser(mentionId, bot.credentials!.userId);
await bot.joinRoom(mentionId);
const clientBot = await bot.prepareClient();
await clientBot.evaluate(
@@ -464,13 +464,13 @@ test.describe("Room list sections", () => {
await app.client.evaluate(async (client, roomId) => {
await client.setRoomTag(roomId, "m.favourite");
}, favouriteId);
await app.client.inviteUser(favouriteId, bot.credentials.userId);
await app.client.inviteUser(favouriteId, bot.credentials!.userId);
await bot.joinRoom(favouriteId);
await bot.sendMessage(favouriteId, "Hello from favourite!");
// Create a regular room with unread messages
const regularId = await app.client.createRoom({ name: "regular with unread" });
await app.client.inviteUser(regularId, bot.credentials.userId);
await app.client.inviteUser(regularId, bot.credentials!.userId);
await bot.joinRoom(regularId);
await bot.sendMessage(regularId, "Hello from regular!");
@@ -62,7 +62,7 @@ test.describe("Room list unread activity toast", () => {
// A room with a real notification count, named so it sorts to the very bottom under A-Z.
const targetId = await app.client.createRoom({ name: "zzz unread room" });
await app.client.inviteUser(targetId, bot.credentials.userId);
await app.client.inviteUser(targetId, bot.credentials!.userId);
await bot.joinRoom(targetId);
// Enough filler rooms to push the target well below the visible area.
@@ -100,7 +100,7 @@ test.describe("Room list unread activity toast", () => {
// The target's unread state will only ever be an activity dot, never a notification count: set it
// to "@mentions & keywords" so a plain (non-mention) message produces activity rather than a count.
const targetId = await app.client.createRoom({ name: "zzz activity room" });
await app.client.inviteUser(targetId, bot.credentials.userId);
await app.client.inviteUser(targetId, bot.credentials!.userId);
await bot.joinRoom(targetId);
await app.viewRoomById(targetId);
@@ -148,7 +148,7 @@ test.describe("Room list unread activity toast", () => {
// A regular (Chats) room with a notification count.
const notifyId = await app.client.createRoom({ name: "chats notify room" });
await app.client.inviteUser(notifyId, bot.credentials.userId);
await app.client.inviteUser(notifyId, bot.credentials!.userId);
await bot.joinRoom(notifyId);
// A favourite room so the list renders in section mode from the start.
@@ -192,7 +192,7 @@ test.describe("Room list", () => {
const roomListView = getRoomList(page);
const roomId = await app.client.createRoom({ name: "1 notification" });
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await bot.joinRoom(roomId);
await bot.sendMessage(roomId, "I am a robot. Beep.");
@@ -396,7 +396,7 @@ test.describe("Room list", () => {
const roomListView = getRoomList(page);
const roomId = await app.client.createRoom({ name: "2 notifications" });
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await bot.joinRoom(roomId);
await bot.sendMessage(roomId, "I am a robot. Beep.");
@@ -412,7 +412,7 @@ test.describe("Room list", () => {
const roomListView = getRoomList(page);
const roomId = await app.client.createRoom({ name: "mention" });
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await bot.joinRoom(roomId);
const clientBot = await bot.prepareClient();
@@ -445,7 +445,7 @@ test.describe("Room list", () => {
// focus the user menu to avoid to have hover decoration
await page.getByRole("button", { name: "User menu" }).focus();
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await bot.joinRoom(roomId);
await bot.sendMessage(roomId, "I am a robot. Beep.");
@@ -483,7 +483,7 @@ test.describe("Room list", () => {
const otherRoomId = await app.client.createRoom({ name: "other room" });
const roomId = await app.client.createRoom({ name: "activity" });
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await bot.joinRoom(roomId);
await app.viewRoomById(roomId);
@@ -510,7 +510,7 @@ test.describe("Room list", () => {
const roomListView = getRoomList(page);
const roomId = await app.client.createRoom({ name: "mark as unread" });
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await bot.joinRoom(roomId);
const room = roomListView.getByRole("option", { name: "mark as unread" });
@@ -527,7 +527,7 @@ test.describe("Room list", () => {
const roomListView = getRoomList(page);
const roomId = await app.client.createRoom({ name: "silent" });
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await bot.joinRoom(roomId);
await app.viewRoomById(roomId);
@@ -38,8 +38,8 @@ test.describe("permalinks", () => {
await app.viewRoomByName(room1Name);
await app.client.inviteUser(room1Id, bob.credentials.userId);
await app.client.inviteUser(room2Id, charlotte.credentials.userId);
await app.client.inviteUser(room1Id, bob.credentials!.userId);
await app.client.inviteUser(room2Id, charlotte.credentials!.userId);
await app.client.sendMessage(room1Id, "At room mention: @room");
@@ -65,11 +65,11 @@ test.describe("permalinks", () => {
await app.client.sendMessage(
room1Id,
`Permalink to a user in the room: ${permalinkPrefix}${bob.credentials.userId}`,
`Permalink to a user in the room: ${permalinkPrefix}${bob.credentials!.userId}`,
);
await app.client.sendMessage(
room1Id,
`Permalink to a user in another room: ${permalinkPrefix}${charlotte.credentials.userId}`,
`Permalink to a user in another room: ${permalinkPrefix}${charlotte.credentials!.userId}`,
);
await app.client.sendMessage(
room1Id,
@@ -117,14 +117,14 @@ test.describe("triple-click message selection", () => {
await bot.prepareClient();
const roomId = await app.client.createRoom({ name: "Test Room" });
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await app.viewRoomByName("Test Room");
// Send a message with user and room pills
await app.client.sendMessage(
roomId,
`Testing triple-click message selection. ` +
`User: ${permalinkPrefix}${bot.credentials.userId}, ` +
`User: ${permalinkPrefix}${bot.credentials!.userId}, ` +
`Room: ${permalinkPrefix}${roomId}, ` +
`Message: ${permalinkPrefix}${roomId}/$dummy-event, ` +
`and @room mention.`,
@@ -75,7 +75,7 @@ async function login(page: Page, homeserver: HomeserverInstance, credentials: Cr
await selectHomeserver(page, homeserver.baseUrl);
await page.getByRole("textbox", { name: "Username" }).fill(credentials.username);
await page.getByPlaceholder("Password").fill(credentials.password);
await page.getByPlaceholder("Password").fill(credentials.password!);
await page.getByRole("button", { name: "Sign in" }).click();
}
@@ -152,7 +152,7 @@ test.describe("Login", () => {
await expect(axe).toHaveNoViolations();
await page.getByRole("textbox", { name: "Username" }).fill(credentials.username);
await page.getByPlaceholder("Password").fill(credentials.password);
await page.getByPlaceholder("Password").fill(credentials.password!);
await page.getByRole("button", { name: "Sign in" }).click();
await expect(page).toHaveURL(/\/#\/home$/);
@@ -323,7 +323,7 @@ test.describe("Login", () => {
// Finally we actually continue
await page.getByRole("button", { name: "Continue" }).click();
await page.getByPlaceholder("Password").fill(credentials.password);
await page.getByPlaceholder("Password").fill(credentials.password!);
await page.getByRole("button", { name: "Continue" }).click();
// We end up at the Home screen
@@ -336,7 +336,7 @@ test.describe("Login", () => {
test.describe("logout", () => {
test("should go to welcome page on logout", async ({ page, user }) => {
await page.getByRole("button", { name: "User menu" }).click();
await expect(page.getByText(user.displayName, { exact: true })).toBeVisible();
await expect(page.getByText(user.displayName!, { exact: true })).toBeVisible();
// Allow the outstanding requests queue to settle before logging out
await page.waitForTimeout(2000);
@@ -24,7 +24,7 @@ test.use({
test.describe("logout with logout_redirect_url", () => {
test("should respect logout_redirect_url", async ({ page, user }) => {
await page.getByRole("button", { name: "User menu" }).click();
await expect(page.getByText(user.displayName, { exact: true })).toBeVisible();
await expect(page.getByText(user.displayName!, { exact: true })).toBeVisible();
// give a change for the outstanding requests queue to settle before logging out
await page.waitForTimeout(2000);
@@ -26,7 +26,7 @@ test.describe("Soft logout with password user", () => {
test("shows the soft-logout page when a request fails, and allows a re-login", async ({ page, user }) => {
await interceptRequestsWithSoftLogout(page, user);
await expect(page.getByText("You're signed out")).toBeVisible();
await page.getByPlaceholder("Password").fill(user.password);
await page.getByPlaceholder("Password").fill(user.password!);
await page.getByPlaceholder("Password").press("Enter");
// back to the welcome page
+4 -4
View File
@@ -52,13 +52,13 @@ export async function doTokenRegistration(
return page.evaluate(() => ({
homeserverBaseUrl: window.mxMatrixClientPeg.get().getHomeserverUrl(),
accessToken: window.mxMatrixClientPeg.get().getAccessToken(),
userId: window.mxMatrixClientPeg.get().getUserId(),
deviceId: window.mxMatrixClientPeg.get().getDeviceId(),
accessToken: window.mxMatrixClientPeg.get().getAccessToken()!,
userId: window.mxMatrixClientPeg.get().getSafeUserId(),
deviceId: window.mxMatrixClientPeg.get().getDeviceId()!,
homeServer: window.mxMatrixClientPeg.get().getHomeserverUrl(),
password: null,
displayName: "Alice",
username: window.mxMatrixClientPeg.get().getUserIdLocalpart(),
username: window.mxMatrixClientPeg.get().getUserIdLocalpart()!,
}));
}
+2 -2
View File
@@ -30,12 +30,12 @@ export async function registerAccountMas(
await page.getByRole("textbox", { name: "Confirm Password" }).fill(password);
await page.getByRole("button", { name: "Continue" }).click();
let code: string;
let code!: string;
await expect(async () => {
const messages = await mailpit.listMessages();
expect(messages.messages[0].To[0].Address).toEqual(email);
const text = await mailpit.renderMessageText(messages.messages[0].ID);
[, code] = text.match(/Your verification code to confirm this email address is: (\d{6})/);
[, code] = text.match(/Your verification code to confirm this email address is: (\d{6})/)!;
}).toPass();
await page.getByRole("textbox", { name: "6-digit code" }).fill(code);
@@ -29,7 +29,7 @@ test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => {
}, testInfo) => {
await page.clock.install();
const tokenUri = `${mas.baseUrl}/oauth2/token`;
const tokenUri = `${mas!.baseUrl}/oauth2/token`;
const tokenApiPromise = page.waitForRequest(
(request) => request.url() === tokenUri && request.postDataJSON()["grant_type"] === "authorization_code",
);
@@ -66,7 +66,7 @@ test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => {
await newPage.close();
// Assert logging out revokes both tokens
const revokeUri = `${mas.baseUrl}/oauth2/revoke`;
const revokeUri = `${mas!.baseUrl}/oauth2/revoke`;
const revokeAccessTokenPromise = page.waitForRequest(
(request) => request.url() === revokeUri && request.postDataJSON()["token_type_hint"] === "access_token",
);
@@ -95,7 +95,7 @@ test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => {
await expect(page.getByText("Welcome")).toBeVisible();
await page.goto("about:blank");
const result = await mas.manage("kill-sessions", userId);
const result = await mas!.manage("kill-sessions", userId);
expect(result.output).toContain("Ended 1 active OAuth 2.0 session");
await page.goto("http://localhost:8080");
@@ -12,7 +12,7 @@ import { type Credentials } from "../../plugins/homeserver";
import { isDendrite } from "../../plugins/homeserver/dendrite";
const test = base.extend<{
user2?: Credentials;
user2: Credentials;
}>({});
test.describe("1:1 chat room", () => {
@@ -38,12 +38,12 @@ test.describe("1:1 chat room", () => {
// wait till the room was left
await expect(
page.getByRole("group", { name: "Rooms" }).locator(".mx_RoomTile").getByText(user2.displayName),
page.getByRole("group", { name: "Rooms" }).locator(".mx_RoomTile").getByText(user2.displayName!),
).not.toBeVisible();
await page.waitForTimeout(500); // avoid race condition with routing
// open new 1:1 chat room
await page.goto(`/#/user/${user2.userId}?action=chat`);
await expect(page.locator(".mx_RoomHeader_heading").getByText(user2.displayName)).toBeVisible();
await expect(page.locator(".mx_RoomHeader_heading").getByText(user2.displayName!)).toBeVisible();
});
});
@@ -19,7 +19,7 @@ type RoomRef = { name: string; roomId: string };
* Set up for pinned message tests.
*/
export const test = base.extend<{
room1Name?: string;
room1Name: string;
room1: { name: string; roomId: string };
util: Helpers;
}>({
@@ -28,7 +28,7 @@ export const test = base.extend<{
room1Name: "Room 1",
room1: async ({ room1Name: name, app, user, bot }, use) => {
const roomId = await app.client.createRoom({ name, invite: [bot.credentials.userId] });
const roomId = await app.client.createRoom({ name, invite: [bot.credentials!.userId] });
await bot.awaitRoomMembership(roomId);
await use({ name, roomId });
},
@@ -95,7 +95,7 @@ test.describe("Poll history", () => {
const roomId = await app.client.createRoom({});
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await page.goto("/#/room/" + roomId);
// wait until Bob joined
await expect(page.getByText("BotBob joined the room")).toBeAttached();
+13 -13
View File
@@ -98,7 +98,7 @@ test.describe("Polls", () => {
test("should be creatable and votable", { tag: "@screenshot" }, async ({ page, app, bot, user }) => {
const roomId: string = await app.client.createRoom({});
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await page.goto("/#/room/" + roomId);
// wait until Bob joined
await expect(page.getByText("BotBob joined the room")).toBeAttached();
@@ -116,10 +116,10 @@ test.describe("Polls", () => {
await createPoll(page, pollParams);
// Wait for message to send, get its ID and save as @pollId
const pollId = await page
const pollId = (await page
.locator(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]")
.filter({ hasText: pollParams.title })
.getAttribute("data-scroll-tokens");
.getAttribute("data-scroll-tokens"))!;
await expect(getPollTile(page, pollId)).toMatchScreenshot("Polls_Timeline_tile_no_votes.png", {
css: `
.mx_MessageTimestamp {
@@ -162,7 +162,7 @@ test.describe("Polls", () => {
test("should be editable from context menu if no votes have been cast", async ({ page, app, user, bot }) => {
const roomId: string = await app.client.createRoom({});
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await page.goto("/#/room/" + roomId);
const locator = await app.openMessageComposerOptions();
@@ -175,10 +175,10 @@ test.describe("Polls", () => {
await createPoll(page, pollParams);
// Wait for message to send, get its ID and save as @pollId
const pollId = await page
const pollId = (await page
.locator(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]")
.filter({ hasText: pollParams.title })
.getAttribute("data-scroll-tokens");
.getAttribute("data-scroll-tokens"))!;
// Open context menu
await getPollTile(page, pollId).click({ button: "right" });
@@ -192,7 +192,7 @@ test.describe("Polls", () => {
test("should not be editable from context menu if votes have been cast", async ({ page, app, user, bot }) => {
const roomId: string = await app.client.createRoom({});
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await page.goto("/#/room/" + roomId);
const locator = await app.openMessageComposerOptions();
@@ -205,10 +205,10 @@ test.describe("Polls", () => {
await createPoll(page, pollParams);
// Wait for message to send, get its ID and save as @pollId
const pollId = await page
const pollId = (await page
.locator(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]")
.filter({ hasText: pollParams.title })
.getAttribute("data-scroll-tokens");
.getAttribute("data-scroll-tokens"))!;
// Bot votes 'Maybe' in the poll
await botVoteForOption(page, bot, roomId, pollId, pollParams.options[2]);
@@ -234,8 +234,8 @@ test.describe("Polls", () => {
await botCharlie.prepareClient();
const roomId: string = await app.client.createRoom({});
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, botCharlie.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await app.client.inviteUser(roomId, botCharlie.credentials!.userId);
await page.goto("/#/room/" + roomId);
// wait until the bots joined
@@ -253,10 +253,10 @@ test.describe("Polls", () => {
await createPoll(page, pollParams);
// Wait for message to send, get its ID and save as @pollId
const pollId = await page
const pollId = (await page
.locator(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]")
.filter({ hasText: pollParams.title })
.getAttribute("data-scroll-tokens");
.getAttribute("data-scroll-tokens"))!;
// Bob starts thread on the poll
await bot.sendMessage(
@@ -18,7 +18,7 @@ test.describe("Presence tests", () => {
// This is failing on CI (https://github.com/element-hq/element-web/issues/27270)
// but not locally, so debugging this is going to be tricky. Let's disable it for now.
test.skip("renders unreachable presence state correctly", async ({ page, app, user, bot: bob }) => {
await app.client.createRoom({ name: "My Room", invite: [bob.credentials.userId] });
await app.client.createRoom({ name: "My Room", invite: [bob.credentials!.userId] });
await app.viewRoomByName("My Room");
await bob.evaluate(async (client) => {
@@ -36,7 +36,7 @@ test.describe("Presence tests", () => {
events: [
{
type: "m.presence",
sender: bob.credentials.userId,
sender: bob.credentials!.userId,
content: {
presence: "io.element.unreachable",
currently_active: false,
@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
/* See readme.md for tips on writing these tests. */
import { customEvent, many, test } from ".";
import { many, test } from ".";
import { isDendrite } from "../../plugins/homeserver/dendrite";
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
@@ -20,6 +20,7 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
roomAlpha: room1,
roomBeta: room2,
util,
msg,
}) => {
await util.goTo(room1);
await util.assertRead(room2);
@@ -29,13 +30,14 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
await util.markAsRead(room2);
await util.assertRead(room2);
await util.receiveMessages(room2, [customEvent("org.custom.event", { body: "foobar" })]);
await util.receiveMessages(room2, [msg.customEvent("org.custom.event", { body: "foobar" })]);
await util.assertRead(room2);
});
test("Sending an important event after unimportant ones makes the room unread", async ({
roomAlpha: room1,
roomBeta: room2,
util,
msg,
}) => {
// Given We have read the important messages
await util.goTo(room1);
@@ -47,7 +49,7 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
await util.goTo(room1);
// When we receive unimportant messages
await util.receiveMessages(room2, [customEvent("org.custom.event", { body: "foobar" })]);
await util.receiveMessages(room2, [msg.customEvent("org.custom.event", { body: "foobar" })]);
// Then the room is still read
await util.assertStillRead(room2);
@@ -62,6 +64,7 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
roomAlpha: room1,
roomBeta: room2,
util,
msg,
}) => {
// Display room 1
await util.goTo(room1);
@@ -71,9 +74,9 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
// We received 3 unimportant messages to room2
await util.receiveMessages(room2, [
customEvent("org.custom.event", { body: "foobar1" }),
customEvent("org.custom.event", { body: "foobar2" }),
customEvent("org.custom.event", { body: "foobar3" }),
msg.customEvent("org.custom.event", { body: "foobar1" }),
msg.customEvent("org.custom.event", { body: "foobar2" }),
msg.customEvent("org.custom.event", { body: "foobar3" }),
]);
// The room 2 is still read
+26 -34
View File
@@ -23,9 +23,9 @@ type RoomRef = { name: string; roomId: string };
* - Invite the bot to both rooms and ensure that it has joined
*/
export const test = base.extend<{
roomAlphaName?: string;
roomAlphaName: string;
roomAlpha: RoomRef;
roomBetaName?: string;
roomBetaName: string;
roomBeta: RoomRef;
msg: MessageBuilder;
util: Helpers;
@@ -35,13 +35,13 @@ export const test = base.extend<{
roomAlphaName: "Room Alpha",
roomAlpha: async ({ roomAlphaName: name, app, user, bot }, use) => {
const roomId = await app.client.createRoom({ name, invite: [bot.credentials.userId] });
const roomId = await app.client.createRoom({ name, invite: [bot.credentials!.userId] });
await bot.awaitRoomMembership(roomId);
await use({ name, roomId });
},
roomBetaName: "Room Beta",
roomBeta: async ({ roomBetaName: name, app, user, bot }, use) => {
const roomId = await app.client.createRoom({ name, invite: [bot.credentials.userId] });
const roomId = await app.client.createRoom({ name, invite: [bot.credentials!.userId] });
await bot.awaitRoomMembership(roomId);
await use({ name, roomId });
},
@@ -159,7 +159,7 @@ export class MessageBuilder {
ev.getRelation()?.rel_type === "m.thread"
? {
rel_type: "m.thread",
event_id: ev.getRelation().event_id,
event_id: ev.getRelation()!.event_id,
}
: {};
@@ -222,11 +222,11 @@ export class MessageBuilder {
public async performAction(bot: Bot, room: JSHandle<Room>): Promise<void> {
const ev = await this.messageFinder.getMessage(room, targetMessage, true);
const { id, threadId } = await ev.evaluate((ev) => ({
id: ev.getId(),
id: ev.getId()!,
threadId: !ev.isThreadRoot ? ev.threadRootId : undefined,
}));
const roomId = await room.evaluate((room) => room.roomId);
await bot.reactToMessage(roomId, threadId, id, reaction);
await bot.reactToMessage(roomId, threadId ?? null, id, reaction);
}
})(this);
}
@@ -240,11 +240,11 @@ export class MessageBuilder {
public async performAction(bot: Bot, room: JSHandle<Room>): Promise<void> {
const ev = await this.messageFinder.getMessage(room, targetMessage, true);
const { id, threadId } = await ev.evaluate((ev) => ({
id: ev.getId(),
id: ev.getId()!,
threadId: !ev.isThreadRoot ? ev.threadRootId : undefined,
}));
const roomId = await room.evaluate((room) => room.roomId);
await bot.redactEvent(roomId, threadId, id);
await bot.redactEvent(roomId, threadId!, id);
}
})(this);
}
@@ -286,6 +286,20 @@ export class MessageBuilder {
{ event },
);
}
/**
* BotActionSpec to send a custom event
* @param eventType - the type of the event to send
* @param content - the event content to send
*/
customEvent(eventType: string, content: Record<string, any>): BotActionSpec {
return new (class extends BotActionSpec {
public async performAction(cli: Client, room: JSHandle<Room>): Promise<void> {
const roomId = await room.evaluate((room) => room.roomId);
await cli.sendEvent(roomId, null, eventType, content);
}
})(this);
}
}
/**
@@ -295,11 +309,7 @@ export class MessageBuilder {
* MessageBuilder.replyTo} which creates a reply based on a previous message.
*/
export abstract class MessageContentSpec {
messageFinder: MessageBuilder | null;
constructor(messageFinder: MessageBuilder = null) {
this.messageFinder = messageFinder;
}
constructor(public readonly messageFinder: MessageBuilder) {}
public abstract getContent(room: JSHandle<Room>): Promise<Record<string, unknown>>;
}
@@ -312,11 +322,7 @@ export abstract class MessageContentSpec {
* MessageBuilder.redactionOf} which redacts the message we are referring to.
*/
export abstract class BotActionSpec {
messageFinder: MessageBuilder | null;
constructor(messageFinder: MessageBuilder = null) {
this.messageFinder = messageFinder;
}
constructor(public readonly messageFinder: MessageBuilder) {}
public abstract performAction(client: Client, room: JSHandle<Room>): Promise<void>;
}
@@ -542,7 +548,7 @@ class Helpers {
async findRoomById(roomId: string): Promise<JSHandle<Room>> {
return this.app.client.evaluateHandle((cli, roomId) => {
return cli.getRooms().find((r) => r.roomId === roomId);
return cli.getRooms().find((r) => r.roomId === roomId)!;
}, roomId);
}
@@ -614,20 +620,6 @@ class Helpers {
}
}
/**
* BotActionSpec to send a custom event
* @param eventType - the type of the event to send
* @param content - the event content to send
*/
export function customEvent(eventType: string, content: Record<string, any>): BotActionSpec {
return new (class extends BotActionSpec {
public async performAction(cli: Client, room: JSHandle<Room>): Promise<void> {
const roomId = await room.evaluate((room) => room.roomId);
await cli.sendEvent(roomId, null, eventType, content);
}
})();
}
/**
* Generate strings with the supplied prefix, suffixed with numbers.
*
@@ -64,7 +64,7 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
const sendThreadedReadReceipt = async (
app: ElementAppPage,
eventResponse: ISendEventResponse,
threadRootEventResponse: ISendEventResponse = undefined,
threadRootEventResponse?: ISendEventResponse,
) => {
await app.client.sendReadReceipt(
await fakeEventFromSent(app, eventResponse, threadRootEventResponse?.event_id),
@@ -93,10 +93,10 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
*/
selectedRoomId = await app.client.createRoom({ name: selectedRoomName });
// Invite the bot to Other room
otherRoomId = await app.client.createRoom({ name: otherRoomName, invite: [bot.credentials.userId] });
otherRoomId = await app.client.createRoom({ name: otherRoomName, invite: [bot.credentials!.userId] });
await page.goto(`/#/room/${otherRoomId}`);
await expect(page.getByText(`${bot.credentials.displayName} joined the room`)).toBeVisible();
await expect(page.getByText(`${bot.credentials!.displayName} joined the room`)).toBeVisible();
// Then go into Selected room
await page.goto(`/#/room/${selectedRoomId}`);
@@ -287,7 +287,7 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
sendMessageResponses.push(await sendMessage(bot, i));
}
const lastMessageId = sendMessageResponses.at(-1).event_id;
const lastMessageId = sendMessageResponses.at(-1)!.event_id;
const uriEncodedLastMessageId = encodeURIComponent(lastMessageId);
// wait until all messages have been received
@@ -330,7 +330,7 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
const readMarkersRequest2 = await readMarkersRequestPromise2;
expect(readMarkersRequest2.postDataJSON()).toEqual({
["m.fully_read"]: sendMessageResponses.at(-1).event_id,
["m.fully_read"]: sendMessageResponses.at(-1)!.event_id,
});
});
});
@@ -22,7 +22,7 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
bot,
}) => {
// Create a third room to navigate to
const room3Id = await app.client.createRoom({ name: "Room Gamma", invite: [bot.credentials.userId] });
const room3Id = await app.client.createRoom({ name: "Room Gamma", invite: [bot.credentials!.userId] });
await bot.awaitRoomMembership(room3Id);
const room3 = { name: "Room Gamma", roomId: room3Id };
@@ -51,7 +51,7 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
bot,
}) => {
// Create a third room to navigate to
const room3Id = await app.client.createRoom({ name: "Room Gamma", invite: [bot.credentials.userId] });
const room3Id = await app.client.createRoom({ name: "Room Gamma", invite: [bot.credentials!.userId] });
await bot.awaitRoomMembership(room3Id);
const room3 = { name: "Room Gamma", roomId: room3Id };
@@ -55,7 +55,7 @@ test.describe("Email Registration", async () => {
expect(messages.messages).toHaveLength(1);
expect(messages.messages[0].To[0].Address).toEqual("alice@email.com");
const text = await mailpitClient.renderMessageText(messages.messages[0].ID);
const [emailLink] = text.match(/http.+/);
const [emailLink] = text.match(/http.+/)!;
await request.get(emailLink); // "Click" the link in the email
await expect(page.getByText("Welcome alice")).toBeVisible();
@@ -46,7 +46,7 @@ test.describe("global retention rules", () => {
test("should apply", async ({ app, bot, page }) => {
const roomId = await app.client.createRoom({
name: "Test",
invite: [bot.credentials.userId],
invite: [bot.credentials!.userId],
});
await checkRetentionInRoom({ app, bot, page }, roomId);
});
@@ -46,7 +46,7 @@ test.describe("Retention", () => {
test("should apply retention to a bunch of messages", async ({ app, homeserver, page, user, bot }) => {
const roomId = await app.client.createRoom({
name: "Test",
invite: [bot.credentials.userId],
invite: [bot.credentials!.userId],
initial_state: [
{
state_key: "",
@@ -66,7 +66,7 @@ test.describe("Retention", () => {
test("retention rules should apply after restart", async ({ app, bot, page }) => {
const roomId = await app.client.createRoom({
name: "Test",
invite: [bot.credentials.userId],
invite: [bot.credentials!.userId],
});
await bot.joinRoom(roomId);
await app.viewRoomByName("Test");
@@ -102,7 +102,7 @@ test.describe("Retention", () => {
const currentTime = new Date();
const roomId = await app.client.createRoom({
name: "Test",
invite: [bot.credentials.userId],
invite: [bot.credentials!.userId],
initial_state: [
{
state_key: "",
@@ -147,7 +147,7 @@ test.describe("RightPanel", () => {
async ({ page, homeserver, app }) => {
const bobLongName = new Bot(page, homeserver, { displayName: LONG_NAME });
await bobLongName.prepareClient();
await app.client.inviteUser(testRoomId, bobLongName.credentials.userId);
await app.client.inviteUser(testRoomId, bobLongName.credentials!.userId);
await bobLongName.joinRoom(testRoomId);
await viewRoomSummaryByName(page, app, ROOM_NAME);
@@ -51,7 +51,7 @@ test.describe("Room Directory", () => {
const resp = await bot.publicRooms({});
expect(resp.total_room_count_estimate).toBeGreaterThanOrEqual(1);
expect(resp.chunk).toHaveLength(resp.total_room_count_estimate);
expect(resp.chunk).toHaveLength(resp.total_room_count_estimate!);
expect(resp.chunk.find((r) => r.room_id === roomId)).toBeTruthy();
},
);
+1 -1
View File
@@ -68,7 +68,7 @@ test.describe("Invites", () => {
await app.settings.openUserSettings("Security & Privacy");
const ignoredUsersList = page.getByRole("list", { name: "Ignored users" });
await ignoredUsersList.scrollIntoViewIfNeeded();
await expect(ignoredUsersList.getByRole("listitem", { name: bot.credentials.userId })).toBeVisible();
await expect(ignoredUsersList.getByRole("listitem", { name: bot.credentials!.userId })).toBeVisible();
},
);
});
@@ -161,9 +161,9 @@ test.describe("Room Status Bar", () => {
});
const other = page.locator(".mx_InviteDialog_other");
await other.getByTestId("invite-dialog-input").fill(bot.credentials.userId);
await other.getByTestId("invite-dialog-input").fill(bot.credentials!.userId);
await expect(
other.getByRole("option", { name: "Alice" }).getByText(bot.credentials.userId),
other.getByRole("option", { name: "Alice" }).getByText(bot.credentials!.userId),
).toBeVisible();
await other.getByRole("option", { name: "Alice" }).click();
await other.getByRole("button", { name: "Go" }).click();
+2 -2
View File
@@ -34,8 +34,8 @@ test.describe("Room Directory", () => {
});
},
{
bob: bob.credentials.userId,
charlie: charlie.credentials.userId,
bob: bob.credentials!.userId,
charlie: charlie.credentials!.userId,
},
);
@@ -31,7 +31,7 @@ test.describe("Mark as Unread", () => {
const dummyRoomId = await app.client.createRoom({
name: "Room of no consequence",
});
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await bot.joinRoom(roomId);
await bot.sendMessage(roomId, "I am a robot. Beep.");
@@ -19,7 +19,7 @@ test.describe("Device manager", () => {
test.beforeEach(async ({ homeserver, user }) => {
// create 3 extra sessions to manage
for (let i = 0; i < 3; i++) {
await homeserver.loginUser(user.userId, user.password);
await homeserver.loginUser(user.userId, user.password!);
}
});
@@ -21,7 +21,7 @@ test.describe("Advanced section in Encryption tab", () => {
const section = util.getEncryptionDetailsSection();
const deviceId = await page.evaluate(() => window.mxMatrixClientPeg.get().getDeviceId());
await expect(section.getByText(deviceId)).toBeVisible();
await expect(section.getByText(deviceId!)).toBeVisible();
await expect(section).toMatchScreenshot("encryption-details.png", {
mask: [section.getByTestId("deviceId"), section.getByTestId("sessionKey")],
@@ -59,7 +59,7 @@ test.describe("Advanced section in Encryption tab", () => {
// Fill password dialog and validate
const dialog = page.locator(".mx_InteractiveAuthDialog");
await dialog.getByRole("textbox", { name: "Password" }).fill(credentials.password);
await dialog.getByRole("textbox", { name: "Password" }).fill(credentials.password!);
await dialog.getByRole("button", { name: "Continue" }).click();
await expect(section.getByRole("button", { name: "Reset cryptographic identity" })).toBeVisible();
@@ -26,6 +26,7 @@ test.describe("Encryption tab", () => {
test.beforeEach(async ({ page, homeserver, credentials }) => {
// The bot bootstraps cross-signing, creates a key backup and sets up a recovery key
const botCredentials = { ...credentials };
// @ts-expect-error
delete botCredentials.accessToken; // use a new login for the bot
const res = await createBot(page, homeserver, botCredentials);
recoveryKey = res.recoveryKey;
@@ -71,7 +72,7 @@ test.describe("Encryption tab", () => {
"should prompt to enter the recovery key when the secrets are not cached locally",
{ tag: "@screenshot" },
async ({ page, app, util }) => {
await verifySession(app, recoveryKey.encodedPrivateKey);
await verifySession(app, recoveryKey.encodedPrivateKey!);
// We need to delete the cached secrets
await deleteCachedSecrets(page);
@@ -104,7 +105,7 @@ test.describe("Encryption tab", () => {
app,
util,
}) => {
await verifySession(app, recoveryKey.encodedPrivateKey);
await verifySession(app, recoveryKey.encodedPrivateKey!);
// We need to delete the cached secrets
await deleteCachedSecrets(page);
@@ -120,7 +121,7 @@ test.describe("Encryption tab", () => {
});
test("should warn before turning off key storage", { tag: "@screenshot" }, async ({ page, app, util }) => {
await verifySession(app, recoveryKey.encodedPrivateKey);
await verifySession(app, recoveryKey.encodedPrivateKey!);
await util.openEncryptionTab();
await page.getByRole("switch", { name: "Allow key storage" }).click();
@@ -55,7 +55,7 @@ class Helpers {
async enterRecoveryKey(recoveryKey: GeneratedSecretStorageKey) {
// Fill the recovery key
const dialog = this.page.locator(".mx_Dialog");
await dialog.getByRole("textbox").fill(recoveryKey.encodedPrivateKey);
await dialog.getByRole("textbox").fill(recoveryKey.encodedPrivateKey!);
await dialog.getByRole("button", { name: "Continue" }).click();
}
@@ -18,6 +18,7 @@ test.describe("Recovery section in Encryption tab", () => {
test.beforeEach(async ({ page, homeserver, credentials }) => {
// The bot bootstraps cross-signing, creates a key backup and sets up a recovery key
const botCredentials = { ...credentials };
// @ts-expect-error
delete botCredentials.accessToken; // use a new login for the bot
const res = await createBot(page, homeserver, botCredentials);
recoveryKey = res.recoveryKey;
@@ -27,7 +28,7 @@ test.describe("Recovery section in Encryption tab", () => {
"should change the recovery key",
{ tag: ["@screenshot", "@no-webkit"] },
async ({ page, app, homeserver, credentials, util, context }) => {
await verifySession(app, recoveryKey.encodedPrivateKey);
await verifySession(app, recoveryKey.encodedPrivateKey!);
const dialog = await util.openEncryptionTab();
// The user can only change the recovery key
@@ -54,7 +55,7 @@ test.describe("Recovery section in Encryption tab", () => {
);
test("should setup the recovery key", { tag: ["@screenshot", "@no-webkit"] }, async ({ page, app, util }) => {
await verifySession(app, recoveryKey.encodedPrivateKey);
await verifySession(app, recoveryKey.encodedPrivateKey!);
await util.removeSecretStorageDefaultKeyId();
// The key backup is deleted and the user needs to set it up
@@ -54,6 +54,6 @@ test.describe("General room settings tab", () => {
const dialogBoundingBox = await page.locator(".mx_Dialog").boundingBox();
const inputBoundingBox = await settings.locator("#canonicalAlias").boundingBox();
// Assert that the width of the select element is less than that of .mx_Dialog div.
expect(inputBoundingBox.width).toBeLessThan(dialogBoundingBox.width);
expect(inputBoundingBox!.width).toBeLessThan(dialogBoundingBox!.width);
});
});
@@ -25,7 +25,7 @@ const test = base.extend<{
joinedBot: async ({ app, bot, testRoom }, use) => {
const roomId = testRoom.roomId;
await bot.prepareClient();
const bobUserId = await bot.evaluate((client) => client.getUserId());
const bobUserId = await bot.evaluate((client) => client.getSafeUserId());
await app.client.evaluate(
async (client, { bobUserId, roomId }) => {
await client.invite(roomId, bobUserId);
@@ -230,7 +230,7 @@ test.describe("Sliding Sync", () => {
joinedBot: bot,
testRoom,
}) => {
const clientUserId = await app.client.evaluate((client) => client.getUserId());
const clientUserId = await app.client.evaluate((client) => client.getSafeUserId());
// invite bot into 3 rooms:
// - roomJoin: will join this room
@@ -48,7 +48,7 @@ function spaceChildInitialState(
serverName: string,
roomId: string,
order?: string,
): ICreateRoomOpts["initial_state"]["0"] {
): NonNullable<ICreateRoomOpts["initial_state"]>[number] {
return {
type: "m.space.child",
state_key: roomId,
@@ -240,7 +240,7 @@ test.describe("Spaces", () => {
await shareDialog.getByRole("button", { name: "Invite people" }).click();
const otherSection = page.locator(".mx_InviteDialog_other");
await otherSection.getByRole("textbox").fill(bot.credentials.userId);
await otherSection.getByRole("textbox").fill(bot.credentials!.userId);
await otherSection.getByRole("button", { name: "Invite" }).click();
await expect(page.locator(".mx_InviteDialog_other")).not.toBeVisible();
@@ -25,9 +25,9 @@ type RoomRef = { name: string; roomId: string };
* - Invite the bot to both rooms and ensure that it has joined
*/
export const test = base.extend<{
room1Name?: string;
room1Name: string;
room1: { name: string; roomId: string };
room2Name?: string;
room2Name: string;
room2: { name: string; roomId: string };
msg: MessageBuilder;
util: Helpers;
@@ -39,7 +39,7 @@ export const test = base.extend<{
room1: async ({ room1Name: name, app, user, bot }, use) => {
const roomId = await app.client.createRoom({
name,
invite: [bot.credentials.userId],
invite: [bot.credentials!.userId],
preset: "public_chat" as Preset,
});
await bot.awaitRoomMembership(roomId);
@@ -47,12 +47,12 @@ export const test = base.extend<{
},
room2Name: "Room 2",
room2: async ({ room2Name: name, app, user, bot }, use) => {
const roomId = await app.client.createRoom({ name, invite: [bot.credentials.userId] });
const roomId = await app.client.createRoom({ name, invite: [bot.credentials!.userId] });
await bot.awaitRoomMembership(roomId);
await use({ name, roomId });
},
msg: async ({ page, app, util }, use) => {
await use(new MessageBuilder(page, app, util));
await use(new MessageBuilder());
},
util: async ({ room1, room2, page, app, bot }, use) => {
await use(new Helpers(page, app, bot));
@@ -70,12 +70,6 @@ export const test = base.extend<{
* which finds a message and then constructs a reply to it.
*/
export class MessageBuilder {
constructor(
private page: Page,
private app: ElementAppPage,
private helpers: Helpers,
) {}
/**
* Map of message content -> event.
*/
@@ -164,11 +158,7 @@ export class MessageBuilder {
* MessageBuilder.replyTo} which creates a reply based on a previous message.
*/
export abstract class MessageContentSpec {
messageFinder: MessageBuilder | null;
constructor(messageFinder: MessageBuilder = null) {
this.messageFinder = messageFinder;
}
constructor(public readonly messageFinder: MessageBuilder) {}
public abstract getContent(room: JSHandle<Room>): Promise<Record<string, unknown>>;
}
@@ -230,9 +220,9 @@ export class Helpers {
await expect(this.page.locator(".mx_ThreadView_timelinePanelWrapper")).toBeVisible();
}
async findRoomById(roomId: string): Promise<JSHandle<Room | undefined>> {
async findRoomById(roomId: string): Promise<JSHandle<Room>> {
return this.app.client.evaluateHandle((cli, roomId) => {
return cli.getRooms().find((r) => r.roomId === roomId);
return cli.getRooms().find((r) => r.roomId === roomId)!;
}, roomId);
}
@@ -97,8 +97,8 @@ test.describe("Spotlight", () => {
test.beforeEach(async ({ page, user, bot1, bot2, room1, room2, room3 }) => {
await bot1.joinRoom(room1.roomId);
await bot2.inviteUser(room2.roomId, bot1.credentials.userId);
await bot2.inviteUser(room3.roomId, bot1.credentials.userId);
await bot2.inviteUser(room2.roomId, bot1.credentials!.userId);
await bot2.inviteUser(room3.roomId, bot1.credentials!.userId);
await page.goto(`/#/room/${room1.roomId}`);
await expect(page.locator(".mx_RoomSublist_skeletonUI")).not.toBeAttached();
@@ -215,12 +215,12 @@ test.describe("Spotlight", () => {
const spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.filter(Filter.People);
await spotlight.search(bot1.credentials.displayName);
await spotlight.search(bot1.credentials!.displayName!);
const resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(1);
await expect(resultLocator.first()).toContainText(bot1.credentials.displayName);
await expect(resultLocator.first()).toContainText(bot1.credentials!.displayName!);
await resultLocator.first().click();
await expect(roomHeaderName(page)).toHaveText(bot1.credentials.displayName);
await expect(roomHeaderName(page)).toHaveText(bot1.credentials!.displayName!);
});
/**
@@ -234,30 +234,30 @@ test.describe("Spotlight", () => {
const spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.filter(Filter.People);
await spotlight.search(bot2.credentials.displayName);
await spotlight.search(bot2.credentials!.displayName!);
const resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(1);
await expect(resultLocator.first()).toContainText(bot2.credentials.displayName);
await expect(resultLocator.first()).toContainText(bot2.credentials!.displayName!);
await resultLocator.first().click();
await expect(roomHeaderName(page)).toHaveText(bot2.credentials.displayName);
await expect(roomHeaderName(page)).toHaveText(bot2.credentials!.displayName!);
});
test("should find group DMs by usernames or user ids", async ({ page, app, bot1, bot2, room1 }) => {
// First we want to share a room with both bots to ensure weve got their usernames cached
await app.client.inviteUser(room1.roomId, bot2.credentials.userId);
await app.client.inviteUser(room1.roomId, bot2.credentials!.userId);
// Starting a DM with ByteBot (will be turned into a group dm later)
let spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.filter(Filter.People);
await spotlight.search(bot2.credentials.displayName);
await spotlight.search(bot2.credentials!.displayName!);
let resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(1);
await expect(resultLocator.first()).toContainText(bot2.credentials.displayName);
await expect(resultLocator.first()).toContainText(bot2.credentials!.displayName!);
await resultLocator.first().click();
// Send first message to actually start DM
await expect(roomHeaderName(page)).toHaveText(bot2.credentials.displayName);
await expect(roomHeaderName(page)).toHaveText(bot2.credentials!.displayName!);
const locator = page.getByRole("textbox", { name: "Send a message…" });
await locator.fill("Hey!");
await locator.press("Enter");
@@ -265,7 +265,7 @@ test.describe("Spotlight", () => {
// Assert DM exists by checking for the first message and the room being in the room list
await expect(page.locator(".mx_EventTile_body").filter({ hasText: "Hey!" })).toBeAttached({ timeout: 3000 });
await expect(
page.getByTestId("room-list").getByRole("option", { name: `Open room ${bot2.credentials.displayName}` }),
page.getByTestId("room-list").getByRole("option", { name: `Open room ${bot2.credentials!.displayName}` }),
).toBeVisible();
// Invite BotBob into existing DM with ByteBot
@@ -273,11 +273,11 @@ test.describe("Spotlight", () => {
const map = client
.getAccountData("m.direct" as keyof AccountDataEvents)
?.getContent<Record<string, string[]>>();
return map[userId] ?? [];
}, bot2.credentials.userId);
return map?.[userId] ?? [];
}, bot2.credentials!.userId);
expect(dmRooms).toHaveLength(1);
const groupDmName = await app.client.evaluate((client, id) => client.getRoom(id).name, dmRooms[0]);
await app.client.inviteUser(dmRooms[0], bot1.credentials.userId);
const groupDmName = await app.client.evaluate((client, id) => client.getRoom(id)!.name, dmRooms[0]);
await app.client.inviteUser(dmRooms[0], bot1.credentials!.userId);
await expect(roomHeaderName(page).first()).toContainText(groupDmName);
await expect(
page.getByTestId("room-list").getByRole("option", { name: `Open room ${groupDmName}` }),
@@ -286,7 +286,7 @@ test.describe("Spotlight", () => {
// Search for BotBob by id, should return group DM and user
spotlight = await app.openSpotlight();
await spotlight.filter(Filter.People);
await spotlight.search(bot1.credentials.userId);
await spotlight.search(bot1.credentials!.userId);
await page.waitForTimeout(1000); // wait for the dialog to settle
resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(2);
@@ -299,7 +299,7 @@ test.describe("Spotlight", () => {
// Search for ByteBot by id, should return group DM and user
spotlight = await app.openSpotlight();
await spotlight.filter(Filter.People);
await spotlight.search(bot2.credentials.userId);
await spotlight.search(bot2.credentials!.userId);
await page.waitForTimeout(1000); // wait for the dialog to settle
resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(2);
@@ -324,11 +324,11 @@ test.describe("Spotlight", () => {
// We search for user ID to trigger the profile lookup within the dialog.
for (let i = 0; i < 2; i++) {
console.log("Iteration: " + i);
await spotlight.search(bot1.credentials.userId);
await spotlight.search(bot1.credentials!.userId);
await page.waitForTimeout(1000); // wait for the dialog to settle
const resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(1);
await expect(resultLocator.first()).toContainText(bot1.credentials.userId);
await expect(resultLocator.first()).toContainText(bot1.credentials!.userId);
}
});
@@ -336,12 +336,12 @@ test.describe("Spotlight", () => {
const spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.filter(Filter.People);
await spotlight.search(bot2.credentials.displayName);
await spotlight.search(bot2.credentials!.displayName!);
await page.waitForTimeout(3000); // wait for the dialog to settle
const resultLocator = spotlight.results;
await expect(resultLocator).toHaveCount(1);
await expect(resultLocator.first()).toContainText(bot2.credentials.displayName);
await expect(resultLocator.first()).toContainText(bot2.credentials!.displayName!);
await expect(spotlight.dialog.locator("#mx_SpotlightDialog_button_startGroupChat")).toContainText(
"Start a group chat",
@@ -351,17 +351,17 @@ test.describe("Spotlight", () => {
});
test("should close spotlight after starting a DM", async ({ page, app, bot1 }) => {
await startDM(app, page, bot1.credentials.displayName);
await startDM(app, page, bot1.credentials!.displayName!);
await expect(page.locator(".mx_SpotlightDialog")).toHaveCount(0);
});
test("should show the same user only once", async ({ page, app, bot1 }) => {
await startDM(app, page, bot1.credentials.displayName);
await startDM(app, page, bot1.credentials!.displayName!);
await page.goto("/#/home");
const spotlight = await app.openSpotlight();
await page.waitForTimeout(500); // wait for the dialog to settle
await spotlight.filter(Filter.People);
await spotlight.search(bot1.credentials.displayName);
await spotlight.search(bot1.credentials!.displayName!);
await page.waitForTimeout(3000); // wait for the dialog to settle
await expect(spotlight.dialog.locator(".mx_Spinner")).not.toBeAttached();
const resultLocator = spotlight.results;
@@ -29,7 +29,7 @@ test.describe("Threads", () => {
test("should be usable for a conversation", { tag: "@screenshot" }, async ({ page, app, bot }) => {
const roomId = await app.client.createRoom({});
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await bot.joinRoom(roomId);
await page.goto("/#/room/" + roomId);
@@ -436,7 +436,7 @@ test.describe("Threads", () => {
{ tag: ["@screenshot", "@no-firefox"] },
async ({ page, app, bot }) => {
const roomId = await app.client.createRoom({});
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await bot.joinRoom(roomId);
await page.goto("/#/room/" + roomId);
@@ -60,7 +60,7 @@ test.describe("Event List Summary", () => {
await app.client.sendMessage(roomId, "Saying something");
// When we ban the bot
await app.client.ban(roomId, bot.credentials.userId);
await app.client.ban(roomId, bot.credentials!.userId);
// Then we say that, but the name is hidden
await expect(
@@ -92,8 +92,8 @@ test.describe("Event List Summary", () => {
).toBeVisible();
// When we perform multiple actions on it
await app.client.kick(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.kick(roomId, bot.credentials!.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await bot.joinRoom(roomId);
// Then those actions are gathered into a single summary
@@ -126,9 +126,9 @@ test.describe("Event List Summary", () => {
).toBeVisible();
// When we perform multiple actions on it, including a ban
await app.client.ban(roomId, bot.credentials.userId);
await app.client.unban(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.ban(roomId, bot.credentials!.userId);
await app.client.unban(roomId, bot.credentials!.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await bot.joinRoom(roomId);
// Then those actions are gathered into a single summary, with the name hidden
@@ -158,7 +158,7 @@ test.describe("Event List Summary", () => {
autoAcceptInvites: false,
});
await bot2.prepareClient();
await app.client.inviteUser(roomId, bot2.credentials.userId);
await app.client.inviteUser(roomId, bot2.credentials!.userId);
await app.client.sendMessage(roomId, "I invited MyBot2...");
await bot.joinRoom(roomId);
await bot2.joinRoom(roomId);
@@ -169,10 +169,10 @@ test.describe("Event List Summary", () => {
).toBeVisible();
// When we perform multiple actions on both bots
await app.client.kick(roomId, bot.credentials.userId);
await app.client.kick(roomId, bot2.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot2.credentials.userId);
await app.client.kick(roomId, bot.credentials!.userId);
await app.client.kick(roomId, bot2.credentials!.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await app.client.inviteUser(roomId, bot2.credentials!.userId);
await bot.joinRoom(roomId);
await bot2.joinRoom(roomId);
@@ -227,7 +227,7 @@ test.describe("Event List Summary", () => {
autoAcceptInvites: false,
});
await bot2.prepareClient();
await app.client.inviteUser(roomId, bot2.credentials.userId);
await app.client.inviteUser(roomId, bot2.credentials!.userId);
await app.client.sendMessage(roomId, "I invited MyBot2...");
await bot.joinRoom(roomId);
await bot2.joinRoom(roomId);
@@ -238,11 +238,11 @@ test.describe("Event List Summary", () => {
).toBeVisible();
// When we ban bot1 but not bot2
await app.client.ban(roomId, bot.credentials.userId);
await app.client.unban(roomId, bot.credentials.userId);
await app.client.kick(roomId, bot2.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot2.credentials.userId);
await app.client.ban(roomId, bot.credentials!.userId);
await app.client.unban(roomId, bot.credentials!.userId);
await app.client.kick(roomId, bot2.credentials!.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await app.client.inviteUser(roomId, bot2.credentials!.userId);
await bot.joinRoom(roomId);
await bot2.joinRoom(roomId);
@@ -318,7 +318,7 @@ async function setupRoom(app: ElementAppPage, homeserver: StartedHomeserverConta
autoAcceptInvites: false,
});
await bot.prepareClient();
await app.client.inviteUser(roomId, bot.credentials.userId);
await app.client.inviteUser(roomId, bot.credentials!.userId);
await app.client.sendMessage(roomId, "I invited MyBot...");
return { bot, roomId };
@@ -336,11 +336,11 @@ async function replaceBotIds(page: Page, bot: Bot, bot2?: Bot) {
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
while (walker.nextNode()) {
const node = walker.currentNode;
node.textContent = node.textContent.replaceAll(bot1UserId, "<<replaced_bot1_id>>");
node.textContent = node.textContent!.replaceAll(bot1UserId, "<<replaced_bot1_id>>");
node.textContent = node.textContent.replaceAll(bot2UserId, "<<replaced_bot2_id>>");
}
}
},
[bot.credentials.userId, bot2?.credentials?.userId ?? "no_bot_2_to_replace"],
[bot.credentials!.userId, bot2?.credentials?.userId ?? "no_bot_2_to_replace"],
);
}
@@ -48,7 +48,7 @@ const expectAvatar = async (cli: Client, e: Locator, avatarUrl: string): Promise
},
{ avatarUrl, size, resizeMethod: AVATAR_RESIZE_METHOD },
);
await expect(e.locator(".mx_BaseAvatar img")).toHaveAttribute("src", url);
await expect(e.locator(".mx_BaseAvatar img")).toHaveAttribute("src", url!);
};
const sendEvent = async (client: Client, roomId: string, html = false): Promise<ISendEventResponse> => {
@@ -917,7 +917,7 @@ test.describe("Timeline", () => {
const bot = new Bot(page, homeserver, {});
await bot.prepareClient();
await app.client.inviteUser(room.roomId, bot.credentials.userId);
await app.client.inviteUser(room.roomId, bot.credentials!.userId);
await sendImage(bot, room.roomId, NEW_AVATAR);
await app.timeline.scrollToBottom();
@@ -936,7 +936,7 @@ test.describe("Timeline", () => {
const bot = new Bot(page, homeserver, {});
await bot.prepareClient();
await app.client.inviteUser(room.roomId, bot.credentials.userId);
await app.client.inviteUser(room.roomId, bot.credentials!.userId);
const upload = await bot.uploadContent(VIDEO_FILE, { name: "bbb.webm", type: "video/webm" });
await bot.sendEvent(room.roomId, null, "m.room.message" as EventType, {
@@ -970,7 +970,7 @@ test.describe("Timeline", () => {
autoAcceptInvites: false,
});
await bot.prepareClient();
await app.client.inviteUser(room.roomId, bot.credentials.userId);
await app.client.inviteUser(room.roomId, bot.credentials!.userId);
await bot.joinRoom(room.roomId);
await bot.sendMessage(room.roomId, messageFromSender);
@@ -1100,7 +1100,7 @@ test.describe("Timeline", () => {
autoAcceptInvites: false,
});
await bot.prepareClient();
await app.client.inviteUser(room.roomId, bot.credentials.userId);
await app.client.inviteUser(room.roomId, bot.credentials!.userId);
await bot.joinRoom(room.roomId);
// Make sure the bot joined the room
@@ -1228,7 +1228,7 @@ test.describe("Timeline", () => {
// Create another room with a long name, invite the bot, and open the room
const testRoomId = await app.client.createRoom({ name: LONG_STRING });
await app.client.inviteUser(testRoomId, bot.credentials.userId);
await app.client.inviteUser(testRoomId, bot.credentials!.userId);
await bot.joinRoom(testRoomId);
await page.goto(`/#/room/${testRoomId}`);
@@ -25,7 +25,7 @@ test.describe("User Menu", () => {
await page.getByRole("button", { name: "User menu", exact: true }).click();
const menu = page.getByRole("menu");
await expect(menu.getByText(user.displayName)).toBeVisible();
await expect(menu.getByText(user.displayName!)).toBeVisible();
await expect(menu.getByText(user.userId)).toBeVisible();
await expect(menu).toMatchScreenshot("user-menu.png", screenshotOptions(page));
});
@@ -15,10 +15,12 @@ test.describe("UserView", () => {
});
test("should render the user view as expected", { tag: "@screenshot" }, async ({ page, homeserver, user, bot }) => {
await page.goto(`/#/user/${bot.credentials.userId}`);
await page.goto(`/#/user/${bot.credentials!.userId}`);
const rightPanel = page.locator("#mx_RightPanel");
await expect(rightPanel.getByRole("heading", { name: bot.credentials.displayName, exact: true })).toBeVisible();
await expect(
rightPanel.getByRole("heading", { name: bot.credentials!.displayName, exact: true }),
).toBeVisible();
await expect(rightPanel).toMatchScreenshot("user-info.png", {
mask: [page.locator(".mx_UserInfo_profile_mxid")],
css: `
+1 -1
View File
@@ -40,7 +40,7 @@ export async function waitForRoom(
await client.evaluateHandle(
(matrixClient, { roomId, predicateId }) => {
return new Promise<Room>((resolve) => {
const room = matrixClient.getRoom(roomId);
const room = matrixClient.getRoom(roomId)!;
if ((<any>window)[predicateId](room)) {
resolve(room);
@@ -63,7 +63,7 @@ async function sendRTCState(bot: Bot, roomId: string, notification?: "ring" | "n
},
"scope": "m.room",
},
`_@${bot.credentials.userId}_OiDFxsZrjz_m.call`,
`_@${bot.credentials!.userId}_OiDFxsZrjz_m.call`,
);
if (!notification) {
return;
@@ -140,7 +140,7 @@ test.describe("Element Call", () => {
await charlie.prepareClient();
const roomId = await app.client.createRoom({
name: "TestRoom",
invite: [bot.credentials.userId, charlie.credentials.userId],
invite: [bot.credentials!.userId, charlie.credentials!.userId],
});
await use({ roomId });
},
@@ -155,7 +155,7 @@ test.describe("Element Call", () => {
const frameUrlStr = await page.locator("iframe").getAttribute("src");
expect(frameUrlStr).toBeDefined();
// Ensure we set the correct parameters for ECall.
const url = new URL(frameUrlStr);
const url = new URL(frameUrlStr!);
const hash = new URLSearchParams(url.hash.slice(1));
assertCommonCallParameters(url.searchParams, hash, user, room);
expect(hash.get("intent")).toEqual("start_call");
@@ -180,7 +180,7 @@ test.describe("Element Call", () => {
const frameUrlStr = await page.locator("iframe").getAttribute("src");
expect(frameUrlStr).toBeDefined();
const url = new URL(frameUrlStr);
const url = new URL(frameUrlStr!);
const hash = new URLSearchParams(url.hash.slice(1));
assertCommonCallParameters(url.searchParams, hash, user, room);
expect(hash.get("intent")).toEqual("start_call");
@@ -192,7 +192,7 @@ test.describe("Element Call", () => {
await app.viewRoomById(room.roomId);
// Allow bob to create a call
await expect(page.getByText("Bob and one other were invited and joined")).toBeVisible();
await app.client.setPowerLevel(room.roomId, bot.credentials.userId, 50);
await app.client.setPowerLevel(room.roomId, bot.credentials!.userId, 50);
// Fake a start of a call
await sendRTCState(bot, room.roomId, undefined, callType === "voice" ? "audio" : "video");
const button = page.getByTestId("join-call-button");
@@ -205,7 +205,7 @@ test.describe("Element Call", () => {
await button.click();
const frameUrlStr = await page.locator("iframe").getAttribute("src");
expect(frameUrlStr).toBeDefined();
const url = new URL(frameUrlStr);
const url = new URL(frameUrlStr!);
const hash = new URLSearchParams(url.hash.slice(1));
assertCommonCallParameters(url.searchParams, hash, user, room);
@@ -237,7 +237,7 @@ test.describe("Element Call", () => {
await app.viewRoomById(room.roomId);
// Allow bob to create a call
await expect(page.getByText("Bob and one other were invited and joined")).toBeVisible();
await app.client.setPowerLevel(room.roomId, bot.credentials.userId, 50);
await app.client.setPowerLevel(room.roomId, bot.credentials!.userId, 50);
// Fake a start of a call
await sendRTCState(bot, room.roomId, "notification", "video");
const toast = page.locator(".mx_Toast_toast");
@@ -255,7 +255,7 @@ test.describe("Element Call", () => {
await button.click();
const frameUrlStr = await page.locator("iframe").getAttribute("src");
expect(frameUrlStr).toBeDefined();
const url = new URL(frameUrlStr);
const url = new URL(frameUrlStr!);
const hash = new URLSearchParams(url.hash.slice(1));
assertCommonCallParameters(url.searchParams, hash, user, room);
@@ -272,7 +272,7 @@ test.describe("Element Call", () => {
await app.viewRoomById(room.roomId);
// Allow bob to create a call
await expect(page.getByText("Bob and one other were invited and joined")).toBeVisible();
await app.client.setPowerLevel(room.roomId, bot.credentials.userId, 50);
await app.client.setPowerLevel(room.roomId, bot.credentials!.userId, 50);
// Fake a start of a call
await sendRTCState(bot, room.roomId, "notification", "audio");
const toast = page.locator(".mx_Toast_toast");
@@ -284,7 +284,7 @@ test.describe("Element Call", () => {
await button.click();
const frameUrlStr = await page.locator("iframe").getAttribute("src");
expect(frameUrlStr).toBeDefined();
const url = new URL(frameUrlStr);
const url = new URL(frameUrlStr!);
const hash = new URLSearchParams(url.hash.slice(1));
assertCommonCallParameters(url.searchParams, hash, user, room);
@@ -299,11 +299,11 @@ test.describe("Element Call", () => {
room: async ({ page, app, user, bot }, use) => {
const roomId = await app.client.createRoom({
preset: "trusted_private_chat" as Preset.TrustedPrivateChat,
invite: [bot.credentials.userId],
invite: [bot.credentials!.userId],
});
await bot.awaitRoomMembership(roomId);
await app.client.setAccountData("m.direct" as EventType.Direct, {
[bot.credentials.userId]: [roomId],
[bot.credentials!.userId]: [roomId],
});
await use({ roomId });
},
@@ -318,7 +318,7 @@ test.describe("Element Call", () => {
const frameUrlStr = await page.locator("iframe").getAttribute("src");
expect(frameUrlStr).toBeDefined();
const url = new URL(frameUrlStr);
const url = new URL(frameUrlStr!);
const hash = new URLSearchParams(url.hash.slice(1));
assertCommonCallParameters(url.searchParams, hash, user, room);
expect(hash.get("intent")).toEqual("start_call_dm");
@@ -336,7 +336,7 @@ test.describe("Element Call", () => {
const frameUrlStr = await page.locator("iframe").getAttribute("src");
expect(frameUrlStr).toBeDefined();
const url = new URL(frameUrlStr);
const url = new URL(frameUrlStr!);
const hash = new URLSearchParams(url.hash.slice(1));
assertCommonCallParameters(url.searchParams, hash, user, room);
expect(hash.get("intent")).toEqual("start_call_dm");
@@ -370,7 +370,7 @@ test.describe("Element Call", () => {
await button.click();
const frameUrlStr = await page.locator("iframe").getAttribute("src");
expect(frameUrlStr).toBeDefined();
const url = new URL(frameUrlStr);
const url = new URL(frameUrlStr!);
const hash = new URLSearchParams(url.hash.slice(1));
assertCommonCallParameters(url.searchParams, hash, user, room);
@@ -410,7 +410,7 @@ test.describe("Element Call", () => {
await button.click();
const frameUrlStr = await page.locator("iframe").getAttribute("src");
expect(frameUrlStr).toBeDefined();
const url = new URL(frameUrlStr);
const url = new URL(frameUrlStr!);
const hash = new URLSearchParams(url.hash.slice(1));
assertCommonCallParameters(url.searchParams, hash, user, room);
@@ -444,7 +444,7 @@ test.describe("Element Call", () => {
await button.click();
const frameUrlStr = await page.locator("iframe").getAttribute("src");
expect(frameUrlStr).toBeDefined();
const url = new URL(frameUrlStr);
const url = new URL(frameUrlStr!);
const hash = new URLSearchParams(url.hash.slice(1));
assertCommonCallParameters(url.searchParams, hash, user, room);
@@ -477,7 +477,7 @@ test.describe("Element Call", () => {
const frameUrlStr = await page.locator("iframe").getAttribute("src");
expect(frameUrlStr).toBeDefined();
// Ensure we set the correct parameters for ECall.
const url = new URL(frameUrlStr);
const url = new URL(frameUrlStr!);
const hash = new URLSearchParams(url.hash.slice(1));
assertCommonCallParameters(url.searchParams, hash, user, { roomId });
expect(hash.get("intent")).toEqual("join_existing");
@@ -494,7 +494,7 @@ test.describe("Element Call", () => {
await charlie.prepareClient();
const roomId = await app.client.createRoom({
name: "TestRoom",
invite: [bot.credentials.userId, charlie.credentials.userId],
invite: [bot.credentials!.userId, charlie.credentials!.userId],
});
await app.client.createRoom({
name: "OtherRoom",
@@ -514,7 +514,7 @@ test.describe("Element Call", () => {
const iframe = page.locator("iframe");
await expect(iframe).toBeVisible();
const frameUrlStr = await page.locator("iframe").getAttribute("src");
const callFrame = page.frame({ url: frameUrlStr });
const callFrame = page.frame({ url: frameUrlStr! })!;
await callFrame.getByRole("button", { name: "Join Call" }).click();
await expect(callFrame.getByText("In call", { exact: true })).toBeVisible();
@@ -608,7 +608,7 @@ test.describe("Element Call", () => {
}) => {
await app.viewRoomById(room.roomId);
await expect(page.getByText("Bob and one other were invited and joined")).toBeVisible();
await app.client.setPowerLevel(room.roomId, bot.credentials.userId, 50);
await app.client.setPowerLevel(room.roomId, bot.credentials!.userId, 50);
await sendRTCState(bot, room.roomId);
await openAndJoinCall(page, true);
@@ -654,7 +654,7 @@ test.describe("Element Call", () => {
await charlie.prepareClient();
const roomId = await app.client.createRoom({
name: "VideoRoom",
invite: [bot.credentials.userId, charlie.credentials.userId],
invite: [bot.credentials!.userId, charlie.credentials!.userId],
creation_content: {
type: "org.matrix.msc3417.call",
},
@@ -75,7 +75,7 @@ test.describe("Widget Events", () => {
}) => {
const roomId = await app.client.createRoom({
name: ROOM_NAME,
invite: [bot.credentials.userId],
invite: [bot.credentials!.userId],
});
// setup widget via state event
@@ -30,7 +30,7 @@ test.describe("Jitsi Calls", () => {
test("should be able to pop out a jitsi widget", async ({ page, app, bot, bot2, context }) => {
const roomId = await app.client.createRoom({
name: ROOM_NAME,
invite: [bot.credentials.userId, bot2.credentials.userId],
invite: [bot.credentials!.userId, bot2.credentials!.userId],
});
await bot.joinRoom(roomId);
@@ -76,19 +76,19 @@ test.describe("Widget Layout", () => {
test("manually resize the height of the top container layout", async ({ page }) => {
const iframe = page.locator('iframe[title="widget"]');
expect((await iframe.boundingBox()).height).toBeLessThan(250);
expect((await iframe.boundingBox())!.height).toBeLessThan(250);
await page.locator(".mx_AppsDrawer_resizer_container_handle").hover();
await page.mouse.down();
await page.mouse.move(0, 550);
await page.mouse.up();
expect((await iframe.boundingBox()).height).toBeGreaterThan(400);
expect((await iframe.boundingBox())!.height).toBeGreaterThan(400);
});
test("programmatically resize the height of the top container layout", async ({ page, app }) => {
const iframe = page.locator('iframe[title="widget"]');
expect((await iframe.boundingBox()).height).toBeLessThan(250);
expect((await iframe.boundingBox())!.height).toBeLessThan(250);
await app.client.sendStateEvent(
roomId,
@@ -106,6 +106,6 @@ test.describe("Widget Layout", () => {
"",
);
await expect.poll(async () => (await iframe.boundingBox()).height).toBeGreaterThan(400);
await expect.poll(async () => (await iframe.boundingBox())!.height).toBeGreaterThan(400);
});
});
@@ -56,7 +56,7 @@ async function waitForRoomWidget(client: Client, widgetId: string, roomId: strin
}
}
const room = matrixClient.getRoom(roomId);
const room = matrixClient.getRoom(roomId)!;
const startingWidgetEvents = room.currentState.getStateEvents("im.vector.modular.widgets");
if (eventsInIntendedState(startingWidgetEvents)) {
@@ -97,14 +97,14 @@ test.describe("Widget PIP", () => {
test(`should be closed on ${userRemove}`, async ({ page, app, bot, user }) => {
const roomId = await app.client.createRoom({
name: ROOM_NAME,
invite: [bot.credentials.userId],
invite: [bot.credentials!.userId],
});
// sets bot to Admin and user to Moderator
await app.client.sendStateEvent(roomId, "m.room.power_levels", {
users: {
[user.userId]: 50,
[bot.credentials.userId]: 100,
[bot.credentials!.userId]: 100,
},
});