mv element.io @types __mocks__/ debian docker module_system/ playwright res src test webapp Dockerfile .dockerignore .eslintignore .stylelintrc.cjs babel.config.cjs recorder-worklet-loader.cjs .modernizr.json components.json config.json config.sample.json package.json project.json tsconfig.json tsconfig.module_system.json jest.config.ts playwright.config.ts webpack.config.ts build_config.sample.yaml apps/web/
mkdir apps/web/scripts
mv scripts/{cleanup.sh,ci_package.sh,copy-res.ts,deploy.py,package.sh} apps/web/scripts
And a couple of gitignore tweaks
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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, act, cleanup } from "jest-matrix-react";
|
||||
import { mocked, type Mocked } from "jest-mock";
|
||||
import {
|
||||
type MatrixClient,
|
||||
PendingEventOrdering,
|
||||
Room,
|
||||
RoomStateEvent,
|
||||
type RoomMember,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { Widget } from "matrix-widget-api";
|
||||
|
||||
import {
|
||||
stubClient,
|
||||
mkRoomMember,
|
||||
wrapInMatrixClientContext,
|
||||
useMockedCalls,
|
||||
MockedCall,
|
||||
setupAsyncStoreWithClient,
|
||||
useMockMediaDevices,
|
||||
} from "../../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import { CallView as _CallView } from "../../../../../src/components/views/voip/CallView";
|
||||
import { WidgetMessagingStore } from "../../../../../src/stores/widgets/WidgetMessagingStore";
|
||||
import { CallStore } from "../../../../../src/stores/CallStore";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import { type WidgetMessaging } from "../../../../../src/stores/widgets/WidgetMessaging";
|
||||
|
||||
const CallView = wrapInMatrixClientContext(_CallView);
|
||||
|
||||
describe("CallView", () => {
|
||||
useMockedCalls();
|
||||
jest.spyOn(HTMLMediaElement.prototype, "play").mockImplementation(async () => {});
|
||||
|
||||
let client: Mocked<MatrixClient>;
|
||||
let room: Room;
|
||||
let alice: RoomMember;
|
||||
let call: MockedCall;
|
||||
let widget: Widget;
|
||||
|
||||
beforeEach(() => {
|
||||
useMockMediaDevices();
|
||||
|
||||
stubClient();
|
||||
client = mocked(MatrixClientPeg.safeGet());
|
||||
DMRoomMap.makeShared(client);
|
||||
|
||||
room = new Room("!1:example.org", client, "@alice:example.org", {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
alice = mkRoomMember(room.roomId, "@alice:example.org");
|
||||
jest.spyOn(room, "getMember").mockImplementation((userId) => (userId === alice.userId ? alice : null));
|
||||
|
||||
client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null));
|
||||
client.getRooms.mockReturnValue([room]);
|
||||
client.reEmitter.reEmit(room, [RoomStateEvent.Events]);
|
||||
|
||||
setupAsyncStoreWithClient(CallStore.instance, client);
|
||||
setupAsyncStoreWithClient(WidgetMessagingStore.instance, client);
|
||||
|
||||
MockedCall.create(room, "1");
|
||||
const maybeCall = CallStore.instance.getCall(room.roomId);
|
||||
if (!(maybeCall instanceof MockedCall)) throw new Error("Failed to create call");
|
||||
call = maybeCall;
|
||||
|
||||
widget = new Widget(call.widget);
|
||||
WidgetMessagingStore.instance.storeMessaging(widget, room.roomId, {
|
||||
on: () => {},
|
||||
off: () => {},
|
||||
stop: () => {},
|
||||
embedUrl: "https://example.org",
|
||||
} as unknown as WidgetMessaging);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup(); // Unmount before we do any cleanup that might update the component
|
||||
call.destroy();
|
||||
WidgetMessagingStore.instance.stopMessaging(widget, room.roomId);
|
||||
client.reEmitter.stopReEmitting(room, [RoomStateEvent.Events]);
|
||||
});
|
||||
|
||||
const renderView = async (role: string | undefined = undefined): Promise<void> => {
|
||||
render(<CallView room={room} resizing={false} role={role} onClose={() => {}} />);
|
||||
await act(() => Promise.resolve()); // Let effects settle
|
||||
};
|
||||
|
||||
it("accepts an accessibility role", async () => {
|
||||
await renderView("main");
|
||||
screen.getByRole("main");
|
||||
});
|
||||
|
||||
it("calls clean on mount", async () => {
|
||||
const cleanSpy = jest.spyOn(call, "clean");
|
||||
await renderView();
|
||||
expect(cleanSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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 } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import DialPad, { BUTTONS, BUTTON_LETTERS } from "../../../../../src/components/views/voip/DialPad";
|
||||
|
||||
it("when hasDial is true, displays all expected numbers and letters", () => {
|
||||
render(<DialPad onDigitPress={jest.fn()} hasDial={true} onDialPress={jest.fn()} />);
|
||||
|
||||
// check that we have the expected number of buttons + 1 for the dial button
|
||||
expect(screen.getAllByRole("button")).toHaveLength(BUTTONS.length + 1);
|
||||
|
||||
// BUTTONS represents the numbers and symbols
|
||||
BUTTONS.forEach((button) => {
|
||||
expect(screen.getByText(button)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// BUTTON_LETTERS represents the `ABC` type strings you see on the keypad, but also contains
|
||||
// some empty strings, so we filter them out prior to tests
|
||||
BUTTON_LETTERS.filter(Boolean).forEach((letterSet) => {
|
||||
expect(screen.getByText(letterSet)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// check for the dial button
|
||||
expect(screen.getByRole("button", { name: "Dial" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("clicking a digit button calls the correct function", async () => {
|
||||
const mockOnDigitPress = jest.fn();
|
||||
render(<DialPad onDigitPress={mockOnDigitPress} hasDial={true} onDialPress={jest.fn()} />);
|
||||
|
||||
// click the `1` button
|
||||
const buttonText = "1";
|
||||
await userEvent.click(screen.getByText(buttonText, { exact: false }));
|
||||
expect(mockOnDigitPress).toHaveBeenCalledTimes(1);
|
||||
expect(mockOnDigitPress.mock.calls[0][0]).toBe(buttonText);
|
||||
});
|
||||
|
||||
it("clicking the dial button calls the correct function", async () => {
|
||||
const mockOnDial = jest.fn();
|
||||
render(<DialPad onDigitPress={jest.fn()} hasDial={true} onDialPress={mockOnDial} />);
|
||||
|
||||
// click the `1` button
|
||||
const buttonText = "Dial";
|
||||
await userEvent.click(screen.getByRole("button", { name: buttonText }));
|
||||
expect(mockOnDial).toHaveBeenCalledTimes(1);
|
||||
expect(mockOnDial).toHaveBeenCalledWith(); // represents no arguments in the call
|
||||
});
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
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 } from "jest-matrix-react";
|
||||
import { type MatrixCall } from "matrix-js-sdk/src/matrix";
|
||||
import { type CallFeed } from "matrix-js-sdk/src/webrtc/callFeed";
|
||||
import { SDPStreamMetadataPurpose } from "matrix-js-sdk/src/webrtc/callEventTypes";
|
||||
|
||||
import LegacyCallView from "../../../../../src/components/views/voip/LegacyCallView";
|
||||
import { stubClient } from "../../../../test-utils";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
|
||||
describe("LegacyCallView", () => {
|
||||
it("should exit full screen on unmount", () => {
|
||||
const element = document.createElement("div");
|
||||
// @ts-expect-error
|
||||
document.fullscreenElement = element;
|
||||
document.exitFullscreen = jest.fn();
|
||||
|
||||
stubClient();
|
||||
|
||||
const call = {
|
||||
on: jest.fn(),
|
||||
removeListener: jest.fn(),
|
||||
getFeeds: jest.fn().mockReturnValue([]),
|
||||
isLocalOnHold: jest.fn().mockReturnValue(false),
|
||||
isRemoteOnHold: jest.fn().mockReturnValue(false),
|
||||
isMicrophoneMuted: jest.fn().mockReturnValue(false),
|
||||
isLocalVideoMuted: jest.fn().mockReturnValue(false),
|
||||
isScreensharing: jest.fn().mockReturnValue(false),
|
||||
} as unknown as MatrixCall;
|
||||
|
||||
const { unmount } = render(<LegacyCallView call={call} sidebarShown={false} />);
|
||||
expect(document.exitFullscreen).not.toHaveBeenCalled();
|
||||
unmount();
|
||||
expect(document.exitFullscreen).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should show/hide the sidebar based on the sidebarShown prop", async () => {
|
||||
stubClient();
|
||||
|
||||
const call = {
|
||||
roomId: "test-room",
|
||||
on: jest.fn(),
|
||||
removeListener: jest.fn(),
|
||||
getFeeds: jest.fn().mockReturnValue(
|
||||
[{ local: true }, { local: false }, { local: true, screenshare: true }].map(
|
||||
(x, i) =>
|
||||
({
|
||||
stream: { id: "test-" + i },
|
||||
addListener: jest.fn(),
|
||||
removeListener: jest.fn(),
|
||||
getMember: jest.fn(),
|
||||
isAudioMuted: jest.fn().mockReturnValue(true),
|
||||
isVideoMuted: jest.fn().mockReturnValue(true),
|
||||
isLocal: jest.fn().mockReturnValue(x.local),
|
||||
purpose: x.screenshare && SDPStreamMetadataPurpose.Screenshare,
|
||||
}) as unknown as CallFeed,
|
||||
),
|
||||
),
|
||||
isLocalOnHold: jest.fn().mockReturnValue(false),
|
||||
isRemoteOnHold: jest.fn().mockReturnValue(false),
|
||||
isMicrophoneMuted: jest.fn().mockReturnValue(true),
|
||||
isLocalVideoMuted: jest.fn().mockReturnValue(true),
|
||||
isScreensharing: jest.fn().mockReturnValue(true),
|
||||
noIncomingFeeds: jest.fn().mockReturnValue(false),
|
||||
opponentSupportsSDPStreamMetadata: jest.fn().mockReturnValue(true),
|
||||
} as unknown as MatrixCall;
|
||||
DMRoomMap.setShared({
|
||||
getUserIdForRoomId: jest.fn().mockReturnValue("test-user"),
|
||||
} as unknown as DMRoomMap);
|
||||
|
||||
const { container, rerender } = render(<LegacyCallView call={call} sidebarShown={true} />);
|
||||
expect(container.querySelector(".mx_LegacyCallViewSidebar")).toBeTruthy();
|
||||
rerender(<LegacyCallView call={call} sidebarShown={true} />);
|
||||
expect(container.querySelector(".mx_LegacyCallViewSidebar")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should not show the sidebar button in picture-in-picture mode", async () => {
|
||||
stubClient();
|
||||
|
||||
const call = {
|
||||
on: jest.fn(),
|
||||
removeListener: jest.fn(),
|
||||
getFeeds: jest.fn().mockReturnValue([]),
|
||||
isLocalOnHold: jest.fn().mockReturnValue(false),
|
||||
isRemoteOnHold: jest.fn().mockReturnValue(false),
|
||||
isMicrophoneMuted: jest.fn().mockReturnValue(false),
|
||||
isLocalVideoMuted: jest.fn().mockReturnValue(false),
|
||||
isScreensharing: jest.fn().mockReturnValue(false),
|
||||
} as unknown as MatrixCall;
|
||||
DMRoomMap.setShared({
|
||||
getUserIdForRoomId: jest.fn().mockReturnValue("test-user"),
|
||||
} as unknown as DMRoomMap);
|
||||
|
||||
const { container } = render(<LegacyCallView call={call} sidebarShown={false} pipMode={true} />);
|
||||
expect(container.querySelector(".mx_LegacyCallViewButtons_button_sidebar")).toBeFalsy();
|
||||
});
|
||||
});
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
* Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 } from "jest-matrix-react";
|
||||
import { MatrixCall } from "matrix-js-sdk/src/webrtc/call";
|
||||
|
||||
import LegacyCallViewButtons from "../../../../../../src/components/views/voip/LegacyCallView/LegacyCallViewButtons";
|
||||
import { createTestClient } from "../../../../../test-utils";
|
||||
|
||||
describe("LegacyCallViewButtons", () => {
|
||||
const matrixClient = createTestClient();
|
||||
const roomId = "test-room-id";
|
||||
|
||||
const renderButtons = () => {
|
||||
const call = new MatrixCall({
|
||||
client: matrixClient,
|
||||
roomId,
|
||||
});
|
||||
|
||||
return render(
|
||||
<LegacyCallViewButtons
|
||||
call={call}
|
||||
handlers={{
|
||||
onScreenshareClick: jest.fn(),
|
||||
onToggleSidebarClick: jest.fn(),
|
||||
onHangupClick: jest.fn(),
|
||||
onMicMuteClick: jest.fn(),
|
||||
onVidMuteClick: jest.fn(),
|
||||
}}
|
||||
buttonsVisibility={{
|
||||
vidMute: true,
|
||||
screensharing: true,
|
||||
sidebar: true,
|
||||
contextMenu: true,
|
||||
dialpad: true,
|
||||
}}
|
||||
buttonsState={{
|
||||
micMuted: false,
|
||||
vidMuted: false,
|
||||
sidebarShown: false,
|
||||
screensharing: false,
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
};
|
||||
|
||||
it("should render the buttons", () => {
|
||||
const { container } = renderButtons();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`LegacyCallViewButtons should render the buttons 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_LegacyCallViewButtons"
|
||||
>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="Dialpad"
|
||||
class="mx_AccessibleButton mx_LegacyCallViewButtons_button"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 18.6c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8M6.6 2.4c-.99 0-1.8.81-1.8 1.8S5.61 6 6.6 6s1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0 5.4c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0 5.4c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8M17.4 6c.99 0 1.8-.81 1.8-1.8s-.81-1.8-1.8-1.8-1.8.81-1.8 1.8.81 1.8 1.8 1.8M12 13.2c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m5.4 0c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0-5.4c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m-5.4 0c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0-5.4c-.99 0-1.8.81-1.8 1.8S11.01 6 12 6s1.8-.81 1.8-1.8-.81-1.8-1.8-1.8"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Mute microphone"
|
||||
class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_mic mx_LegacyCallViewButtons_button_on"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8 6a4 4 0 1 1 8 0v6a4 4 0 0 1-8 0z"
|
||||
/>
|
||||
<path
|
||||
d="M5 11a1 1 0 0 1 1 1 6 6 0 0 0 12 0 1 1 0 1 1 2 0 8 8 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8 8 0 0 1 4 12a1 1 0 0 1 1-1"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_dropdownButton mx_LegacyCallViewButtons_button_on"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m12 10.775-3.9 3.9a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275.95.95 0 0 1-.275-.7q0-.425.275-.7l4.6-4.6q.15-.15.325-.212Q11.8 8.4 12 8.4t.375.063a.9.9 0 0 1 .325.212l4.6 4.6a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Turn off camera"
|
||||
class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_vid mx_LegacyCallViewButtons_button_on"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 4h10a2 2 0 0 1 2 2v4.286l3.35-2.871a1 1 0 0 1 1.65.76v7.65a1 1 0 0 1-1.65.76L18 13.715V18a2 2 0 0 1-2 2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_dropdownButton mx_LegacyCallViewButtons_button_on"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m12 10.775-3.9 3.9a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275.95.95 0 0 1-.275-.7q0-.425.275-.7l4.6-4.6q.15-.15.325-.212Q11.8 8.4 12 8.4t.375.063a.9.9 0 0 1 .325.212l4.6 4.6a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Start sharing your screen"
|
||||
class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_screensharing mx_LegacyCallViewButtons_button_off"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M1.288 20.713Q1.575 21 2 21h20q.424 0 .712-.288A.97.97 0 0 0 23 20a.97.97 0 0 0-.288-.712A.97.97 0 0 0 22 19H2a.97.97 0 0 0-.712.288A.97.97 0 0 0 1 20q0 .424.288.712m1.3-3.299A1.93 1.93 0 0 1 2 16V5q0-.824.587-1.412A1.93 1.93 0 0 1 4 3h16q.824 0 1.413.587Q22 4.176 22 5v11q0 .824-.587 1.413A1.93 1.93 0 0 1 20 18H4q-.824 0-1.412-.587m10.12-10.12a1 1 0 0 0-1.415 0l-2.5 2.5a1 1 0 0 0 1.414 1.414l.793-.793V13a1 1 0 1 0 2 0v-2.586l.793.793a1 1 0 0 0 1.414-1.414z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Show sidebar"
|
||||
class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_sidebar mx_LegacyCallViewButtons_button_off"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M20 6H4v12h16zM4 4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="More"
|
||||
class="mx_AccessibleButton mx_LegacyCallViewButtons_button"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 4 12q0-.825.588-1.412A1.93 1.93 0 0 1 6 10q.824 0 1.412.588Q8 11.175 8 12t-.588 1.412A1.93 1.93 0 0 1 6 14m6 0q-.825 0-1.412-.588A1.93 1.93 0 0 1 10 12q0-.825.588-1.412A1.93 1.93 0 0 1 12 10q.825 0 1.412.588Q14 11.175 14 12t-.588 1.412A1.93 1.93 0 0 1 12 14m6 0q-.824 0-1.413-.588A1.93 1.93 0 0 1 16 12q0-.825.587-1.412A1.93 1.93 0 0 1 18 10q.824 0 1.413.588Q20 11.175 20 12t-.587 1.412A1.93 1.93 0 0 1 18 14"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Hangup"
|
||||
class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_hangup"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m2.765 16.02-2.47-2.416A1.02 1.02 0 0 1 0 12.852q0-.456.295-.751a15.6 15.6 0 0 1 5.316-3.786A15.9 15.9 0 0 1 12 7q3.355 0 6.39 1.329a16 16 0 0 1 5.315 3.772q.295.294.295.751t-.295.752l-2.47 2.416a1.047 1.047 0 0 1-1.396.108l-3.114-2.363a1.1 1.1 0 0 1-.322-.376 1.1 1.1 0 0 1-.108-.483v-2.27a13.6 13.6 0 0 0-2.12-.524C13.459 9.996 12 9.937 12 9.937s-1.459.059-2.174.175q-1.074.174-2.121.523v2.271q0 .268-.108.483a1.1 1.1 0 0 1-.322.376l-3.114 2.363a1.047 1.047 0 0 1-1.396-.107"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
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 { fireEvent, render, waitFor } from "jest-matrix-react";
|
||||
import { MatrixCall } from "matrix-js-sdk/src/webrtc/call";
|
||||
import { CallEventHandlerEvent } from "matrix-js-sdk/src/webrtc/callEventHandler";
|
||||
|
||||
import LegacyCallView from "../../../../../src/components/views/voip/LegacyCallView";
|
||||
import LegacyCallViewForRoom from "../../../../../src/components/views/voip/LegacyCallViewForRoom";
|
||||
import { mkStubRoom, stubClient } from "../../../../test-utils";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import LegacyCallHandler from "../../../../../src/LegacyCallHandler";
|
||||
import { SDKContext, SdkContextClass } from "../../../../../src/contexts/SDKContext";
|
||||
|
||||
jest.mock("../../../../../src/components/views/voip/LegacyCallView", () => jest.fn(() => "LegacyCallView"));
|
||||
|
||||
describe("LegacyCallViewForRoom", () => {
|
||||
const LegacyCallViewMock = LegacyCallView as unknown as jest.Mock;
|
||||
let sdkContext: SdkContextClass;
|
||||
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
sdkContext = new SdkContextClass();
|
||||
LegacyCallViewMock.mockClear();
|
||||
});
|
||||
|
||||
it("should remember sidebar state, defaulting to shown", async () => {
|
||||
const callHandler = new LegacyCallHandler();
|
||||
callHandler.start();
|
||||
jest.spyOn(LegacyCallHandler, "instance", "get").mockImplementation(() => callHandler);
|
||||
|
||||
const call = new MatrixCall({
|
||||
client: MatrixClientPeg.safeGet(),
|
||||
roomId: "test-room",
|
||||
});
|
||||
DMRoomMap.setShared({
|
||||
getUserIdForRoomId: jest.fn().mockReturnValue("test-user"),
|
||||
} as unknown as DMRoomMap);
|
||||
|
||||
const room = mkStubRoom(call.roomId, "room", MatrixClientPeg.safeGet());
|
||||
MatrixClientPeg.safeGet().getRoom = jest.fn().mockReturnValue(room);
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
cli.emit(CallEventHandlerEvent.Incoming, call);
|
||||
|
||||
const { rerender } = render(<LegacyCallViewForRoom roomId={call.roomId} />);
|
||||
|
||||
let props = LegacyCallViewMock.mock.lastCall![0];
|
||||
expect(props.sidebarShown).toBeTruthy(); // Sidebar defaults to shown
|
||||
|
||||
props.setSidebarShown(false); // Hide the sidebar
|
||||
|
||||
rerender(<LegacyCallViewForRoom roomId={call.roomId} />);
|
||||
|
||||
console.log(LegacyCallViewMock.mock);
|
||||
|
||||
props = LegacyCallViewMock.mock.lastCall![0];
|
||||
expect(props.sidebarShown).toBeFalsy();
|
||||
|
||||
rerender(<div> </div>); // Destroy the LegacyCallViewForRoom and LegacyCallView
|
||||
LegacyCallViewMock.mockClear(); // Drop stored LegacyCallView props
|
||||
|
||||
rerender(<LegacyCallViewForRoom roomId={call.roomId} />);
|
||||
|
||||
props = LegacyCallViewMock.mock.lastCall![0];
|
||||
expect(props.sidebarShown).toBeFalsy(); // Value was remembered
|
||||
});
|
||||
|
||||
it("should notify on resize start events", async () => {
|
||||
const call = new MatrixCall({
|
||||
client: MatrixClientPeg.safeGet(),
|
||||
roomId: "test-room",
|
||||
});
|
||||
|
||||
const callHandler = {
|
||||
getCallForRoom: jest.fn().mockReturnValue(call),
|
||||
isCallSidebarShown: jest.fn().mockReturnValue(true),
|
||||
addListener: jest.fn(),
|
||||
removeListener: jest.fn(),
|
||||
};
|
||||
jest.spyOn(LegacyCallHandler, "instance", "get").mockImplementation(
|
||||
() => callHandler as unknown as LegacyCallHandler,
|
||||
);
|
||||
|
||||
jest.spyOn(sdkContext.resizeNotifier, "startResizing");
|
||||
jest.spyOn(sdkContext.resizeNotifier, "stopResizing");
|
||||
jest.spyOn(sdkContext.resizeNotifier, "notifyTimelineHeightChanged");
|
||||
|
||||
const { container } = render(<LegacyCallViewForRoom roomId={call.roomId} />, {
|
||||
wrapper: ({ children }) => <SDKContext.Provider value={sdkContext}>{children}</SDKContext.Provider>,
|
||||
});
|
||||
|
||||
const resizer = container.querySelector(".mx_LegacyCallViewForRoom_ResizeHandle");
|
||||
await waitFor(() => {
|
||||
expect(resizer).toBeInTheDocument();
|
||||
});
|
||||
|
||||
fireEvent.mouseDown(resizer!);
|
||||
fireEvent.mouseMove(resizer!, { clientY: 100 });
|
||||
fireEvent.mouseUp(resizer!);
|
||||
|
||||
expect(sdkContext.resizeNotifier.startResizing).toHaveBeenCalled();
|
||||
expect(sdkContext.resizeNotifier.stopResizing).toHaveBeenCalled();
|
||||
expect(sdkContext.resizeNotifier.notifyTimelineHeightChanged).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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 } from "jest-matrix-react";
|
||||
import { type CallFeed } from "matrix-js-sdk/src/webrtc/callFeed";
|
||||
import { type MatrixCall } from "matrix-js-sdk/src/webrtc/call";
|
||||
import { type MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import * as AvatarModule from "../../../../../src/Avatar";
|
||||
import VideoFeed from "../../../../../src/components/views/voip/VideoFeed";
|
||||
import { stubClient, useMockedCalls } from "../../../../test-utils";
|
||||
import type LegacyCallHandler from "../../../../../src/LegacyCallHandler";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
|
||||
const FAKE_AVATAR_URL = "http://fakeurl.dummy/fake.png";
|
||||
|
||||
describe("VideoFeed", () => {
|
||||
useMockedCalls();
|
||||
|
||||
let client: MatrixClient;
|
||||
|
||||
beforeAll(() => {
|
||||
client = stubClient();
|
||||
(AvatarModule as any).avatarUrlForRoom = jest.fn().mockReturnValue(FAKE_AVATAR_URL);
|
||||
|
||||
const dmRoomMap = new DMRoomMap(client);
|
||||
jest.spyOn(dmRoomMap, "getUserIdForRoomId");
|
||||
jest.spyOn(DMRoomMap, "shared").mockReturnValue(dmRoomMap);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("Displays the room avatar when no video is available", () => {
|
||||
window.mxLegacyCallHandler = {
|
||||
roomIdForCall: jest.fn().mockReturnValue("!this:room.here"),
|
||||
} as unknown as LegacyCallHandler;
|
||||
|
||||
const mockCall = {
|
||||
room: new Room("!room:example.com", client, client.getSafeUserId()),
|
||||
};
|
||||
|
||||
const feed = {
|
||||
isAudioMuted: jest.fn().mockReturnValue(false),
|
||||
isVideoMuted: jest.fn().mockReturnValue(true),
|
||||
addListener: jest.fn(),
|
||||
removeListener: jest.fn(),
|
||||
};
|
||||
render(<VideoFeed feed={feed as unknown as CallFeed} call={mockCall as unknown as MatrixCall} />);
|
||||
const avatarImg = screen.getByRole("presentation");
|
||||
expect(avatarImg).toHaveAttribute("src", FAKE_AVATAR_URL);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user