Bundle Element Call with Element Web packages (#29309)
* Embed Element Call into Element Web packages Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Pass rageshakeSubmitUrl & posthogApiHost to EC widget Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Improve coverage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Improve coverage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update snapshots Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Use @vector-im/element-call-embedded * Only pass posthog params to EC if Analytics is enabled Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix test mock Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update EC params to match https://github.com/element-hq/element-call/pull/3089 Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update to latest element-call package Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * yarn.lock Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update to element-call-embedded@ v0.9.0-rc.1 * Gate Sentry params behind analytics consent Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update to element-call-embedded v0.9.0-rc.4 * Update Element Call embedded to 0.9.0 release Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> Co-authored-by: Hugh Nimmo-Smith <hughns@element.io>
This commit is contained in:
committed by
GitHub
parent
209ab59978
commit
8bb4d44532
@@ -190,6 +190,31 @@ exports[`DevtoolsDialog renders the devtools dialog 1`] = `
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<div
|
||||
class="_field_19upo_26"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="radix-:r4:"
|
||||
>
|
||||
Element Call URL
|
||||
</label>
|
||||
<div
|
||||
class="_controls_17lij_8"
|
||||
>
|
||||
<input
|
||||
class="_control_sqdq4_10"
|
||||
id="radix-:r4:"
|
||||
name="input"
|
||||
title=""
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright 2025 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 React from "react";
|
||||
import { render, screen, waitFor } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import SettingsField from "../../../../../src/components/views/elements/SettingsField";
|
||||
import { SettingLevel } from "../../../../../src/settings/SettingLevel.ts";
|
||||
|
||||
describe("<SettingsField />", () => {
|
||||
it("should render with the default label", () => {
|
||||
const component = render(<SettingsField settingKey="Developer.elementCallUrl" level={SettingLevel.DEVICE} />);
|
||||
|
||||
expect(screen.getByText("Element Call URL")).toBeTruthy();
|
||||
expect(component.asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should call onChange when saving a change", async () => {
|
||||
const fn = jest.fn();
|
||||
render(<SettingsField settingKey="Developer.elementCallUrl" level={SettingLevel.DEVICE} onChange={fn} />);
|
||||
|
||||
const input = screen.getByRole("textbox");
|
||||
await userEvent.type(input, "https://call.element.dev");
|
||||
expect(input).toHaveValue("https://call.element.dev");
|
||||
|
||||
screen.getByLabelText("Save").click();
|
||||
await waitFor(() => {
|
||||
expect(fn).toHaveBeenCalledWith("https://call.element.dev");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<SettingsField /> should render with the default label 1`] = `
|
||||
<DocumentFragment>
|
||||
<form
|
||||
class="_root_19upo_16"
|
||||
>
|
||||
<div
|
||||
class="_field_19upo_26"
|
||||
>
|
||||
<label
|
||||
class="_label_19upo_59"
|
||||
for="radix-:r0:"
|
||||
>
|
||||
Element Call URL
|
||||
</label>
|
||||
<div
|
||||
class="_controls_17lij_8"
|
||||
>
|
||||
<input
|
||||
class="_control_sqdq4_10"
|
||||
id="radix-:r0:"
|
||||
name="input"
|
||||
title=""
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
@@ -49,8 +49,9 @@ import { WidgetMessagingStore } from "../../../src/stores/widgets/WidgetMessagin
|
||||
import ActiveWidgetStore, { ActiveWidgetStoreEvent } from "../../../src/stores/ActiveWidgetStore";
|
||||
import { ElementWidgetActions } from "../../../src/stores/widgets/ElementWidgetActions";
|
||||
import SettingsStore from "../../../src/settings/SettingsStore";
|
||||
import { PosthogAnalytics } from "../../../src/PosthogAnalytics";
|
||||
import { Anonymity, PosthogAnalytics } from "../../../src/PosthogAnalytics";
|
||||
import { type SettingKey } from "../../../src/settings/Settings.tsx";
|
||||
import SdkConfig from "../../../src/SdkConfig.ts";
|
||||
|
||||
jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({
|
||||
[MediaDeviceKindEnum.AudioInput]: [
|
||||
@@ -664,6 +665,7 @@ describe("ElementCall", () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
({ client, room, alice } = setUpClientRoomAndStores());
|
||||
SdkConfig.reset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -683,6 +685,23 @@ describe("ElementCall", () => {
|
||||
Call.get(room)?.destroy();
|
||||
});
|
||||
|
||||
it("should use element call URL from developer settings if present", async () => {
|
||||
const originalGetValue = SettingsStore.getValue;
|
||||
SettingsStore.getValue = (name: SettingKey, roomId: string | null = null, excludeDefault = false): any => {
|
||||
if (name === "Developer.elementCallUrl") {
|
||||
return "https://call.element.dev";
|
||||
}
|
||||
return excludeDefault
|
||||
? originalGetValue(name, roomId, excludeDefault)
|
||||
: originalGetValue(name, roomId, excludeDefault);
|
||||
};
|
||||
await ElementCall.create(room);
|
||||
const call = ElementCall.get(room);
|
||||
expect(call?.widget.url.startsWith("https://call.element.dev/")).toBeTruthy();
|
||||
SettingsStore.getValue = originalGetValue;
|
||||
call?.destroy();
|
||||
});
|
||||
|
||||
it("finds ongoing calls that are created by the session manager", async () => {
|
||||
// There is an existing session created by another user in this room.
|
||||
client.matrixRTC.getRoomSession.mockReturnValue({
|
||||
@@ -758,7 +777,14 @@ describe("ElementCall", () => {
|
||||
SettingsStore.getValue = originalGetValue;
|
||||
});
|
||||
|
||||
it("passes analyticsID through widget URL", async () => {
|
||||
it("passes analyticsID and posthog params through widget URL", async () => {
|
||||
SdkConfig.put({
|
||||
posthog: {
|
||||
api_host: "https://posthog",
|
||||
project_api_key: "DEADBEEF",
|
||||
},
|
||||
});
|
||||
jest.spyOn(PosthogAnalytics.instance, "getAnonymity").mockReturnValue(Anonymity.Pseudonymous);
|
||||
client.getAccountData.mockImplementation((eventType: string) => {
|
||||
if (eventType === PosthogAnalytics.ANALYTICS_EVENT_TYPE) {
|
||||
return new MatrixEvent({ content: { id: "123456789987654321", pseudonymousAnalyticsOptIn: true } });
|
||||
@@ -771,6 +797,9 @@ describe("ElementCall", () => {
|
||||
|
||||
const urlParams = new URLSearchParams(new URL(call.widget.url).hash.slice(1));
|
||||
expect(urlParams.get("analyticsID")).toBe("123456789987654321");
|
||||
expect(urlParams.get("posthogUserId")).toBe("123456789987654321");
|
||||
expect(urlParams.get("posthogApiHost")).toBe("https://posthog");
|
||||
expect(urlParams.get("posthogApiKey")).toBe("DEADBEEF");
|
||||
call.destroy();
|
||||
});
|
||||
|
||||
@@ -788,7 +817,7 @@ describe("ElementCall", () => {
|
||||
if (!(call instanceof ElementCall)) throw new Error("Failed to create call");
|
||||
|
||||
const urlParams = new URLSearchParams(new URL(call.widget.url).hash.slice(1));
|
||||
expect(urlParams.get("analyticsID")).toBe("");
|
||||
expect(urlParams.get("analyticsID")).toBeFalsy();
|
||||
call.destroy();
|
||||
});
|
||||
|
||||
@@ -827,7 +856,7 @@ describe("ElementCall", () => {
|
||||
if (!(call instanceof ElementCall)) throw new Error("Failed to create call");
|
||||
|
||||
const urlParams = new URLSearchParams(new URL(call.widget.url).hash.slice(1));
|
||||
expect(urlParams.get("analyticsID")).toBe("");
|
||||
expect(urlParams.get("analyticsID")).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -196,6 +196,8 @@ describe("RoomViewStore", function () {
|
||||
stores.client = mockClient;
|
||||
stores._SlidingSyncManager = slidingSyncManager;
|
||||
stores._PosthogAnalytics = new MockPosthogAnalytics();
|
||||
// @ts-expect-error
|
||||
MockPosthogAnalytics.instance = stores._PosthogAnalytics;
|
||||
stores._SpaceStore = new MockSpaceStore();
|
||||
roomViewStore = new RoomViewStore(dis, stores);
|
||||
stores._RoomViewStore = roomViewStore;
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import {
|
||||
Widget,
|
||||
MatrixWidgetType,
|
||||
WidgetKind,
|
||||
type WidgetDriver,
|
||||
type ITurnServer,
|
||||
@@ -44,6 +43,7 @@ import { ModuleRunner } from "../../../../src/modules/ModuleRunner";
|
||||
import dis from "../../../../src/dispatcher/dispatcher";
|
||||
import Modal from "../../../../src/Modal";
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
import { WidgetType } from "../../../../src/widgets/WidgetType.ts";
|
||||
|
||||
describe("StopGapWidgetDriver", () => {
|
||||
let client: MockedObject<MatrixClient>;
|
||||
@@ -79,7 +79,7 @@ describe("StopGapWidgetDriver", () => {
|
||||
new Widget({
|
||||
id: "group_call",
|
||||
creatorUserId: "@alice:example.org",
|
||||
type: MatrixWidgetType.Custom,
|
||||
type: WidgetType.CALL.preferred,
|
||||
url: "https://call.element.io",
|
||||
}),
|
||||
WidgetKind.Room,
|
||||
|
||||
@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import { mocked } from "jest-mock";
|
||||
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { MatrixWidgetType, Widget, WidgetKind } from "matrix-widget-api";
|
||||
import { Widget, WidgetKind } from "matrix-widget-api";
|
||||
|
||||
import { OIDCState, WidgetPermissionStore } from "../../../../src/stores/widgets/WidgetPermissionStore";
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
@@ -17,6 +17,7 @@ import { type SettingLevel } from "../../../../src/settings/SettingLevel";
|
||||
import { SdkContextClass } from "../../../../src/contexts/SDKContext";
|
||||
import { stubClient } from "../../../test-utils";
|
||||
import { StopGapWidgetDriver } from "../../../../src/stores/widgets/StopGapWidgetDriver";
|
||||
import { WidgetType } from "../../../../src/widgets/WidgetType.ts";
|
||||
|
||||
jest.mock("../../../../src/settings/SettingsStore");
|
||||
|
||||
@@ -34,7 +35,7 @@ describe("WidgetPermissionStore", () => {
|
||||
const elementCallWidget = new Widget({
|
||||
id: "group_call",
|
||||
creatorUserId: "@alice:example.org",
|
||||
type: MatrixWidgetType.Custom,
|
||||
type: WidgetType.CALL.preferred,
|
||||
url: "https://call.element.io",
|
||||
});
|
||||
let settings: Record<string, any> = {}; // key value store
|
||||
|
||||
@@ -28,7 +28,7 @@ describe("recordClientInformation()", () => {
|
||||
const sdkConfig: DeepReadonly<IConfigOptions> = {
|
||||
...DEFAULTS,
|
||||
brand: "Test Brand",
|
||||
element_call: { url: "", use_exclusively: false, brand: "Element Call" },
|
||||
element_call: { use_exclusively: false, brand: "Element Call" },
|
||||
};
|
||||
|
||||
const platform = {
|
||||
|
||||
Reference in New Issue
Block a user