Cypress test for incoming verification requests (#11123)
* Cypress: move verification tests to their own file * Remove redundant cypress test This does nothing that the verification test doesn't do better. * Factor `beginKeyVerification` call out of `doTwoWaySasVerification` ... for more flexibility. This allows us to have tests where the other side picks the verification method. * Cypress test for incoming verification requestspull/28788/head^2
parent
9f580a8680
commit
35f8c525aa
|
@ -14,11 +14,8 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { VerificationRequest } from "matrix-js-sdk/src/crypto-api";
|
|
||||||
import { HomeserverInstance } from "../../plugins/utils/homeserver";
|
import { HomeserverInstance } from "../../plugins/utils/homeserver";
|
||||||
import { handleVerificationRequest, logIntoElement, waitForVerificationRequest } from "./utils";
|
import { logIntoElement } from "./utils";
|
||||||
import { CypressBot } from "../../support/bot";
|
|
||||||
import { skipIfRustCrypto } from "../../support/util";
|
|
||||||
|
|
||||||
describe("Complete security", () => {
|
describe("Complete security", () => {
|
||||||
let homeserver: HomeserverInstance;
|
let homeserver: HomeserverInstance;
|
||||||
|
@ -46,39 +43,5 @@ describe("Complete security", () => {
|
||||||
cy.findByText("Welcome Jeff");
|
cy.findByText("Welcome Jeff");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should walk through device verification if we have a signed device", () => {
|
// see also "Verify device during login with SAS" in `verifiction.spec.ts`.
|
||||||
skipIfRustCrypto();
|
|
||||||
|
|
||||||
// create a new user, and have it bootstrap cross-signing
|
|
||||||
let botClient: CypressBot;
|
|
||||||
cy.getBot(homeserver, { displayName: "Jeff" })
|
|
||||||
.then(async (bot) => {
|
|
||||||
botClient = bot;
|
|
||||||
await bot.bootstrapCrossSigning({});
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
// now log in, in Element. We go in through the login page because otherwise the device setup flow
|
|
||||||
// doesn't get triggered
|
|
||||||
console.log("%cAccount set up; logging in user", "font-weight: bold; font-size:x-large");
|
|
||||||
logIntoElement(homeserver.baseUrl, botClient.getSafeUserId(), botClient.__cypress_password);
|
|
||||||
|
|
||||||
// we should see a prompt for a device verification
|
|
||||||
cy.findByRole("heading", { name: "Verify this device" });
|
|
||||||
const botVerificationRequestPromise = waitForVerificationRequest(botClient);
|
|
||||||
cy.findByRole("button", { name: "Verify with another device" }).click();
|
|
||||||
|
|
||||||
// accept the verification request on the "bot" side
|
|
||||||
cy.wrap(botVerificationRequestPromise).then(async (verificationRequest: VerificationRequest) => {
|
|
||||||
await handleVerificationRequest(verificationRequest);
|
|
||||||
});
|
|
||||||
|
|
||||||
// confirm that the emojis match
|
|
||||||
cy.findByRole("button", { name: "They match" }).click();
|
|
||||||
|
|
||||||
// we should get the confirmation box
|
|
||||||
cy.findByText(/You've successfully verified/);
|
|
||||||
|
|
||||||
cy.findByRole("button", { name: "Got it" }).click();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,13 +19,7 @@ import type { VerificationRequest } from "matrix-js-sdk/src/crypto-api";
|
||||||
import type { CypressBot } from "../../support/bot";
|
import type { CypressBot } from "../../support/bot";
|
||||||
import { HomeserverInstance } from "../../plugins/utils/homeserver";
|
import { HomeserverInstance } from "../../plugins/utils/homeserver";
|
||||||
import { UserCredentials } from "../../support/login";
|
import { UserCredentials } from "../../support/login";
|
||||||
import {
|
import { doTwoWaySasVerification, waitForVerificationRequest } from "./utils";
|
||||||
checkDeviceIsCrossSigned,
|
|
||||||
EmojiMapping,
|
|
||||||
handleVerificationRequest,
|
|
||||||
logIntoElement,
|
|
||||||
waitForVerificationRequest,
|
|
||||||
} from "./utils";
|
|
||||||
import { skipIfRustCrypto } from "../../support/util";
|
import { skipIfRustCrypto } from "../../support/util";
|
||||||
|
|
||||||
interface CryptoTestContext extends Mocha.Context {
|
interface CryptoTestContext extends Mocha.Context {
|
||||||
|
@ -110,27 +104,6 @@ function autoJoin(client: MatrixClient) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a VerificationRequest in a bot client, add cypress commands to:
|
|
||||||
* - wait for the bot to receive a 'verify by emoji' notification
|
|
||||||
* - check that the bot sees the same emoji as the application
|
|
||||||
*
|
|
||||||
* @param botVerificationRequest - a verification request in a bot client
|
|
||||||
*/
|
|
||||||
function doTwoWaySasVerification(botVerificationRequest: VerificationRequest): void {
|
|
||||||
// on the bot side, wait for the emojis, confirm they match, and return them
|
|
||||||
const emojiPromise = handleVerificationRequest(botVerificationRequest);
|
|
||||||
|
|
||||||
// then, check that our application shows an emoji panel with the same emojis.
|
|
||||||
cy.wrap(emojiPromise).then((emojis: EmojiMapping[]) => {
|
|
||||||
cy.get(".mx_VerificationShowSas_emojiSas_block").then((emojiBlocks) => {
|
|
||||||
emojis.forEach((emoji: EmojiMapping, index: number) => {
|
|
||||||
expect(emojiBlocks[index].textContent.toLowerCase()).to.eq(emoji[0] + emoji[1]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const verify = function (this: CryptoTestContext) {
|
const verify = function (this: CryptoTestContext) {
|
||||||
const bobsVerificationRequestPromise = waitForVerificationRequest(this.bob);
|
const bobsVerificationRequestPromise = waitForVerificationRequest(this.bob);
|
||||||
|
|
||||||
|
@ -144,7 +117,9 @@ const verify = function (this: CryptoTestContext) {
|
||||||
cy.findByRole("button", { name: "Verify by emoji", timeout: 30000 }).click();
|
cy.findByRole("button", { name: "Verify by emoji", timeout: 30000 }).click();
|
||||||
|
|
||||||
cy.wrap(bobsVerificationRequestPromise).then((request: VerificationRequest) => {
|
cy.wrap(bobsVerificationRequestPromise).then((request: VerificationRequest) => {
|
||||||
doTwoWaySasVerification(request);
|
// the bot user races with the Element user to hit the "verify by emoji" button
|
||||||
|
const verifier = request.beginKeyVerification("m.sas.v1");
|
||||||
|
doTwoWaySasVerification(verifier);
|
||||||
});
|
});
|
||||||
cy.findByRole("button", { name: "They match" }).click();
|
cy.findByRole("button", { name: "They match" }).click();
|
||||||
cy.findByText("You've successfully verified Bob!").should("exist");
|
cy.findByText("You've successfully verified Bob!").should("exist");
|
||||||
|
@ -408,68 +383,3 @@ describe("Cryptography", function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Verify own device", () => {
|
|
||||||
let aliceBotClient: CypressBot;
|
|
||||||
let homeserver: HomeserverInstance;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
skipIfRustCrypto();
|
|
||||||
cy.startHomeserver("default").then((data: HomeserverInstance) => {
|
|
||||||
homeserver = data;
|
|
||||||
|
|
||||||
// Visit the login page of the app, to load the matrix sdk
|
|
||||||
cy.visit("/#/login");
|
|
||||||
|
|
||||||
// wait for the page to load
|
|
||||||
cy.window({ log: false }).should("have.property", "matrixcs");
|
|
||||||
|
|
||||||
// Create a new device for alice
|
|
||||||
cy.getBot(homeserver, { bootstrapCrossSigning: true }).then((bot) => {
|
|
||||||
aliceBotClient = bot;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
cy.stopHomeserver(homeserver);
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Click the "Verify with another device" button, and have the bot client auto-accept it.
|
|
||||||
*
|
|
||||||
* Stores the incoming `VerificationRequest` on the bot client as `@verificationRequest`.
|
|
||||||
*/
|
|
||||||
function initiateAliceVerificationRequest() {
|
|
||||||
// alice bot waits for verification request
|
|
||||||
const promiseVerificationRequest = waitForVerificationRequest(aliceBotClient);
|
|
||||||
|
|
||||||
// Click on "Verify with another device"
|
|
||||||
cy.get(".mx_AuthPage").within(() => {
|
|
||||||
cy.findByRole("button", { name: "Verify with another device" }).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
// alice bot responds yes to verification request from alice
|
|
||||||
cy.wrap(promiseVerificationRequest).as("verificationRequest");
|
|
||||||
}
|
|
||||||
|
|
||||||
it("with SAS", function (this: CryptoTestContext) {
|
|
||||||
logIntoElement(homeserver.baseUrl, aliceBotClient.getUserId(), aliceBotClient.__cypress_password);
|
|
||||||
|
|
||||||
// Launch the verification request between alice and the bot
|
|
||||||
initiateAliceVerificationRequest();
|
|
||||||
|
|
||||||
// Handle emoji SAS verification
|
|
||||||
cy.get(".mx_InfoDialog").within(() => {
|
|
||||||
cy.get<VerificationRequest>("@verificationRequest").then((request: VerificationRequest) => {
|
|
||||||
// Handle emoji request and check that emojis are matching
|
|
||||||
doTwoWaySasVerification(request);
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.findByRole("button", { name: "They match" }).click();
|
|
||||||
cy.findByRole("button", { name: "Got it" }).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check that our device is now cross-signed
|
|
||||||
checkDeviceIsCrossSigned();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import type { ISasEvent } from "matrix-js-sdk/src/crypto/verification/SAS";
|
import type { ISasEvent } from "matrix-js-sdk/src/crypto/verification/SAS";
|
||||||
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
|
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||||
import type { VerificationRequest } from "matrix-js-sdk/src/crypto-api";
|
import type { VerificationRequest, Verifier } from "matrix-js-sdk/src/crypto-api";
|
||||||
|
|
||||||
export type EmojiMapping = [emoji: string, name: string];
|
export type EmojiMapping = [emoji: string, name: string];
|
||||||
|
|
||||||
|
@ -39,15 +39,15 @@ export function waitForVerificationRequest(cli: MatrixClient): Promise<Verificat
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically handle an incoming verification request
|
* Automatically handle a SAS verification
|
||||||
*
|
*
|
||||||
* Starts the key verification process, and, once it is accepted on the other side, confirms that the
|
* Given a verifier which has already been started, wait for the emojis to be received, blindly confirm they
|
||||||
* emojis match.
|
* match, and return them
|
||||||
*
|
*
|
||||||
* @param request - incoming verification request
|
* @param verifier - verifier
|
||||||
* @returns A promise that resolves, with the emoji list, once we confirm the emojis
|
* @returns A promise that resolves, with the emoji list, once we confirm the emojis
|
||||||
*/
|
*/
|
||||||
export function handleVerificationRequest(request: VerificationRequest): Promise<EmojiMapping[]> {
|
export function handleSasVerification(verifier: Verifier): Promise<EmojiMapping[]> {
|
||||||
return new Promise<EmojiMapping[]>((resolve) => {
|
return new Promise<EmojiMapping[]>((resolve) => {
|
||||||
const onShowSas = (event: ISasEvent) => {
|
const onShowSas = (event: ISasEvent) => {
|
||||||
// @ts-ignore VerifierEvent is a pain to get at here as we don't have a reference to matrixcs;
|
// @ts-ignore VerifierEvent is a pain to get at here as we don't have a reference to matrixcs;
|
||||||
|
@ -57,7 +57,6 @@ export function handleVerificationRequest(request: VerificationRequest): Promise
|
||||||
resolve(event.sas.emoji);
|
resolve(event.sas.emoji);
|
||||||
};
|
};
|
||||||
|
|
||||||
const verifier = request.beginKeyVerification("m.sas.v1");
|
|
||||||
// @ts-ignore as above, avoiding reference to VerifierEvent
|
// @ts-ignore as above, avoiding reference to VerifierEvent
|
||||||
verifier.on("show_sas", onShowSas);
|
verifier.on("show_sas", onShowSas);
|
||||||
verifier.verify();
|
verifier.verify();
|
||||||
|
@ -119,3 +118,24 @@ export function logIntoElement(homeserverUrl: string, username: string, password
|
||||||
cy.findByPlaceholderText("Password").type(password);
|
cy.findByPlaceholderText("Password").type(password);
|
||||||
cy.findByRole("button", { name: "Sign in" }).click();
|
cy.findByRole("button", { name: "Sign in" }).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a SAS verifier for a bot client, add cypress commands to:
|
||||||
|
* - wait for the bot to receive the emojis
|
||||||
|
* - check that the bot sees the same emoji as the application
|
||||||
|
*
|
||||||
|
* @param botVerificationRequest - a verification request in a bot client
|
||||||
|
*/
|
||||||
|
export function doTwoWaySasVerification(verifier: Verifier): void {
|
||||||
|
// on the bot side, wait for the emojis, confirm they match, and return them
|
||||||
|
const emojiPromise = handleSasVerification(verifier);
|
||||||
|
|
||||||
|
// then, check that our application shows an emoji panel with the same emojis.
|
||||||
|
cy.wrap(emojiPromise).then((emojis: EmojiMapping[]) => {
|
||||||
|
cy.get(".mx_VerificationShowSas_emojiSas_block").then((emojiBlocks) => {
|
||||||
|
emojis.forEach((emoji: EmojiMapping, index: number) => {
|
||||||
|
expect(emojiBlocks[index].textContent.toLowerCase()).to.eq(emoji[0] + emoji[1]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { VerificationRequest, Verifier } from "matrix-js-sdk/src/crypto-api/verification";
|
||||||
|
import { CypressBot } from "../../support/bot";
|
||||||
|
import { HomeserverInstance } from "../../plugins/utils/homeserver";
|
||||||
|
import { emitPromise, skipIfRustCrypto } from "../../support/util";
|
||||||
|
import { checkDeviceIsCrossSigned, doTwoWaySasVerification, logIntoElement, waitForVerificationRequest } from "./utils";
|
||||||
|
import { getToast } from "../../support/toasts";
|
||||||
|
|
||||||
|
describe("Device verification", () => {
|
||||||
|
let aliceBotClient: CypressBot;
|
||||||
|
let homeserver: HomeserverInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
skipIfRustCrypto();
|
||||||
|
cy.startHomeserver("default").then((data: HomeserverInstance) => {
|
||||||
|
homeserver = data;
|
||||||
|
|
||||||
|
// Visit the login page of the app, to load the matrix sdk
|
||||||
|
cy.visit("/#/login");
|
||||||
|
|
||||||
|
// wait for the page to load
|
||||||
|
cy.window({ log: false }).should("have.property", "matrixcs");
|
||||||
|
|
||||||
|
// Create a new device for alice
|
||||||
|
cy.getBot(homeserver, { bootstrapCrossSigning: true }).then((bot) => {
|
||||||
|
aliceBotClient = bot;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cy.stopHomeserver(homeserver);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Click the "Verify with another device" button, and have the bot client auto-accept it.
|
||||||
|
*
|
||||||
|
* Stores the incoming `VerificationRequest` on the bot client as `@verificationRequest`.
|
||||||
|
*/
|
||||||
|
function initiateAliceVerificationRequest() {
|
||||||
|
// alice bot waits for verification request
|
||||||
|
const promiseVerificationRequest = waitForVerificationRequest(aliceBotClient);
|
||||||
|
|
||||||
|
// Click on "Verify with another device"
|
||||||
|
cy.get(".mx_AuthPage").within(() => {
|
||||||
|
cy.findByRole("button", { name: "Verify with another device" }).click();
|
||||||
|
});
|
||||||
|
|
||||||
|
// alice bot responds yes to verification request from alice
|
||||||
|
cy.wrap(promiseVerificationRequest).as("verificationRequest");
|
||||||
|
}
|
||||||
|
|
||||||
|
it("Verify device during login with SAS", () => {
|
||||||
|
logIntoElement(homeserver.baseUrl, aliceBotClient.getUserId(), aliceBotClient.__cypress_password);
|
||||||
|
|
||||||
|
// Launch the verification request between alice and the bot
|
||||||
|
initiateAliceVerificationRequest();
|
||||||
|
|
||||||
|
// Handle emoji SAS verification
|
||||||
|
cy.get(".mx_InfoDialog").within(() => {
|
||||||
|
cy.get<VerificationRequest>("@verificationRequest").then((request: VerificationRequest) => {
|
||||||
|
// the bot chooses to do an emoji verification
|
||||||
|
const verifier = request.beginKeyVerification("m.sas.v1");
|
||||||
|
|
||||||
|
// Handle emoji request and check that emojis are matching
|
||||||
|
doTwoWaySasVerification(verifier);
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.findByRole("button", { name: "They match" }).click();
|
||||||
|
cy.findByRole("button", { name: "Got it" }).click();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check that our device is now cross-signed
|
||||||
|
checkDeviceIsCrossSigned();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Handle incoming verification request with SAS", () => {
|
||||||
|
logIntoElement(homeserver.baseUrl, aliceBotClient.getUserId(), aliceBotClient.__cypress_password);
|
||||||
|
|
||||||
|
/* Dismiss "Verify this device" */
|
||||||
|
cy.get(".mx_AuthPage").within(() => {
|
||||||
|
cy.findByRole("button", { name: "Skip verification for now" }).click();
|
||||||
|
cy.findByRole("button", { name: "I'll verify later" }).click();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* figure out the device id of the Element client */
|
||||||
|
let elementDeviceId: string;
|
||||||
|
cy.window({ log: false }).then((win) => {
|
||||||
|
const cli = win.mxMatrixClientPeg.safeGet();
|
||||||
|
elementDeviceId = cli.getDeviceId();
|
||||||
|
expect(elementDeviceId).to.exist;
|
||||||
|
cy.log(`Got element device id: ${elementDeviceId}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Now initiate a verification request from the *bot* device. */
|
||||||
|
let botVerificationRequest: VerificationRequest;
|
||||||
|
cy.then(() => {
|
||||||
|
async function initVerification() {
|
||||||
|
botVerificationRequest = await aliceBotClient
|
||||||
|
.getCrypto()!
|
||||||
|
.requestDeviceVerification(aliceBotClient.getUserId(), elementDeviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
cy.wrap(initVerification(), { log: false });
|
||||||
|
}).then(() => {
|
||||||
|
cy.log("Initiated verification request");
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Check the toast for the incoming request */
|
||||||
|
getToast("Verification requested").within(() => {
|
||||||
|
// it should contain the device ID of the requesting device
|
||||||
|
cy.contains(`${aliceBotClient.getDeviceId()} from `);
|
||||||
|
|
||||||
|
// Accept
|
||||||
|
cy.findByRole("button", { name: "Verify Session" }).click();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Click 'Start' to start SAS verification */
|
||||||
|
cy.findByRole("button", { name: "Start" }).click();
|
||||||
|
|
||||||
|
/* on the bot side, wait for the verifier to exist ... */
|
||||||
|
async function awaitVerifier() {
|
||||||
|
// wait for the verifier to exist
|
||||||
|
while (!botVerificationRequest.verifier) {
|
||||||
|
await emitPromise(botVerificationRequest, "change");
|
||||||
|
}
|
||||||
|
return botVerificationRequest.verifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
cy.then(() => cy.wrap(awaitVerifier())).then((verifier: Verifier) => {
|
||||||
|
// ... confirm ...
|
||||||
|
botVerificationRequest.verifier.verify();
|
||||||
|
|
||||||
|
// ... and then check the emoji match
|
||||||
|
doTwoWaySasVerification(verifier);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* And we're all done! */
|
||||||
|
cy.get(".mx_InfoDialog").within(() => {
|
||||||
|
cy.findByRole("button", { name: "They match" }).click();
|
||||||
|
cy.findByText(`You've successfully verified (${aliceBotClient.getDeviceId()})!`).should("exist");
|
||||||
|
cy.findByRole("button", { name: "Got it" }).click();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that a toast with the given title exists, and return it
|
||||||
|
*
|
||||||
|
* @param expectedTitle - Expected title of the test
|
||||||
|
* @returns a Chainable for the DOM element of the toast
|
||||||
|
*/
|
||||||
|
export function getToast(expectedTitle: string): Cypress.Chainable<JQuery> {
|
||||||
|
return cy.contains(".mx_Toast_toast h2", expectedTitle).should("exist").closest(".mx_Toast_toast");
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
/// <reference types="cypress" />
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
import "cypress-each";
|
import "cypress-each";
|
||||||
|
import EventEmitter from "events";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
@ -75,3 +76,10 @@ export function skipIfRustCrypto() {
|
||||||
export function isRustCryptoEnabled(): boolean {
|
export function isRustCryptoEnabled(): boolean {
|
||||||
return !!Cypress.env("RUST_CRYPTO");
|
return !!Cypress.env("RUST_CRYPTO");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Promise which will resolve when the given event emitter emits a given event
|
||||||
|
*/
|
||||||
|
export function emitPromise(e: EventEmitter, k: string | symbol) {
|
||||||
|
return new Promise((r) => e.once(k, r));
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue