/* Copyright 2024 New Vector Ltd. Copyright 2022, 2023 The Matrix.org Foundation C.I.C. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ import type { Page } from "@playwright/test"; import { test, expect } from "../../element-web-test"; import { openIntegrationManager } from "./utils"; const ROOM_NAME = "Integration Manager Test"; const USER_DISPLAY_NAME = "Alice"; const BOT_DISPLAY_NAME = "Bob"; const KICK_REASON = "Goodbye"; const INTEGRATION_MANAGER_TOKEN = "DefinitelySecret_DoNotUseThisForReal"; const INTEGRATION_MANAGER_HTML = ` Fake Integration Manager `; async function closeIntegrationManager(page: Page, integrationManagerUrl: string) { const iframe = page.frameLocator(`iframe[src*="${integrationManagerUrl}"]`); await iframe.getByRole("button", { name: "Press to close" }).click(); } async function sendActionFromIntegrationManager( page: Page, integrationManagerUrl: string, targetRoomId: string, targetUserId: string, ) { const iframe = page.frameLocator(`iframe[src*="${integrationManagerUrl}"]`); await iframe.locator("#target-room-id").fill(targetRoomId); await iframe.locator("#target-user-id").fill(targetUserId); await iframe.getByRole("button", { name: "Press to send action" }).click(); } async function clickUntilGone(page: Page, selector: string, attempt = 0) { if (attempt === 11) { throw new Error("clickUntilGone attempt count exceeded"); } await page.locator(selector).last().click(); const count = await page.locator(selector).count(); if (count > 0) { return clickUntilGone(page, selector, ++attempt); } } async function expectKickedMessage(page: Page, shouldExist: boolean) { // Expand any event summaries, we can't use a click multiple here because clicking one might de-render others // This is quite horrible but seems the most stable way of clicking 0-N buttons, // one at a time with a full re-evaluation after each click await clickUntilGone(page, ".mx_GenericEventListSummary_toggle[aria-expanded=false]"); // Check for the event message (or lack thereof) await expect(page.getByText(`${USER_DISPLAY_NAME} removed ${BOT_DISPLAY_NAME}: ${KICK_REASON}`)).toBeVisible({ visible: shouldExist, }); } test.describe("Integration Manager: Kick", () => { test.use({ displayName: "Alice", room: async ({ user, app }, use) => { const roomId = await app.client.createRoom({ name: ROOM_NAME, }); await use({ roomId }); }, botCreateOpts: { displayName: BOT_DISPLAY_NAME, autoAcceptInvites: true, }, }); let integrationManagerUrl: string; test.beforeEach(async ({ page, webserver }) => { integrationManagerUrl = webserver.start(INTEGRATION_MANAGER_HTML); await page.addInitScript( ({ token, integrationManagerUrl }) => { window.localStorage.setItem("mx_scalar_token", token); window.localStorage.setItem(`mx_scalar_token_at_${integrationManagerUrl}`, token); }, { token: INTEGRATION_MANAGER_TOKEN, integrationManagerUrl, }, ); }); test.beforeEach(async ({ page, user, app, room }) => { await app.client.setAccountData("m.widgets", { "m.integration_manager": { content: { type: "m.integration_manager", name: "Integration Manager", url: integrationManagerUrl, data: { api_url: integrationManagerUrl, }, }, id: "integration-manager", }, }); // Succeed when checking the token is valid await page.route( `${integrationManagerUrl}/account?scalar_token=${INTEGRATION_MANAGER_TOKEN}*`, async (route) => { await route.fulfill({ json: { user_id: user.userId, }, }); }, ); await app.viewRoomByName(ROOM_NAME); }); 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 expect(page.getByText(`${BOT_DISPLAY_NAME} joined the room`)).toBeVisible(); await openIntegrationManager(app); 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 expect(page.getByText(`${BOT_DISPLAY_NAME} joined the room`)).toBeVisible(); await app.client.sendStateEvent(room.roomId, "m.room.power_levels", { kick: 50, users: { [user.userId]: 0, }, }); await openIntegrationManager(app); 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 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 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 expect(page.getByText(`${BOT_DISPLAY_NAME} joined the room`)).toBeVisible(); await app.client.ban(room.roomId, targetUser.credentials.userId); await openIntegrationManager(app); 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 never a room member", async ({ page, app, bot: targetUser, room }) => { await app.viewRoomByName(ROOM_NAME); await openIntegrationManager(app); await sendActionFromIntegrationManager(page, integrationManagerUrl, room.roomId, targetUser.credentials.userId); await closeIntegrationManager(page, integrationManagerUrl); await expectKickedMessage(page, false); }); });