Remove toasts fixture from playwright-common: use rejectToast etc. directly. (#33520)

This commit is contained in:
Andy Balaam
2026-05-18 13:40:02 +01:00
committed by GitHub
parent 5156e88c9d
commit 714bc20718
3 changed files with 3 additions and 166 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@element-hq/element-web-playwright-common",
"type": "module",
"version": "4.1.0",
"version": "5.0.0",
"license": "SEE LICENSE IN README.md",
"repository": {
"type": "git",
@@ -1,163 +0,0 @@
/*
* Copyright 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
import { expect, type Locator, type Page } from "@playwright/test";
// We want to avoid using `mergeTests` in index.ts because it drops useful type
// information about the fixtures. Instead, we add `services` into our fixture
// suite by using its `test` as a base, so that there is a linear hierarchy.
import { test as base } from "./services.js";
// This fixture provides convenient handling of Element Web's toasts.
export const test = base.extend<{
/**
* Convenience functions for handling toasts.
*/
toasts: Toasts;
}>({
toasts: async ({ page }, use) => {
const toasts = new Toasts(page);
await use(toasts);
},
});
class Toasts {
public constructor(public readonly page: Page) {}
/**
* Assert that no toasts exist.
*/
public async assertNoToasts(): Promise<void> {
await expect(this.page.locator(".mx_Toast_toast")).not.toBeVisible();
}
/**
* Return the toast with the supplied title. Fail or return null if it does
* not exist.
*
* If `required` is false, you should supply a relatively short `timeout`
* (e.g. 2000, meaning 2 seconds) to prevent your test taking too long.
*
* @param title - Expected title of the toast.
* @param timeout - Time in ms before we give up and decide the toast does
* not exist. If `required` is true, defaults to `timeout`
* in `TestConfig.expect`. Otherwise, defaults to 2000 (2
* seconds).
* @param required - If true, fail the test (throw an exception) if the
* toast is not visible. Otherwise, just return null if
* the toast is not visible.
* @returns the Locator for the matching toast, or null if it is not
* visible. (null will only be returned if `required` is false.)
*/
public async getToast(title: string, timeout?: number, required?: true): Promise<Locator>;
public async getToast(title: string, timeout: number | undefined, required: false): Promise<Locator | null>;
public async getToast(title: string, timeout?: number, required = true): Promise<Locator | null> {
const toast = this.page.locator(".mx_Toast_toast", { hasText: title }).first();
if (required) {
await expect(toast).toBeVisible({ timeout });
return toast;
} else {
// If we don't set a timeout, waitFor will wait forever. Since
// required is false, we definitely don't want to wait forever.
timeout = timeout ?? 2000;
try {
await toast.waitFor({ state: "visible", timeout });
return toast;
} catch {
return null;
}
}
}
/**
* Accept the toast with the supplied title, or fail if it does not exist.
*
* Only works if this toast is at the top of the stack of toasts.
*
* @param title - Expected title of the toast.
*/
public async acceptToast(title: string): Promise<void> {
return await clickToastButton(this, title, "primary");
}
/**
* Accept the toast with the supplied title, if it exists, or return after 2
* seconds if it is not found.
*
* Only works if this toast is at the top of the stack of toasts.
*
* @param title - Expected title of the toast.
*/
public async acceptToastIfExists(title: string): Promise<void> {
return await clickToastButton(this, title, "primary", 2000, false);
}
/**
* Reject the toast with the supplied title, or fail if it does not exist.
*
* Only works if this toast is at the top of the stack of toasts.
*
* @param title - Expected title of the toast.
*/
public async rejectToast(title: string): Promise<void> {
return await clickToastButton(this, title, "secondary");
}
/**
* Reject the toast with the supplied title, if it exists, or return after 2
* seconds if it is not found.
*
* Only works if this toast is at the top of the stack of toasts.
*
* @param title - Expected title of the toast.
*/
public async rejectToastIfExists(title: string): Promise<void> {
return await clickToastButton(this, title, "secondary", 2000, false);
}
}
/**
* Find the toast with the supplied title and click a button on it.
*
* Only works if this toast is at the top of the stack of toasts.
*
* If `required` is false, you should supply a relatively short `timeout`
* (e.g. 2000, meaning 2 seconds) to prevent your test taking too long.
*
* @param toasts - A Toasts instance.
* @param title - Expected title of the toast.
* @param button - Which button to click on the toast. Allowed values are
* "primary", which will accept the toast, or "secondary",
* which will reject it.
* @param timeout - Time in ms before we give up and decide the toast does
* not exist. If `required` is true, defaults to `timeout`
* in `TestConfig.expect`. Otherwise, defaults to 2000 (2
* seconds).
* @param required - If true, fail the test (throw an exception) if the
* toast is not visible. Otherwise, just return after
* `timeout` if the toast is not visible.
*/
async function clickToastButton(
toasts: Toasts,
title: string,
button: "primary" | "secondary",
timeout?: number,
required = true,
): Promise<void> {
let toast: Locator | null;
if (required) {
toast = await toasts.getToast(title, timeout, true);
} else {
toast = await toasts.getToast(title, timeout, false);
}
if (toast) {
await toast.locator(`.mx_Toast_buttons button[data-kind="${button}"]`).click();
}
}
@@ -10,9 +10,9 @@ import { type Page } from "@playwright/test";
import { sample, uniqueId } from "lodash-es";
// We want to avoid using `mergeTests` in index.ts because it drops useful type
// information about the fixtures. Instead, we add `toasts` into our fixture
// information about the fixtures. Instead, we add `services` into our fixture
// suite by using its `test` as a base, so that there is a linear hierarchy.
import { test as base } from "./toasts.js";
import { test as base } from "./services.js";
import { type Credentials } from "../utils/api.js";
/** Adds an initScript to the given page which will populate localStorage appropriately so that Element will use the given credentials. */