You've already forked element-web
mirror of
https://github.com/element-hq/element-web.git
synced 2026-10-24 23:19:26 +03:00
* Add an end-to-end Element Call test with a real MatrixRTC backend Element Web's Playwright coverage of Element Call has only ever used a stand-in: `sample-files/fake-element-call.html`, a page that does just enough for the widget plumbing to be exercised. Nothing in the suite has ever carried media. This adds one real call between two users, through the Element Call the build under test ships (`@element-hq/element-call-embedded`, copied to `widgets/element-call/`), against a real MatrixRTC backend: - `testcontainers/livekit.ts` — a LiveKit SFU advertising loopback so the browser reaches it through published ports on macOS as well as Linux. - `testcontainers/lk-jwt-service.ts` — the authorisation service, validating OpenID tokens over Synapse's federation API. - `testcontainers/matrix-rtc.ts` — starts the two and produces the Synapse configuration that announces the transport (MSC4143) and enables the MSCs Element Call needs. - `services.ts` — a `matrixRTC` worker option turning all of that on. `routeConfigJson` no longer hard-codes the dev server's origin: it resolves `config.json` against the context's `baseURL`, so a `BASE_URL` other than the default works, and other origins a test loads keep their own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Make Renovate actually see the new testcontainer images The custom manager's `depName` group is `[^@]+`, which matches newlines and quotes, so it is only bounded by the next `@` in the file. In `synapse.ts` and `mas.ts` an `@element-hq/…` import happens to sit above the image literal and caps it; `livekit.ts` and `lk-jwt-service.ts` import `testcontainers` and `yaml`, so the match instead starts at that import and swallows everything up to the image name. Renovate gets a nonsense package name, finds nothing, and the pins quietly rot. Exclude quotes and whitespace from both groups. All four digest-pinned files now extract correctly, synapse and mas unchanged. Bump lk-jwt-service to 0.7.0 while here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Address the bundled Element Call by directory, not by index.html The full-call spec passed against the webpack dev server and failed in CI, where the production build is hosted by `serve`. `ElementCall.generateWidgetUrl` built `./widgets/element-call/index.html#…`. `serve` has `cleanUrls` on by default, so it 301s that to the extensionless `/widgets/element-call`. Element Call's `index.html` references its chunks relatively (`./assets/…`), and against a base with no trailing slash the browser resolves those to `/widgets/assets/…` — 404. Element Call never boots, so the lobby never appears and the test times out. Address the directory instead. It is the same deployment, the trailing slash sidesteps the redirect, and the hash is overwritten further down either way. This is not only a test concern: Netlify also strips `.html` by default, and that is what serves the PR previews and develop.element.io. Doing it here rather than in the spec keeps the test honest — it exercises the widget URL Element Web really generates, with no `Developer.elementCallUrl` override. Setting `cleanUrls` in a `serve.json` was the other candidate and is not viable: any value other than the default also stops `serve` resolving `/` to `index.html`, which breaks every other test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
92 lines
3.4 KiB
TypeScript
92 lines
3.4 KiB
TypeScript
/*
|
|
Copyright 2024 New Vector Ltd.
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE files in the repository root for full details.
|
|
*/
|
|
|
|
import { test as base } from "@element-hq/element-web-playwright-common";
|
|
import {
|
|
type Services as BaseServices,
|
|
type WorkerOptions as BaseWorkerOptions,
|
|
} from "@element-hq/element-web-playwright-common/lib/fixtures";
|
|
import { type HomeserverContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js";
|
|
|
|
import { type OAuthServer } from "./plugins/oauth_server";
|
|
import { DendriteContainer, PineconeContainer } from "./testcontainers/dendrite";
|
|
import { type HomeserverType } from "./plugins/homeserver";
|
|
import { SynapseContainer } from "./testcontainers/synapse";
|
|
import { startMatrixRTCBackend, type StartedMatrixRTCBackend } from "./testcontainers/matrix-rtc";
|
|
|
|
export interface Services extends BaseServices {
|
|
// Set in legacyOAuthHomeserver only
|
|
oAuthServer?: OAuthServer;
|
|
/**
|
|
* The started MatrixRTC backend (LiveKit SFU + lk-jwt-service) for the worker.
|
|
* Only set when the `matrixRTC` option is on.
|
|
*/
|
|
matrixRTCBackend?: StartedMatrixRTCBackend;
|
|
}
|
|
|
|
export interface WorkerOptions extends BaseWorkerOptions {
|
|
homeserverType: HomeserverType;
|
|
/**
|
|
* Start a real MatrixRTC backend (LiveKit SFU + lk-jwt-service) and configure Synapse to announce it,
|
|
* so that Element Call can hold real calls with media. Synapse only.
|
|
*/
|
|
matrixRTC: boolean;
|
|
}
|
|
|
|
export const test = base.extend<{}, Services & WorkerOptions>({
|
|
homeserverType: ["synapse", { option: true, scope: "worker" }],
|
|
matrixRTC: [false, { option: true, scope: "worker" }],
|
|
matrixRTCBackend: [
|
|
async ({ matrixRTC, network, logger }, use) => {
|
|
if (!matrixRTC) {
|
|
await use(undefined);
|
|
return;
|
|
}
|
|
const backend = await startMatrixRTCBackend(network, logger);
|
|
await use(backend);
|
|
await backend.stop();
|
|
},
|
|
{ scope: "worker" },
|
|
],
|
|
_homeserver: [
|
|
async ({ homeserverType, matrixRTCBackend }, use) => {
|
|
let container: HomeserverContainer<unknown>;
|
|
switch (homeserverType) {
|
|
case "synapse":
|
|
container = new SynapseContainer();
|
|
break;
|
|
case "dendrite":
|
|
container = new DendriteContainer();
|
|
break;
|
|
case "pinecone":
|
|
container = new PineconeContainer();
|
|
break;
|
|
}
|
|
|
|
if (matrixRTCBackend) {
|
|
if (!(container instanceof SynapseContainer)) {
|
|
throw new Error(`The matrixRTC option is only supported with Synapse, not ${homeserverType}`);
|
|
}
|
|
container
|
|
.withConfig(matrixRTCBackend.synapseConfig)
|
|
.withCopyFilesToContainer(matrixRTCBackend.synapseFiles);
|
|
}
|
|
|
|
await use(container);
|
|
},
|
|
{ scope: "worker" },
|
|
],
|
|
|
|
context: async ({ homeserverType, synapseConfig, context, _homeserver }, use, testInfo) => {
|
|
testInfo.skip(
|
|
!(_homeserver instanceof SynapseContainer) && Object.keys(synapseConfig).length > 0,
|
|
`Test specifies Synapse config options so is unsupported with ${homeserverType}`,
|
|
);
|
|
await use(context);
|
|
},
|
|
});
|