Add widget.ts tests
This commit is contained in:
@@ -104,7 +104,7 @@
|
|||||||
"livekit-client": "^2.13.0",
|
"livekit-client": "^2.13.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"loglevel": "^1.9.1",
|
"loglevel": "^1.9.1",
|
||||||
"matrix-js-sdk": "matrix-org/matrix-js-sdk#robin/sticky-embedded",
|
"matrix-js-sdk": "matrix-org/matrix-js-sdk#develop",
|
||||||
"matrix-widget-api": "^1.16.1",
|
"matrix-widget-api": "^1.16.1",
|
||||||
"node-stdlib-browser": "^1.3.1",
|
"node-stdlib-browser": "^1.3.1",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ import {
|
|||||||
tryMakeSticky,
|
tryMakeSticky,
|
||||||
widget,
|
widget,
|
||||||
} from "./helper";
|
} from "./helper";
|
||||||
import { ElementWidgetActions } from "../src/widget";
|
import { ElementWidgetActions, initializeWidget } from "../src/widget";
|
||||||
import { type Connection } from "../src/state/CallViewModel/remoteMembers/Connection";
|
import { type Connection } from "../src/state/CallViewModel/remoteMembers/Connection";
|
||||||
|
|
||||||
interface MatrixRTCSdk {
|
interface MatrixRTCSdk {
|
||||||
@@ -88,7 +88,7 @@ export async function createMatrixRTCSdk(
|
|||||||
application: string = "m.call",
|
application: string = "m.call",
|
||||||
id: string = "",
|
id: string = "",
|
||||||
): Promise<MatrixRTCSdk> {
|
): Promise<MatrixRTCSdk> {
|
||||||
logger.info("Hello");
|
initializeWidget();
|
||||||
const client = await widget.client;
|
const client = await widget.client;
|
||||||
logger.info("client created");
|
logger.info("client created");
|
||||||
const scope = new ObservableScope();
|
const scope = new ObservableScope();
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import { getUrlParams } from "./UrlParams";
|
|||||||
import { Config } from "./config/Config";
|
import { Config } from "./config/Config";
|
||||||
import { platform } from "./Platform";
|
import { platform } from "./Platform";
|
||||||
import { isFailure } from "./utils/fetch";
|
import { isFailure } from "./utils/fetch";
|
||||||
|
import { initializeWidget } from "./widget";
|
||||||
|
|
||||||
// This generates a map of locale names to their URL (based on import.meta.url), which looks like this:
|
// This generates a map of locale names to their URL (based on import.meta.url), which looks like this:
|
||||||
// {
|
// {
|
||||||
@@ -115,6 +116,8 @@ export class Initializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async initBeforeReact(): Promise<void> {
|
public static async initBeforeReact(): Promise<void> {
|
||||||
|
initializeWidget();
|
||||||
|
|
||||||
const polyfills: Promise<unknown>[] = [];
|
const polyfills: Promise<unknown>[] = [];
|
||||||
if (shouldPolyfillSegmenter()) {
|
if (shouldPolyfillSegmenter()) {
|
||||||
polyfills.push(import("@formatjs/intl-segmenter/polyfill-force"));
|
polyfills.push(import("@formatjs/intl-segmenter/polyfill-force"));
|
||||||
|
|||||||
127
src/widget.test.ts
Normal file
127
src/widget.test.ts
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2026 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { beforeAll, describe, expect, vi, it } from "vitest";
|
||||||
|
import { createRoomWidgetClient, EventType } from "matrix-js-sdk";
|
||||||
|
|
||||||
|
import { getUrlParams } from "./UrlParams";
|
||||||
|
import { initializeWidget, widget } from "./widget";
|
||||||
|
import { Config } from "./config/Config";
|
||||||
|
import { ElementCallReactionEventType } from "./reactions";
|
||||||
|
|
||||||
|
vi.mock("matrix-js-sdk", { spy: true });
|
||||||
|
const createRoomWidgetClientSpy = vi.mocked(createRoomWidgetClient);
|
||||||
|
|
||||||
|
vi.mock("./config/Config", () => ({
|
||||||
|
Config: {
|
||||||
|
init: vi.fn().mockImplementation(async () => Promise.resolve()),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
const configInitSpy = vi.mocked(Config.init);
|
||||||
|
|
||||||
|
vi.mock("./UrlParams", () => ({
|
||||||
|
getUrlParams: vi.fn(() => ({
|
||||||
|
widgetId: "id",
|
||||||
|
parentUrl: "http://parentUrl",
|
||||||
|
roomId: "room",
|
||||||
|
userId: "myYser",
|
||||||
|
deviceId: "AAAAA",
|
||||||
|
baseUrl: "http://baseUrl",
|
||||||
|
e2eEnabled: true,
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
initializeWidget();
|
||||||
|
describe("widget", () => {
|
||||||
|
beforeAll(() => {});
|
||||||
|
|
||||||
|
it("should create an embedded client with the correct params", () => {
|
||||||
|
expect(getUrlParams()).toStrictEqual({
|
||||||
|
widgetId: "id",
|
||||||
|
parentUrl: "http://parentUrl",
|
||||||
|
roomId: "room",
|
||||||
|
userId: "myYser",
|
||||||
|
deviceId: "AAAAA",
|
||||||
|
baseUrl: "http://baseUrl",
|
||||||
|
e2eEnabled: true,
|
||||||
|
});
|
||||||
|
expect(widget).toBeDefined();
|
||||||
|
expect(configInitSpy).toHaveBeenCalled();
|
||||||
|
const sendEvent = [
|
||||||
|
EventType.CallNotify, // Sent as a deprecated fallback
|
||||||
|
EventType.RTCNotification,
|
||||||
|
];
|
||||||
|
const sendRecvEvent = [
|
||||||
|
"org.matrix.rageshake_request",
|
||||||
|
EventType.CallEncryptionKeysPrefix,
|
||||||
|
EventType.Reaction,
|
||||||
|
EventType.RoomRedaction,
|
||||||
|
ElementCallReactionEventType,
|
||||||
|
EventType.RTCDecline,
|
||||||
|
EventType.RTCMembership,
|
||||||
|
];
|
||||||
|
|
||||||
|
const sendState = [
|
||||||
|
"myYser", // Legacy call membership events
|
||||||
|
`_myYser_AAAAA_m.call`, // Session membership events
|
||||||
|
`myYser_AAAAA_m.call`, // The above with no leading underscore, for room versions whose auth rules allow it
|
||||||
|
].map((stateKey) => ({
|
||||||
|
eventType: EventType.GroupCallMemberPrefix,
|
||||||
|
stateKey,
|
||||||
|
}));
|
||||||
|
const receiveState = [
|
||||||
|
{ eventType: EventType.RoomCreate },
|
||||||
|
{ eventType: EventType.RoomName },
|
||||||
|
{ eventType: EventType.RoomMember },
|
||||||
|
{ eventType: EventType.RoomEncryption },
|
||||||
|
{ eventType: EventType.GroupCallMemberPrefix },
|
||||||
|
];
|
||||||
|
|
||||||
|
const sendRecvToDevice = [
|
||||||
|
EventType.CallInvite,
|
||||||
|
EventType.CallCandidates,
|
||||||
|
EventType.CallAnswer,
|
||||||
|
EventType.CallHangup,
|
||||||
|
EventType.CallReject,
|
||||||
|
EventType.CallSelectAnswer,
|
||||||
|
EventType.CallNegotiate,
|
||||||
|
EventType.CallSDPStreamMetadataChanged,
|
||||||
|
EventType.CallSDPStreamMetadataChangedPrefix,
|
||||||
|
EventType.CallReplaces,
|
||||||
|
EventType.CallEncryptionKeysPrefix,
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(createRoomWidgetClientSpy.mock.calls[0][1]).toStrictEqual({
|
||||||
|
sendEvent: [...sendEvent, ...sendRecvEvent],
|
||||||
|
receiveEvent: sendRecvEvent,
|
||||||
|
sendState,
|
||||||
|
receiveState,
|
||||||
|
sendToDevice: sendRecvToDevice,
|
||||||
|
receiveToDevice: sendRecvToDevice,
|
||||||
|
turnServers: false,
|
||||||
|
sendDelayedEvents: true,
|
||||||
|
updateDelayedEvents: true,
|
||||||
|
sendSticky: true,
|
||||||
|
receiveSticky: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(createRoomWidgetClientSpy.mock.calls[0][2]).toStrictEqual("room");
|
||||||
|
expect(createRoomWidgetClientSpy.mock.calls[0][3]).toStrictEqual({
|
||||||
|
baseUrl: "http://baseUrl",
|
||||||
|
userId: "myYser",
|
||||||
|
deviceId: "AAAAA",
|
||||||
|
timelineSupport: true,
|
||||||
|
useE2eForGroupCall: true,
|
||||||
|
fallbackICEServerAllowed: undefined,
|
||||||
|
store: expect.any(Object),
|
||||||
|
cryptoStore: expect.any(Object),
|
||||||
|
idBaseUrl: undefined,
|
||||||
|
scheduler: expect.any(Object),
|
||||||
|
});
|
||||||
|
expect(createRoomWidgetClientSpy.mock.calls[0][4]).toStrictEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -6,14 +6,17 @@ Please see LICENSE in the repository root for full details.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { logger } from "matrix-js-sdk/lib/logger";
|
import { logger } from "matrix-js-sdk/lib/logger";
|
||||||
import { EventType, createRoomWidgetClient } from "matrix-js-sdk";
|
import {
|
||||||
|
EventType,
|
||||||
|
createRoomWidgetClient,
|
||||||
|
type MatrixClient,
|
||||||
|
} from "matrix-js-sdk";
|
||||||
import {
|
import {
|
||||||
WidgetApi,
|
WidgetApi,
|
||||||
MatrixCapabilities,
|
MatrixCapabilities,
|
||||||
WidgetApiToWidgetAction,
|
WidgetApiToWidgetAction,
|
||||||
} from "matrix-widget-api";
|
} from "matrix-widget-api";
|
||||||
|
|
||||||
import type { MatrixClient } from "matrix-js-sdk";
|
|
||||||
import type { IWidgetApiRequest } from "matrix-widget-api";
|
import type { IWidgetApiRequest } from "matrix-widget-api";
|
||||||
import { LazyEventEmitter } from "./LazyEventEmitter";
|
import { LazyEventEmitter } from "./LazyEventEmitter";
|
||||||
import { getUrlParams } from "./UrlParams";
|
import { getUrlParams } from "./UrlParams";
|
||||||
@@ -55,15 +58,29 @@ export interface WidgetHelpers {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A point of access to the widget API, if the app is running as a widget. This
|
* A point of access to the widget API, if the app is running as a widget. This
|
||||||
* is declared and initialized on the top level because the widget messaging
|
* is initialized with `initializeWidget`. This should happen at the top level because the widget messaging
|
||||||
* needs to be set up ASAP on load to ensure it doesn't miss any requests.
|
* needs to be set up ASAP on load to ensure it doesn't miss any requests.
|
||||||
*/
|
*/
|
||||||
export const widget = ((): WidgetHelpers | null => {
|
export let widget: WidgetHelpers | null;
|
||||||
try {
|
|
||||||
const { widgetId, parentUrl } = getUrlParams();
|
/**
|
||||||
|
* Should be called as soon as possible on app start. (In the initilizer before react)
|
||||||
|
*/
|
||||||
|
// this needs to be a seperate call and cannot be done on import to allow us to spy on methods in here before
|
||||||
|
// execution.
|
||||||
|
export const initializeWidget = (): void => {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
widgetId,
|
||||||
|
parentUrl,
|
||||||
|
roomId,
|
||||||
|
userId,
|
||||||
|
deviceId,
|
||||||
|
baseUrl,
|
||||||
|
e2eEnabled,
|
||||||
|
allowIceFallback,
|
||||||
|
} = getUrlParams();
|
||||||
|
|
||||||
const { roomId, userId, deviceId, baseUrl, e2eEnabled, allowIceFallback } =
|
|
||||||
getUrlParams();
|
|
||||||
if (!roomId) throw new Error("Room ID must be supplied");
|
if (!roomId) throw new Error("Room ID must be supplied");
|
||||||
if (!userId) throw new Error("User ID must be supplied");
|
if (!userId) throw new Error("User ID must be supplied");
|
||||||
if (!deviceId) throw new Error("Device ID must be supplied");
|
if (!deviceId) throw new Error("Device ID must be supplied");
|
||||||
@@ -175,14 +192,14 @@ export const widget = ((): WidgetHelpers | null => {
|
|||||||
return client;
|
return client;
|
||||||
};
|
};
|
||||||
|
|
||||||
return { api, lazyActions, client: clientPromise() };
|
widget = { api, lazyActions, client: clientPromise() };
|
||||||
} else {
|
} else {
|
||||||
if (import.meta.env.MODE !== "test")
|
if (import.meta.env.MODE !== "test")
|
||||||
logger.info("No widget API available");
|
logger.info("No widget API available");
|
||||||
return null;
|
widget = null;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.warn("Continuing without the widget API", e);
|
logger.warn("Continuing without the widget API", e);
|
||||||
return null;
|
widget = null;
|
||||||
}
|
}
|
||||||
})();
|
};
|
||||||
|
|||||||
@@ -8364,7 +8364,7 @@ __metadata:
|
|||||||
livekit-client: "npm:^2.13.0"
|
livekit-client: "npm:^2.13.0"
|
||||||
lodash-es: "npm:^4.17.21"
|
lodash-es: "npm:^4.17.21"
|
||||||
loglevel: "npm:^1.9.1"
|
loglevel: "npm:^1.9.1"
|
||||||
matrix-js-sdk: "matrix-org/matrix-js-sdk#robin/sticky-embedded"
|
matrix-js-sdk: "matrix-org/matrix-js-sdk#develop"
|
||||||
matrix-widget-api: "npm:^1.16.1"
|
matrix-widget-api: "npm:^1.16.1"
|
||||||
node-stdlib-browser: "npm:^1.3.1"
|
node-stdlib-browser: "npm:^1.3.1"
|
||||||
normalize.css: "npm:^8.0.1"
|
normalize.css: "npm:^8.0.1"
|
||||||
@@ -11452,9 +11452,9 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"matrix-js-sdk@matrix-org/matrix-js-sdk#robin/sticky-embedded":
|
"matrix-js-sdk@matrix-org/matrix-js-sdk#develop":
|
||||||
version: 40.0.0
|
version: 40.0.0
|
||||||
resolution: "matrix-js-sdk@https://github.com/matrix-org/matrix-js-sdk.git#commit=e205ce2cc689ace090f85bf74e0fd59ec00a179a"
|
resolution: "matrix-js-sdk@https://github.com/matrix-org/matrix-js-sdk.git#commit=dbb2ae5c0752c28639502e93f26cb3003d0d0595"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime": "npm:^7.12.5"
|
"@babel/runtime": "npm:^7.12.5"
|
||||||
"@matrix-org/matrix-sdk-crypto-wasm": "npm:^17.0.0"
|
"@matrix-org/matrix-sdk-crypto-wasm": "npm:^17.0.0"
|
||||||
@@ -11470,7 +11470,7 @@ __metadata:
|
|||||||
sdp-transform: "npm:^3.0.0"
|
sdp-transform: "npm:^3.0.0"
|
||||||
unhomoglyph: "npm:^1.0.6"
|
unhomoglyph: "npm:^1.0.6"
|
||||||
uuid: "npm:13"
|
uuid: "npm:13"
|
||||||
checksum: 10c0/1b70a6520f034c2daa6db1d3626e116adb1f564c13bd4977f17af49e2e4e2a2ab629c77472f649962c9f301f56dd1c3b2480e6b560756f4ceed62755e1688cd6
|
checksum: 10c0/9f97cec346e0dcce8599bc3afa1608f5166408260937f8311fa9af95b8fd2ff6d86422124fcb721fc830a3ec269389067334c344b4f512b64299561484135326
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user