Add playwright tests for the opendesk module

This commit is contained in:
Michael Telatynski
2025-03-12 14:20:09 +00:00
parent e5c658421c
commit 83788cc8e0
13 changed files with 389 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px"
width="789.322px" height="336.807px" viewBox="0 0 789.322 336.807" enable-background="new 0 0 789.322 336.807"
xml:space="preserve">
<path d="M8.876,7.71v321.386h23.13v7.711H0V0h32.006v7.71H8.876z"/>
<path d="M97.989,109.594v16.264h0.463c4.338-6.191,9.563-10.998,15.684-14.406c6.117-3.402,13.129-5.11,21.027-5.11
c7.588,0,14.521,1.475,20.793,4.415c6.274,2.945,11.038,8.131,14.291,15.567c3.56-5.265,8.4-9.913,14.521-13.94
c6.117-4.025,13.358-6.042,21.724-6.042c6.351,0,12.234,0.776,17.66,2.325c5.418,1.549,10.065,4.027,13.938,7.434
c3.869,3.41,6.889,7.863,9.062,13.357c2.167,5.504,3.253,12.122,3.253,19.869v80.385h-32.993v-68.074
c0-4.025-0.154-7.82-0.465-11.385c-0.313-3.56-1.161-6.656-2.555-9.293c-1.395-2.631-3.45-4.724-6.157-6.274
c-2.711-1.543-6.391-2.322-11.037-2.322s-8.403,0.896-11.269,2.671c-2.868,1.784-5.112,4.109-6.737,6.971
c-1.626,2.869-2.711,6.12-3.252,9.762c-0.545,3.638-0.814,7.318-0.814,11.035v66.91h-32.991v-67.375c0-3.562-0.081-7.087-0.23-10.57
c-0.158-3.487-0.814-6.7-1.978-9.645c-1.162-2.94-3.099-5.304-5.809-7.088c-2.711-1.775-6.699-2.671-11.965-2.671
c-1.551,0-3.603,0.349-6.156,1.048c-2.556,0.697-5.036,2.016-7.435,3.949c-2.404,1.938-4.454,4.726-6.158,8.363
c-1.705,3.642-2.556,8.402-2.556,14.287v69.701h-32.99V109.594H97.989z"/>
<path d="M271.545,127.254c3.405-5.113,7.744-9.215,13.012-12.316c5.264-3.097,11.186-5.303,17.771-6.621
c6.582-1.315,13.205-1.976,19.865-1.976c6.042,0,12.158,0.428,18.354,1.277c6.195,0.855,11.85,2.522,16.962,4.997
c5.111,2.477,9.292,5.926,12.546,10.338c3.253,4.414,4.879,10.262,4.879,17.543v62.494c0,5.428,0.31,10.611,0.931,15.567
c0.615,4.959,1.701,8.676,3.251,11.153h-33.455c-0.621-1.86-1.126-3.755-1.511-5.693c-0.39-1.933-0.661-3.908-0.813-5.923
c-5.267,5.422-11.465,9.217-18.585,11.386c-7.127,2.163-14.407,3.251-21.842,3.251c-5.733,0-11.077-0.698-16.033-2.09
c-4.958-1.395-9.293-3.562-13.01-6.51c-3.718-2.938-6.622-6.656-8.713-11.147s-3.138-9.84-3.138-16.033
c0-6.813,1.199-12.43,3.604-16.84c2.399-4.417,5.495-7.939,9.295-10.575c3.793-2.632,8.129-4.607,13.01-5.923
c4.878-1.315,9.795-2.358,14.752-3.137c4.957-0.772,9.835-1.393,14.638-1.857c4.801-0.466,9.062-1.164,12.779-2.093
c3.718-0.929,6.658-2.282,8.829-4.065c2.165-1.781,3.172-4.375,3.02-7.785c0-3.56-0.58-6.389-1.742-8.479
c-1.161-2.09-2.711-3.719-4.646-4.88c-1.937-1.161-4.183-1.936-6.737-2.325c-2.557-0.382-5.309-0.58-8.248-0.58
c-6.506,0-11.617,1.395-15.335,4.183c-3.716,2.788-5.889,7.437-6.506,13.94h-32.991C266.2,138.793,268.133,132.362,271.545,127.254z
M336.714,173.837c-2.09,0.696-4.337,1.275-6.736,1.741c-2.402,0.465-4.918,0.853-7.551,1.161c-2.635,0.313-5.268,0.698-7.899,1.163
c-2.48,0.461-4.919,1.086-7.317,1.857c-2.404,0.779-4.495,1.822-6.274,3.138c-1.784,1.317-3.216,2.985-4.3,4.994
c-1.085,2.014-1.626,4.571-1.626,7.668c0,2.94,0.541,5.422,1.626,7.431c1.084,2.017,2.558,3.604,4.416,4.765
s4.025,1.976,6.507,2.438c2.475,0.466,5.031,0.698,7.665,0.698c6.505,0,11.537-1.082,15.103-3.253
c3.561-2.166,6.192-4.762,7.899-7.785c1.702-3.019,2.749-6.072,3.137-9.174c0.384-3.097,0.58-5.576,0.58-7.434V170.93
C340.548,172.172,338.806,173.139,336.714,173.837z"/>
<path d="M461.826,109.594v22.072h-24.161v59.479c0,5.573,0.928,9.292,2.788,11.149c1.856,1.859,5.576,2.788,11.152,2.788
c1.859,0,3.638-0.076,5.343-0.232c1.703-0.152,3.33-0.388,4.878-0.696v25.557c-2.788,0.465-5.887,0.773-9.293,0.931
c-3.407,0.149-6.737,0.23-9.99,0.23c-5.111,0-9.953-0.35-14.521-1.048c-4.571-0.695-8.597-2.047-12.081-4.063
c-3.486-2.011-6.236-4.88-8.248-8.597c-2.016-3.714-3.021-8.595-3.021-14.639v-70.859h-19.98v-22.072h19.98V73.582h32.992v36.012
H461.826z"/>
<path d="M508.989,109.594v22.306h0.465c1.546-3.72,3.636-7.163,6.272-10.341c2.634-3.172,5.652-5.885,9.06-8.131
c3.405-2.242,7.047-3.985,10.923-5.228c3.868-1.237,7.898-1.859,12.081-1.859c2.168,0,4.566,0.39,7.202,1.163v30.67
c-1.551-0.312-3.41-0.584-5.576-0.814c-2.17-0.233-4.26-0.35-6.274-0.35c-6.041,0-11.152,1.01-15.332,3.021
c-4.182,2.014-7.55,4.761-10.107,8.247c-2.555,3.487-4.379,7.55-5.462,12.198c-1.083,4.645-1.625,9.682-1.625,15.102v54.133h-32.991
V109.594H508.989z"/>
<path d="M568.931,91.006V63.823h32.994v27.183H568.931z M601.925,109.594v120.117h-32.994V109.594H601.925z"/>
<path d="M619.116,109.594h37.637l21.144,31.365l20.911-31.365h36.476l-39.496,56.226l44.377,63.892h-37.64l-25.093-37.87
l-25.094,37.87H615.4l43.213-63.193L619.116,109.594z"/>
<path d="M780.444,329.096V7.71h-23.13V0h32.008v336.807h-32.008v-7.711H780.444z"/>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

