Add playwright tests for the opendesk module
|
After Width: | Height: | Size: 1.6 KiB |
|
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"
|
||||
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.2 KiB |