diff --git a/modules/opendesk/element-web/tests/fixture/app1.png b/modules/opendesk/element-web/tests/fixture/app1.png new file mode 100644 index 0000000000..2c34416b88 Binary files /dev/null and b/modules/opendesk/element-web/tests/fixture/app1.png differ diff --git a/modules/opendesk/element-web/tests/fixture/app2.png b/modules/opendesk/element-web/tests/fixture/app2.png new file mode 100644 index 0000000000..ee42954c78 Binary files /dev/null and b/modules/opendesk/element-web/tests/fixture/app2.png differ diff --git a/modules/opendesk/element-web/tests/fixture/logo.svg b/modules/opendesk/element-web/tests/fixture/logo.svg new file mode 100644 index 0000000000..90c502cacf --- /dev/null +++ b/modules/opendesk/element-web/tests/fixture/logo.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/opendesk/element-web/tests/fixture/navigation.json b/modules/opendesk/element-web/tests/fixture/navigation.json new file mode 100644 index 0000000000..79bac98d25 --- /dev/null +++ b/modules/opendesk/element-web/tests/fixture/navigation.json @@ -0,0 +1,44 @@ +{ + "categories": [ + { + "identifier": "category1", + "display_name": "Category 1", + "entries": [ + { + "identifier": "app1", + "display_name": "App 1", + "icon_url": "http://localhost:8080/app1.png", + "link": "https://example.com/app1", + "target": "_blank" + }, + { + "identifier": "app2", + "display_name": "App 2", + "icon_url": "http://localhost:8080/app2.png", + "link": "https://example.com/app2", + "target": "_blank" + } + ] + }, + { + "identifier": "category2", + "display_name": "Category 2", + "entries": [ + { + "identifier": "app3", + "display_name": "App 3", + "icon_url": "http://localhost:8080/app1.png", + "link": "https://example.com/app3", + "target": "_blank" + }, + { + "identifier": "app4", + "display_name": "App 4", + "icon_url": "http://localhost:8080/app2.png", + "link": "https://example.com/app4", + "target": "_blank" + } + ] + } + ] +} diff --git a/modules/opendesk/element-web/tests/fixture/silent-login.html b/modules/opendesk/element-web/tests/fixture/silent-login.html new file mode 100644 index 0000000000..ae80633f60 --- /dev/null +++ b/modules/opendesk/element-web/tests/fixture/silent-login.html @@ -0,0 +1,14 @@ + + + + + + Univention Silent Login + + + + diff --git a/modules/opendesk/element-web/tests/fixture/widget.html b/modules/opendesk/element-web/tests/fixture/widget.html new file mode 100644 index 0000000000..3e6a834a73 --- /dev/null +++ b/modules/opendesk/element-web/tests/fixture/widget.html @@ -0,0 +1,52 @@ + + + + Demo Widget + + + +

Hello unknown!

