mirror of
https://github.com/vector-im/element-call.git
synced 2026-10-16 23:45:50 +00:00
Add the MatrixRTC SDK package, its harness and a failing smoke test
Set up `sdk/` as a package shaped exactly like `component/`: its own package.json with peer dependencies, the pnpm workspace marker so a host can install it as a git dependency, a build tsconfig for type emission, and a vite lib config with an externals list that `pnpm lint:externals` now checks for both library builds. The package holds only the interface for now. `createRtcSession` returns an empty object; `RtcSession`, `RtcMember`, `MemberMedia` and `TransportMetadata` are the contract Element Call's `CallViewModel` will be rebuilt on (see sdk-plan.md). The shared primitives are re-exported from `src` until the first implementation slice moves them. `sdk/dev` is the smallest consumer of the SDK: a plain page that logs in, joins a room and renders one tile per member, served by `pnpm dev:sdk` on port 3002. Playwright starts it as a third web server, and `playwright/sdk/smoke.spec.ts` has two browsers join one session. It fails on the status line with the SDK's own error until there is an implementation behind the interface. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
2b9fdfe4d5
commit
9910496b90
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
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 Page } from "@playwright/test";
|
||||
|
||||
import { SynapseAdmin } from "../utils/synapse-admin.ts";
|
||||
|
||||
/**
|
||||
* Where the SDK harness is served — `sdk/dev`, a page that uses the SDK the way
|
||||
* a host would, with none of Element Call in it.
|
||||
*/
|
||||
export const SDK_HARNESS_URL = "https://localhost:3002";
|
||||
|
||||
const HOMESERVER_URL = "https://synapse.m.localhost";
|
||||
const PASSWORD = "foobarbaz1!";
|
||||
|
||||
/**
|
||||
* Registers two users through the Synapse admin API and has the first create a
|
||||
* public room, so that the second can join it by id without an invite.
|
||||
*/
|
||||
export async function createUsersAndRoom(
|
||||
name: string,
|
||||
): Promise<{ usernames: [string, string]; roomId: string }> {
|
||||
const admin = SynapseAdmin.forHomeserver(HOMESERVER_URL);
|
||||
const usernames: [string, string] = [
|
||||
`${name}_a_${Date.now()}`,
|
||||
`${name}_b_${Date.now()}`,
|
||||
];
|
||||
const [{ access_token: accessToken }] = await Promise.all(
|
||||
usernames.map(async (username, index) =>
|
||||
admin.registerUser(username, PASSWORD, `${name} ${"AB"[index]}`),
|
||||
),
|
||||
);
|
||||
|
||||
const response = await fetch(
|
||||
`${HOMESERVER_URL}/_matrix/client/v3/createRoom`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: `${name}'s session`,
|
||||
preset: "public_chat",
|
||||
}),
|
||||
},
|
||||
);
|
||||
if (!response.ok)
|
||||
throw new Error(
|
||||
`Could not create a room: ${response.status} ${await response.text()}`,
|
||||
);
|
||||
const { room_id: roomId } = (await response.json()) as { room_id: string };
|
||||
|
||||
return { usernames, roomId };
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the harness signed in as the given user and waits until the SDK
|
||||
* reports the session as joined. The status line carries the SDK's error if
|
||||
* it does not get there, so the failure says why.
|
||||
*/
|
||||
export async function startHarness(
|
||||
page: Page,
|
||||
username: string,
|
||||
roomId: string,
|
||||
): Promise<void> {
|
||||
const query = new URLSearchParams({
|
||||
homeserver: HOMESERVER_URL,
|
||||
username,
|
||||
password: PASSWORD,
|
||||
room: roomId,
|
||||
});
|
||||
await page.goto(`${SDK_HARNESS_URL}/?${query.toString()}`);
|
||||
await page.getByRole("button", { name: "Start" }).click();
|
||||
|
||||
// A login, a crypto setup and an initial sync happen first. An error is
|
||||
// final, so it is not worth waiting out the timeout for "Joined" after one.
|
||||
const status = page.getByTestId("status");
|
||||
await expect(status).toHaveText(/^(Joined|Error)/, { timeout: 120_000 });
|
||||
const text = await status.textContent();
|
||||
if (text !== "Joined")
|
||||
throw new Error(`The harness did not join the session: ${text}`);
|
||||
}
|
||||
Reference in New Issue
Block a user