mirror of
https://github.com/vector-im/element-call.git
synced 2026-11-10 01:02:27 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab4f0f0f77 |
@@ -107,6 +107,9 @@ jobs:
|
||||
run: pnpm playwright install --with-deps
|
||||
- name: Run backend components
|
||||
run: |
|
||||
# Make sure the directory for the embedded build exists as Docker might
|
||||
# create it with wrong access rights otherwise.
|
||||
mkdir -p embedded/web/dist
|
||||
docker compose -f docker-compose-dev.yml -f docker-compose-playwright.yml pull
|
||||
docker compose -f docker-compose-dev.yml -f docker-compose-playwright.yml up -d
|
||||
docker ps
|
||||
|
||||
@@ -19,7 +19,12 @@ services:
|
||||
- ./backend/playwright_homeserver-othersite.yaml:/data/cfg/homeserver.yaml:Z
|
||||
element-web:
|
||||
# Pin to a SHA so that upstream cannot break our tests. Renovate handles regular updates.
|
||||
image: ghcr.io/element-hq/element-web:develop@sha256:9e2335e93a6683fff303ca6d14d0978ce21fe78ab9f6eaa4495c614006b8c3a7
|
||||
image: ghcr.io/element-hq/element-web:develop@sha256:f30be05ae9893a5f8acab96207056d1144ce436b8550b95fb60cdfa838f50699
|
||||
element-web-1:
|
||||
# Pin to a SHA so that upstream cannot break our tests. Renovate handles regular updates.
|
||||
image: ghcr.io/element-hq/element-web:develop@sha256:9e2335e93a6683fff303ca6d14d0978ce21fe78ab9f6eaa4495c614006b8c3a7
|
||||
image: ghcr.io/element-hq/element-web:develop@sha256:f30be05ae9893a5f8acab96207056d1144ce436b8550b95fb60cdfa838f50699
|
||||
volumes:
|
||||
# The other site opens the embedded package under test in place of the
|
||||
# copy its image bundles (the first site opens the dev server's app.
|
||||
# Built by the Playwright run (see build.setup.ts).
|
||||
- ./embedded/web/dist:/app/widgets/element-call:ro,Z
|
||||
|
||||
@@ -34,6 +34,10 @@ translations. The browser is Playwright's, so a fresh clone needs
|
||||
|
||||
## End-to-end
|
||||
|
||||
The Element Web instance `HOST1` opens the app the dev server serves. The instance
|
||||
`HOST2` (the other site) opens the embedded package as a host ships it. This package
|
||||
is built by the `build-embedded` setup project and mounted in `docker-compose-playwright.yml`.
|
||||
|
||||
- `playwright/*.spec.ts` — standalone, on Chromium and Firefox.
|
||||
- `playwright/widget/` — widget mode, where cross-context bugs surface.
|
||||
- `playwright/component/` — the component in a host page, via the harness on port
|
||||
|
||||
@@ -63,8 +63,15 @@ export default defineConfig({
|
||||
},
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
// Builds the embedded package into the directory the other site's
|
||||
// Element Web container serves it from (see docker-compose-playwright.yml).
|
||||
name: "build-embedded",
|
||||
testMatch: "**/widget/embedded/build.setup.ts",
|
||||
},
|
||||
{
|
||||
name: "chromium",
|
||||
dependencies: ["build-embedded"],
|
||||
testIgnore: "**/mobile/**",
|
||||
use: {
|
||||
...devices["Desktop Chrome"],
|
||||
@@ -86,6 +93,7 @@ export default defineConfig({
|
||||
},
|
||||
{
|
||||
name: "firefox",
|
||||
dependencies: ["build-embedded"],
|
||||
testIgnore: "**/mobile/**",
|
||||
use: {
|
||||
...devices["Desktop Firefox"],
|
||||
|
||||
@@ -127,13 +127,14 @@ export const widgetTest = test.extend<MyFixtures>({
|
||||
* Provide a way to add additional users within a test.
|
||||
* The returned user will be registered on the default homeserver, the name will be made unique by appending a timestamp.
|
||||
*/
|
||||
addUser: async ({ browser }, use) => {
|
||||
addUser: async ({ browser }, use, testInfo) => {
|
||||
await use(
|
||||
async (
|
||||
username: string,
|
||||
host: string = HOST1,
|
||||
): Promise<UserBaseFixture> => {
|
||||
const uniqueSuffix = Date.now();
|
||||
// Tests in parallel workers can reach this in the same millisecond.
|
||||
const uniqueSuffix = `${Date.now()}_${testInfo.parallelIndex}`;
|
||||
const { page, clientHandle, mxId } = await TestHelpers.registerUser(
|
||||
browser,
|
||||
`${username.toLowerCase()}_${uniqueSuffix}`,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { test as setup } from "@playwright/test";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { mkdirSync } from "node:fs";
|
||||
|
||||
const EMBEDDED_DIST = "embedded/web/dist";
|
||||
|
||||
setup("build the embedded package", () => {
|
||||
setup.setTimeout(10 * 60 * 1000);
|
||||
mkdirSync(EMBEDDED_DIST, { recursive: true });
|
||||
execFileSync("pnpm", ["build:embedded", "--outDir", EMBEDDED_DIST], {
|
||||
stdio: "inherit",
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { expect, type Route, test } from "@playwright/test";
|
||||
|
||||
import { widgetTest } from "../../fixtures/widget-user.ts";
|
||||
import { HOST2, TestHelpers } from "../test-helpers.ts";
|
||||
|
||||
/** Where the other site's Element Web serves the embedded package from */
|
||||
const CONFIG_JSON = "**/widgets/element-call/config.json";
|
||||
|
||||
const HOST_ANSWERS: Record<string, (route: Route) => Promise<void>> = {
|
||||
"a 404": async (route) => route.fulfill({ status: 404 }),
|
||||
"a failed fetch": async (route) => route.abort("failed"),
|
||||
"an HTML page": async (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: "text/html",
|
||||
body: "<!doctype html><title>Element</title>",
|
||||
}),
|
||||
};
|
||||
|
||||
widgetTest.skip(
|
||||
({ browserName }) => browserName === "firefox",
|
||||
"The widget suite does not run on Firefox, see the other specs here",
|
||||
);
|
||||
|
||||
for (const [answer, respond] of Object.entries(HOST_ANSWERS)) {
|
||||
widgetTest(
|
||||
`reaches the lobby when config.json gets ${answer}`,
|
||||
async ({ addUser }) => {
|
||||
test.slow();
|
||||
const { page } = await addUser("Brooks", HOST2);
|
||||
await TestHelpers.createRoom("Config Room", page);
|
||||
|
||||
let answered = 0;
|
||||
await page.route(CONFIG_JSON, async (route) => {
|
||||
answered++;
|
||||
await respond(route);
|
||||
});
|
||||
|
||||
await TestHelpers.startCallInCurrentRoom(page);
|
||||
|
||||
await expect(
|
||||
page
|
||||
.locator('iframe[title="Element Call"]')
|
||||
.contentFrame()
|
||||
.getByTestId("lobby_joinCall"),
|
||||
).toBeVisible({ timeout: 15000 });
|
||||
expect(answered).toBeGreaterThan(0);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -144,7 +144,11 @@ export class TestHelpers {
|
||||
|
||||
await this.dismissStartupToasts(page);
|
||||
|
||||
await TestHelpers.setDevToolElementCallDevUrl(page);
|
||||
// The other site's Element Web opens the embedded package it serves itself
|
||||
// (see docker-compose-playwright.yml), not the dev server's app.
|
||||
if (host !== HOST2) {
|
||||
await TestHelpers.setDevToolElementCallDevUrl(page);
|
||||
}
|
||||
|
||||
const clientHandle = await page.evaluateHandle(() =>
|
||||
window.mxMatrixClientPeg.get(),
|
||||
|
||||
@@ -108,6 +108,22 @@ describe("Config.initWith", () => {
|
||||
expect(Config.get().media_quality).toEqual(DEFAULT_CONFIG.media_quality);
|
||||
});
|
||||
|
||||
it("falls back to the defaults when config.json is not JSON", async () => {
|
||||
vi.spyOn(globalThis, "fetch").mockResolvedValue(
|
||||
new Response("<!doctype html><title>Element</title>", {
|
||||
status: 200,
|
||||
headers: { "content-type": "text/html" },
|
||||
}),
|
||||
);
|
||||
const warnSpy = vi.spyOn(logger, "warn").mockImplementation(() => {});
|
||||
Reflect.set(Config, "internalInstance", undefined);
|
||||
|
||||
await expect(Config.init()).resolves.toBeUndefined();
|
||||
|
||||
expect(Config.get().media_quality).toEqual(DEFAULT_CONFIG.media_quality);
|
||||
expect(warnSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("replaces a config initialized earlier", () => {
|
||||
Config.initWith({ ssla: "https://first.invalid/ssla" });
|
||||
Config.initWith({ ssla: "https://second.invalid/ssla" });
|
||||
|
||||
@@ -156,5 +156,10 @@ async function downloadConfig(fetchTarget: string): Promise<ConfigOptions> {
|
||||
return {};
|
||||
}
|
||||
|
||||
return response.json();
|
||||
try {
|
||||
return await response.json();
|
||||
} catch (e) {
|
||||
logger.warn(`Config at ${fetchTarget} is not JSON, using defaults`, e);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user