+ + diff --git a/modules/opendesk/element-web/tests/opendesk.spec.ts b/modules/opendesk/element-web/tests/opendesk.spec.ts new file mode 100644 index 0000000000..6598dad05a --- /dev/null +++ b/modules/opendesk/element-web/tests/opendesk.spec.ts @@ -0,0 +1,209 @@ +/* +Copyright 2025 New Vector 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 SynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers"; + +import { test, expect } from "../../../../playwright/element-web-test.ts"; + +declare module "@element-hq/element-web-module-api" { + interface Config { + "net.nordeck.element_web.module.opendesk": { + config: { + banner: { + ics_navigation_json_url: string; + ics_silent_url: string; + portal_logo_svg_url: string; + portal_url: string; + }; + custom_css_variables: { + [key: `--cpd-${string}`]: string; + }; + }; + }; + "net.nordeck.element_web.module.widget_toggles": { + config: { + types: string[]; + }; + }; + "net.nordeck.element_web.module.widget_lifecycle": { + widget_permissions: { + [url: string]: { + preload_approved: boolean; + identity_approved: boolean; + capabilities_approved: string[]; + }; + }; + }; + } +} + +test.use({ + displayName: "Timmy", + synapseConfig: async ({ synapseConfig, _homeserver: homeserver }, use) => { + // Required for widget lifecycle test + (homeserver as SynapseContainer).withConfigField("listeners[0].resources[0].names", ["client", "openid"]); + await use(synapseConfig); + }, + page: async ({ context, page, moduleDir }, use) => { + for (const path of ["logo.svg", "app1.png", "app2.png"]) { + await context.route(`/${path}*`, async (route) => { + await route.fulfill({ path: `${moduleDir}/tests/fixture/${path}` }); + }); + } + await context.route("http://localhost:8080/ics/navigation.json*", async (route) => { + await route.fulfill({ + path: `${moduleDir}/tests/fixture/navigation.json`, + contentType: "application/json", + }); + }); + await context.route("http://localhost:8080/ics/silent", async (route) => { + await route.fulfill({ path: `${moduleDir}/tests/fixture/silent-login.html`, contentType: "text/html" }); + }); + await context.route("http://localhost:8080/widget.html*", async (route) => { + await route.fulfill({ path: `${moduleDir}/tests/fixture/widget.html`, contentType: "text/html" }); + }); + + await page.goto("/"); + await use(page); + }, + config: { + "net.nordeck.element_web.module.opendesk": { + config: { + banner: { + ics_navigation_json_url: "http://localhost:8080/ics/navigation.json", + ics_silent_url: "http://localhost:8080/ics/silent", + portal_logo_svg_url: "http://localhost:8080/logo.svg", + portal_url: "https://example.com/portal", + }, + + custom_css_variables: { + "--cpd-color-text-action-accent": "purple", + }, + }, + }, + "net.nordeck.element_web.module.widget_toggles": { + config: { + types: [], + }, + }, + "net.nordeck.element_web.module.widget_lifecycle": { + widget_permissions: { + "http://localhost:8080/widget.html": { + preload_approved: true, + identity_approved: true, + capabilities_approved: ["org.matrix.msc2762.receive.state_event:m.room.topic"], + }, + }, + }, + "net.nordeck.element_web.module.guest": { + config: { + guest_user_homeserver_url: "https://matrix.local/", + }, + }, + }, +}); + +test.describe("Opendesk", () => { + test("should render top-bar", { tag: ["@screenshot"] }, async ({ page }) => { + await expect(page.getByRole("heading", { name: "Welcome to Element!" })).toBeVisible(); + await expect(page.getByLabel("Show portal")).toHaveAttribute("href", "https://example.com/portal"); + + const nav = page.locator("nav"); + await expect(nav).toMatchScreenshot(`nav_loading.png`); + // The stub silent html doesn't seem to work in Playwright so send the postMessage manually + await page.evaluate(() => { + window.postMessage({ + loggedIn: true, + }); + }); + + const trigger = page.getByLabel("Show menu"); + await expect(trigger).toBeVisible(); + + // Assert the banner looks as we expect + await expect(nav).toMatchAriaSnapshot(); + await expect(nav).toMatchScreenshot(`nav.png`); + + // Check hover styles + await trigger.hover(); + await expect(nav).toMatchScreenshot(`nav_hover.png`); + + await test.step("open menu", async () => { + await trigger.click(); + const app1 = page.getByRole("link", { name: "App 1" }); + await expect(app1).toHaveAttribute("href", "https://example.com/app1"); + await app1.hover(); + + // Assert the sidebar looks as we expect + await expect(page.getByTestId("menu-list")).toMatchAriaSnapshot(); + await expect(page).toMatchScreenshot(`menu.png`, { + // We exclude this as we don't want to assert Element's styling, only our own + css: ` + .mx_AuthPage { + opacity: 0; + } + `, + }); + }); + + await test.step("close menu", async () => { + // Assert it closes by clicking the overlay + await page.getByTestId("menu-backdrop").click(); + await expect(page.getByTestId("menu-list")).not.toBeVisible(); + }); + }); + + test("should apply custom_css_variables", async ({ page, user }) => { + await expect(page.getByRole("button", { name: "Send a Direct Message" })).toHaveCSS( + "background-color", + "rgb(128, 0, 128)", + ); // -> purple + }); + + test("should show the widget without any permission requests", async ({ page, user, homeserver }) => { + const bot = await homeserver.registerUser("bot", "password", "Bot"); + const { room_id: roomId } = await homeserver.csApi.request<{ room_id: string }>( + "POST", + "/v3/createRoom", + bot.accessToken, + { + name: "Trusted Widget", + }, + ); + await homeserver.csApi.request<{ + event_id: string; + }>("PUT", `/v3/rooms/${encodeURIComponent(roomId)}/state/im.vector.modular.widgets/1`, bot.accessToken, { + id: "1", + creatorUserId: bot.userId, + type: "custom", + name: "Trusted Widget", + url: `http://localhost:8080/widget.html?hsUrl=${encodeURIComponent(homeserver.baseUrl)}`, + }); + await homeserver.csApi.request( + "PUT", + `/v3/rooms/${encodeURIComponent(roomId)}/state/io.element.widgets.layout/`, + bot.accessToken, + { + widgets: { + "1": { + container: "top", + }, + }, + }, + ); + await homeserver.csApi.request("POST", `/v3/rooms/${encodeURIComponent(roomId)}/invite`, bot.accessToken, { + user_id: user.userId, + }); + + await page.getByText("Trusted Widget").click(); + await page.getByText("Accept").click(); + + await expect( + page.frameLocator(`iframe[title="Trusted Widget"]`).getByRole("heading", { name: `Hello ${user.userId}!` }), + ).toBeVisible(); + }); +}); diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml new file mode 100644 index 0000000000..f6b58ee014 --- /dev/null +++ b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-1-linux.yml @@ -0,0 +1,5 @@ +- navigation: + - link "Show portal": + - img "Portal logo" + - button "Show menu": + - img diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml new file mode 100644 index 0000000000..e979e85507 --- /dev/null +++ b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/Opendesk-should-render-top-bar-2-linux.yml @@ -0,0 +1,15 @@ +- list: + - listitem: + - text: Category 1 + - list: + - listitem: + - link "App 1" + - listitem: + - link "App 2" + - listitem: + - text: Category 2 + - list: + - listitem: + - link "App 3" + - listitem: + - link "App 4" diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/menu-linux.png b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/menu-linux.png new file mode 100644 index 0000000000..c4a893dd31 Binary files /dev/null and b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/menu-linux.png differ diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-hover-linux.png b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-hover-linux.png new file mode 100644 index 0000000000..c1adf808ab Binary files /dev/null and b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-hover-linux.png differ diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-linux.png b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-linux.png new file mode 100644 index 0000000000..c1adf808ab Binary files /dev/null and b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-linux.png differ diff --git a/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-loading-linux.png b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-loading-linux.png new file mode 100644 index 0000000000..f79c7ff28b Binary files /dev/null and b/modules/opendesk/element-web/tests/snapshots/opendesk.spec.ts/nav-loading-linux.png differ