@@ -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"
}
]
}
]
}
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Univention Silent Login</title>
<script type="text/javascript">
window.postMessage({
loggedIn: true,
});
</script>
</head>
<body></body>
</html>
@@ -0,0 +1,52 @@
<!--Based on https://github.com/nordeck/element-web-modules/blob/main/e2e/src/deploy/widgets/widget.html-->
<html lang="en">
<head>
<title>Demo Widget</title>
<script>
let sendEventCount = 0;
window.onmessage = async (ev) => {
if (ev.data.action === "capabilities") {
// Return the capabilities for this widget
window.parent.postMessage(
Object.assign(
{
response: {
capabilities: ["org.matrix.msc2762.receive.state_event:m.room.topic"],
},
},
ev.data,
),
"*",
);
} else if (ev.data.action === "notify_capabilities") {
// Ask for an openid token
window.parent.postMessage(
{
api: "fromWidget",
widgetId: ev.data.widgetId,
requestId: "widget-" + sendEventCount,
action: "get_openid",
data: {},
},
"*",
);
} else if (ev.data.action === "get_openid" && ev.data.response?.state === "allowed") {
// Add the userid to the heading
const { access_token } = ev.data.response;
const hsUrl = new URLSearchParams(window.location.search).get("hsUrl");
const response = await fetch(
`${hsUrl}/_matrix/federation/v1/openid/userinfo?access_token=${access_token}`,
);
const { sub } = await response.json();
const titleElement = document.getElementById("title");
titleElement.innerText = `Hello ${sub}!`;
}
};
</script>
</head>
<body>
<h1 id="title">Hello unknown!</h1>
</body>
</html>
@@ -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();
});
});
@@ -0,0 +1,5 @@
- navigation:
- link "Show portal":
- img "Portal logo"
- button "Show menu":
- img
@@ -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"
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB