/* Copyright 2026 Element Creations Ltd. SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ import { type Page } from "@playwright/test"; import { type Credentials } from "@element-hq/element-web-playwright-common/lib/utils/api"; import { type StartedHomeserverContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/HomeserverContainer"; import { type Container } from "@element-hq/element-web-module-api"; import { test as base, expect } from "../../playwright/element-web-test.ts"; const test = base.extend<{ // Resolver for when to respond to the navigation.json request navigationJsonResolver: PromiseWithResolvers; }>({ navigationJsonResolver: async ({}, use) => { await use(Promise.withResolvers()); }, }); const TEST_WIDGET_NAME = "Name of the test widget"; async function makeRoomWithWidgetAndGoTo( homeserver: StartedHomeserverContainer, user: Credentials, page: Page, avatarUrl?: string, ): Promise { const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( "POST", "/v3/createRoom", user.accessToken, { name: "Come on in we've got widgets", }, ); await homeserver.csApi.request<{ event_id: string; }>("PUT", `/v3/rooms/${encodeURIComponent(roomId)}/state/im.vector.modular.widgets/1`, user.accessToken, { id: "1", creatorUserId: user.userId, type: "m.custom", name: TEST_WIDGET_NAME, url: `http://localhost:8080/widget.html`, avatar_url: avatarUrl, }); await page.goto(`/#/room/${roomId}`); return roomId; } async function moveWidgetToContainer( homeserver: StartedHomeserverContainer, user: Credentials, roomId: string, container: Container, ): Promise { await homeserver.csApi.request( "PUT", `/v3/user/${encodeURIComponent(user.userId)}/rooms/${encodeURIComponent(roomId)}/account_data/im.vector.web.settings`, user.accessToken, { "Widgets.layout": { widgets: { "1": { container }, }, }, }, ); } test.describe("widget-toggles", () => { test.use({ displayName: "Timmy", page: async ({ context, page, moduleDir }, use) => { await context.route("http://localhost:8080/widget.html*", async (route) => { await route.fulfill({ path: `${moduleDir}/e2e/fixture/widget.html`, contentType: "text/html" }); }); await context.route("http://localhost:8080/wigeon.png", async (route) => { await route.fulfill({ path: `${moduleDir}/e2e/fixture/wigeon.png`, contentType: "image/png" }); }); await page.goto("/"); await use(page); }, }); test("should error if config is missing", async ({ page }) => { await expect(page.getByText("Your Element is misconfigured")).toBeVisible(); await expect(page.getByText("Errors in module configuration")).toBeVisible(); // We don't take a screenshot as we don't want to assert Element's styling, only our own }); test.describe("with correct config", () => { test.use({ config: { "io.element.element-web-modules.widget-toggles": { types: ["m.custom"], }, }, }); test( "should render 'show' button for widget not in top", { tag: ["@screenshot"] }, async ({ homeserver, page, user }) => { await makeRoomWithWidgetAndGoTo(homeserver, user, page); await expect(page.getByRole("button", { name: "Show " + TEST_WIDGET_NAME })).toBeVisible(); }, ); test( "should render 'hide' button for widget in top", { tag: ["@screenshot"] }, async ({ homeserver, page, user }) => { const roomId = await makeRoomWithWidgetAndGoTo(homeserver, user, page); await moveWidgetToContainer(homeserver, user, roomId, "top"); await expect(page.getByRole("button", { name: "Hide " + TEST_WIDGET_NAME })).toBeVisible(); }, ); test("should move widget to top when 'show' button is clicked", async ({ homeserver, page, user }) => { await makeRoomWithWidgetAndGoTo(homeserver, user, page); await page.getByRole("button", { name: "Show " + TEST_WIDGET_NAME }).click(); await expect(page.getByRole("button", { name: "Hide " + TEST_WIDGET_NAME })).toBeVisible(); await expect(page.locator('iframe[title="Name of the test widget"]')).toBeVisible(); }); test("should move widget to left when 'hide' button is clicked", async ({ homeserver, page, user }) => { const roomId = await makeRoomWithWidgetAndGoTo(homeserver, user, page); await moveWidgetToContainer(homeserver, user, roomId, "top"); await expect(page.locator('iframe[title="Name of the test widget"]')).toBeVisible(); await page.getByRole("button", { name: "Hide " + TEST_WIDGET_NAME }).click(); await expect(page.locator('iframe[title="Name of the test widget"]')).not.toBeVisible(); }); test("uses widget icon for button image if present", async ({ homeserver, page, user }) => { await makeRoomWithWidgetAndGoTo(homeserver, user, page, "mxc://fakehomeserver/fake_content_id"); await expect( page.getByRole("button", { name: "Show " + TEST_WIDGET_NAME }).getByRole("img"), ).toHaveAttribute("src", /\/_matrix\/media\/v3\/download\/fakehomeserver\/fake_content_id$/); }); test( "uses built in icon for widgets with no avatar", { tag: ["@screenshot"] }, async ({ homeserver, page, user }) => { await makeRoomWithWidgetAndGoTo(homeserver, user, page); await expect(page.getByRole("button", { name: "Show " + TEST_WIDGET_NAME })).toMatchScreenshot( "widget-toggle-button-default-icon.png", ); }, ); }); });