Prepare for repo merge

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2024-10-15 11:35:21 +01:00
parent 0f670b8dc0
commit b084ff2313
No known key found for this signature in database
GPG key ID: A2B008A5F49F5D0D
807 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,171 @@
/*
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
Please see LICENSE files in the repository root for full details.
*/
import React, { ReactElement } from "react";
import { act, render, screen } from "jest-matrix-react";
import { mocked } from "jest-mock";
import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
import {
VoiceBroadcastBody as UnwrappedVoiceBroadcastBody,
VoiceBroadcastInfoState,
VoiceBroadcastRecordingBody,
VoiceBroadcastRecording,
VoiceBroadcastPlaybackBody,
VoiceBroadcastPlayback,
VoiceBroadcastRecordingsStore,
} from "../../../src/voice-broadcast";
import { withClientContextRenderOptions, stubClient, wrapInSdkContext } from "../../test-utils";
import { mkVoiceBroadcastInfoStateEvent } from "../utils/test-utils";
import { MediaEventHelper } from "../../../src/utils/MediaEventHelper";
import { RoomPermalinkCreator } from "../../../src/utils/permalinks/Permalinks";
import { SdkContextClass } from "../../../src/contexts/SDKContext";
jest.mock("../../../src/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody", () => ({
VoiceBroadcastRecordingBody: jest.fn(),
}));
jest.mock("../../../src/voice-broadcast/components/molecules/VoiceBroadcastPlaybackBody", () => ({
VoiceBroadcastPlaybackBody: jest.fn(),
}));
jest.mock("../../../src/utils/permalinks/Permalinks");
jest.mock("../../../src/utils/MediaEventHelper");
jest.mock("../../../src/stores/WidgetStore");
jest.mock("../../../src/stores/widgets/WidgetLayoutStore");
describe("VoiceBroadcastBody", () => {
const roomId = "!room:example.com";
let userId: string;
let deviceId: string;
let client: MatrixClient;
let room: Room;
let infoEvent: MatrixEvent;
let stoppedEvent: MatrixEvent;
let testRecording: VoiceBroadcastRecording;
let testPlayback: VoiceBroadcastPlayback;
const renderVoiceBroadcast = () => {
const VoiceBroadcastBody = wrapInSdkContext(UnwrappedVoiceBroadcastBody, SdkContextClass.instance);
render(
<VoiceBroadcastBody
mxEvent={infoEvent}
mediaEventHelper={new MediaEventHelper(infoEvent)}
onHeightChanged={() => {}}
onMessageAllowed={() => {}}
permalinkCreator={new RoomPermalinkCreator(room)}
/>,
withClientContextRenderOptions(client),
);
testRecording = SdkContextClass.instance.voiceBroadcastRecordingsStore.getByInfoEvent(infoEvent, client);
};
beforeEach(() => {
client = stubClient();
userId = client.getUserId() || "";
deviceId = client.getDeviceId() || "";
mocked(client.relations).mockClear();
mocked(client.relations).mockResolvedValue({ events: [] });
room = new Room(roomId, client, userId);
mocked(client.getRoom).mockImplementation((getRoomId?: string) => {
if (getRoomId === roomId) return room;
return null;
});
infoEvent = mkVoiceBroadcastInfoStateEvent(roomId, VoiceBroadcastInfoState.Started, userId, deviceId);
stoppedEvent = mkVoiceBroadcastInfoStateEvent(
roomId,
VoiceBroadcastInfoState.Stopped,
userId,
deviceId,
infoEvent,
);
room.addEventsToTimeline([infoEvent], true, room.getLiveTimeline());
testRecording = new VoiceBroadcastRecording(infoEvent, client);
testPlayback = new VoiceBroadcastPlayback(infoEvent, client, new VoiceBroadcastRecordingsStore());
mocked(VoiceBroadcastRecordingBody).mockImplementation(({ recording }): ReactElement | null => {
if (testRecording === recording) {
return <div data-testid="voice-broadcast-recording-body" />;
}
return null;
});
mocked(VoiceBroadcastPlaybackBody).mockImplementation(({ playback }): ReactElement | null => {
if (testPlayback === playback) {
return <div data-testid="voice-broadcast-playback-body" />;
}
return null;
});
jest.spyOn(SdkContextClass.instance.voiceBroadcastRecordingsStore, "getByInfoEvent").mockImplementation(
(getEvent: MatrixEvent, getClient: MatrixClient): VoiceBroadcastRecording => {
if (getEvent === infoEvent && getClient === client) {
return testRecording;
}
throw new Error("unexpected event");
},
);
jest.spyOn(SdkContextClass.instance.voiceBroadcastPlaybacksStore, "getByInfoEvent").mockImplementation(
(getEvent: MatrixEvent): VoiceBroadcastPlayback => {
if (getEvent === infoEvent) {
return testPlayback;
}
throw new Error("unexpected event");
},
);
});
describe("when there is a stopped voice broadcast", () => {
beforeEach(() => {
room.addEventsToTimeline([stoppedEvent], true, room.getLiveTimeline());
renderVoiceBroadcast();
});
it("should render a voice broadcast playback body", () => {
screen.getByTestId("voice-broadcast-playback-body");
});
});
describe("when there is a started voice broadcast from the current user", () => {
beforeEach(() => {
renderVoiceBroadcast();
});
it("should render a voice broadcast recording body", () => {
screen.getByTestId("voice-broadcast-recording-body");
});
describe("and the recordings ends", () => {
beforeEach(() => {
act(() => {
room.addEventsToTimeline([stoppedEvent], true, room.getLiveTimeline());
});
});
it("should render a voice broadcast playback body", () => {
screen.getByTestId("voice-broadcast-playback-body");
});
});
});
describe("when displaying a voice broadcast playback", () => {
beforeEach(() => {
mocked(client).getUserId.mockReturnValue("@other:example.com");
renderVoiceBroadcast();
});
it("should render a voice broadcast playback body", () => {
screen.getByTestId("voice-broadcast-playback-body");
});
});
});

View file

@ -0,0 +1,24 @@
/*
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
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { render } from "jest-matrix-react";
import { LiveBadge } from "../../../../src/voice-broadcast";
describe("LiveBadge", () => {
it("should render as expected with default props", () => {
const { container } = render(<LiveBadge />);
expect(container).toMatchSnapshot();
});
it("should render in grey as expected", () => {
const { container } = render(<LiveBadge grey={true} />);
expect(container).toMatchSnapshot();
});
});

View file

@ -0,0 +1,44 @@
/*
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
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { render, RenderResult, screen } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import { VoiceBroadcastControl } from "../../../../src/voice-broadcast";
import { Icon as StopIcon } from "../../../../res/img/compound/stop-16.svg";
describe("VoiceBroadcastControl", () => {
let result: RenderResult;
let onClick: () => void;
beforeEach(() => {
onClick = jest.fn();
});
describe("when rendering it", () => {
beforeEach(() => {
const stopIcon = <StopIcon className="mx_Icon mx_Icon_16" />;
result = render(<VoiceBroadcastControl onClick={onClick} label="test label" icon={stopIcon} />);
});
it("should render as expected", () => {
expect(result.container).toMatchSnapshot();
});
describe("when clicking it", () => {
beforeEach(async () => {
await userEvent.click(screen.getByLabelText("test label"));
});
it("should call onClick", () => {
expect(onClick).toHaveBeenCalled();
});
});
});
});

View file

@ -0,0 +1,89 @@
/*
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
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { MatrixClient, Room, RoomMember } from "matrix-js-sdk/src/matrix";
import { render, RenderResult } from "jest-matrix-react";
import { VoiceBroadcastHeader, VoiceBroadcastLiveness } from "../../../../src/voice-broadcast";
import { mkRoom, stubClient } from "../../../test-utils";
// mock RoomAvatar, because it is doing too much fancy stuff
jest.mock("../../../../src/components/views/avatars/RoomAvatar", () => ({
__esModule: true,
default: jest.fn().mockImplementation(({ room }) => {
return <div data-testid="room-avatar">room avatar: {room.name}</div>;
}),
}));
describe("VoiceBroadcastHeader", () => {
const userId = "@user:example.com";
const roomId = "!room:example.com";
let client: MatrixClient;
let room: Room;
const sender = new RoomMember(roomId, userId);
let container: RenderResult["container"];
const renderHeader = (live: VoiceBroadcastLiveness, showBroadcast?: boolean, buffering?: boolean): RenderResult => {
return render(
<VoiceBroadcastHeader
live={live}
microphoneLabel={sender.name}
room={room}
showBroadcast={showBroadcast}
showBuffering={buffering}
/>,
);
};
beforeAll(() => {
client = stubClient();
room = mkRoom(client, roomId);
sender.name = "test user";
});
describe("when rendering a live broadcast header with broadcast info", () => {
beforeEach(() => {
container = renderHeader("live", true, true).container;
});
it("should render the header with a red live badge", () => {
expect(container).toMatchSnapshot();
});
});
describe("when rendering a buffering live broadcast header with broadcast info", () => {
beforeEach(() => {
container = renderHeader("live", true).container;
});
it("should render the header with a red live badge", () => {
expect(container).toMatchSnapshot();
});
});
describe("when rendering a live (grey) broadcast header with broadcast info", () => {
beforeEach(() => {
container = renderHeader("grey", true).container;
});
it("should render the header with a grey live badge", () => {
expect(container).toMatchSnapshot();
});
});
describe("when rendering a non-live broadcast header", () => {
beforeEach(() => {
container = renderHeader("not-live").container;
});
it("should render the header without a live badge", () => {
expect(container).toMatchSnapshot();
});
});
});

View file

@ -0,0 +1,51 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { render, RenderResult, screen } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import { VoiceBroadcastPlaybackControl, VoiceBroadcastPlaybackState } from "../../../../src/voice-broadcast";
describe("<VoiceBroadcastPlaybackControl />", () => {
const renderControl = (state: VoiceBroadcastPlaybackState): { result: RenderResult; onClick: () => void } => {
const onClick = jest.fn();
return {
onClick,
result: render(<VoiceBroadcastPlaybackControl state={state} onClick={onClick} />),
};
};
it.each([
VoiceBroadcastPlaybackState.Stopped,
VoiceBroadcastPlaybackState.Paused,
VoiceBroadcastPlaybackState.Buffering,
VoiceBroadcastPlaybackState.Playing,
])("should render state %s as expected", (state: VoiceBroadcastPlaybackState) => {
expect(renderControl(state).result.container).toMatchSnapshot();
});
it("should not render for error state", () => {
expect(renderControl(VoiceBroadcastPlaybackState.Error).result.asFragment()).toMatchInlineSnapshot(
`<DocumentFragment />`,
);
});
describe("when clicking the control", () => {
let onClick: () => void;
beforeEach(async () => {
onClick = renderControl(VoiceBroadcastPlaybackState.Playing).onClick;
await userEvent.click(screen.getByLabelText("pause voice broadcast"));
});
it("should invoke the onClick callback", () => {
expect(onClick).toHaveBeenCalled();
});
});
});

View file

@ -0,0 +1,27 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LiveBadge should render as expected with default props 1`] = `
<div>
<div
class="mx_LiveBadge"
>
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
</div>
`;
exports[`LiveBadge should render in grey as expected 1`] = `
<div>
<div
class="mx_LiveBadge mx_LiveBadge--grey"
>
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
</div>
`;

View file

@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`VoiceBroadcastControl when rendering it should render as expected 1`] = `
<div>
<div
aria-label="test label"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_16"
/>
</div>
</div>
`;

View file

@ -0,0 +1,277 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`VoiceBroadcastHeader when rendering a buffering live broadcast header with broadcast info should render the header with a red live badge 1`] = `
<div>
<div
class="mx_VoiceBroadcastHeader"
>
<div
data-testid="room-avatar"
>
room avatar:
!room:example.com
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
!room:example.com
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
test user
</span>
</div>
<div
class="mx_VoiceBroadcastHeader_line"
>
<div
class="mx_Icon mx_Icon_16"
/>
Voice broadcast
</div>
</div>
<div
class="mx_LiveBadge"
>
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
</div>
</div>
`;
exports[`VoiceBroadcastHeader when rendering a live (grey) broadcast header with broadcast info should render the header with a grey live badge 1`] = `
<div>
<div
class="mx_VoiceBroadcastHeader"
>
<div
data-testid="room-avatar"
>
room avatar:
!room:example.com
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
!room:example.com
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
test user
</span>
</div>
<div
class="mx_VoiceBroadcastHeader_line"
>
<div
class="mx_Icon mx_Icon_16"
/>
Voice broadcast
</div>
</div>
<div
class="mx_LiveBadge mx_LiveBadge--grey"
>
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
</div>
</div>
`;
exports[`VoiceBroadcastHeader when rendering a live broadcast header with broadcast info should render the header with a red live badge 1`] = `
<div>
<div
class="mx_VoiceBroadcastHeader"
>
<div
data-testid="room-avatar"
>
room avatar:
!room:example.com
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
!room:example.com
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
test user
</span>
</div>
<div
class="mx_VoiceBroadcastHeader_line"
>
<div
class="mx_Icon mx_Icon_16"
/>
Voice broadcast
</div>
<div
class="mx_VoiceBroadcastHeader_line"
>
<div
class="mx_Spinner"
>
<div
aria-label="Loading…"
class="mx_Spinner_icon"
data-testid="spinner"
role="progressbar"
style="width: 14px; height: 14px;"
/>
</div>
Buffering…
</div>
</div>
<div
class="mx_LiveBadge"
>
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
</div>
</div>
`;
exports[`VoiceBroadcastHeader when rendering a non-live broadcast header should render the header without a live badge 1`] = `
<div>
<div
class="mx_VoiceBroadcastHeader"
>
<div
data-testid="room-avatar"
>
room avatar:
!room:example.com
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
!room:example.com
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
test user
</span>
</div>
</div>
</div>
</div>
`;

View file

@ -0,0 +1,97 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<VoiceBroadcastPlaybackControl /> should render state buffering as expected 1`] = `
<div>
<div
aria-label="pause voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_12"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 4a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Zm8 0a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Z"
/>
</svg>
</div>
</div>
`;
exports[`<VoiceBroadcastPlaybackControl /> should render state pause as expected 1`] = `
<div>
<div
aria-label="resume voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl mx_VoiceBroadcastControl-play"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m8.98 4.677 9.921 5.58c1.36.764 1.36 2.722 0 3.486l-9.92 5.58C7.647 20.073 6 19.11 6 17.58V6.42c0-1.53 1.647-2.493 2.98-1.743Z"
/>
</svg>
</div>
</div>
`;
exports[`<VoiceBroadcastPlaybackControl /> should render state playing as expected 1`] = `
<div>
<div
aria-label="pause voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_12"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 4a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Zm8 0a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Z"
/>
</svg>
</div>
</div>
`;
exports[`<VoiceBroadcastPlaybackControl /> should render state stopped as expected 1`] = `
<div>
<div
aria-label="play voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl mx_VoiceBroadcastControl-play"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m8.98 4.677 9.921 5.58c1.36.764 1.36 2.722 0 3.486l-9.92 5.58C7.647 20.073 6 19.11 6 17.58V6.42c0-1.53 1.647-2.493 2.98-1.743Z"
/>
</svg>
</div>
</div>
`;

View file

@ -0,0 +1,249 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
import { act, render, RenderResult, screen } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import { mocked } from "jest-mock";
import {
VoiceBroadcastInfoState,
VoiceBroadcastLiveness,
VoiceBroadcastPlayback,
VoiceBroadcastPlaybackBody,
VoiceBroadcastPlaybackEvent,
VoiceBroadcastPlaybackState,
} from "../../../../src/voice-broadcast";
import { filterConsole, stubClient } from "../../../test-utils";
import { mkVoiceBroadcastInfoStateEvent } from "../../utils/test-utils";
import dis from "../../../../src/dispatcher/dispatcher";
import { Action } from "../../../../src/dispatcher/actions";
import { SdkContextClass } from "../../../../src/contexts/SDKContext";
jest.mock("../../../../src/dispatcher/dispatcher");
// mock RoomAvatar, because it is doing too much fancy stuff
jest.mock("../../../../src/components/views/avatars/RoomAvatar", () => ({
__esModule: true,
default: jest.fn().mockImplementation(({ room }) => {
return <div data-testid="room-avatar">room avatar: {room.name}</div>;
}),
}));
describe("VoiceBroadcastPlaybackBody", () => {
const userId = "@user:example.com";
const roomId = "!room:example.com";
const duration = 23 * 60 + 42; // 23:42
let client: MatrixClient;
let infoEvent: MatrixEvent;
let playback: VoiceBroadcastPlayback;
let renderResult: RenderResult;
filterConsole(
// expected for some tests
"voice broadcast chunk event to skip to not found",
);
beforeAll(() => {
client = stubClient();
mocked(client.relations).mockClear();
mocked(client.relations).mockResolvedValue({ events: [] });
infoEvent = mkVoiceBroadcastInfoStateEvent(
roomId,
VoiceBroadcastInfoState.Stopped,
userId,
client.getDeviceId(),
);
});
beforeEach(() => {
playback = new VoiceBroadcastPlayback(
infoEvent,
client,
SdkContextClass.instance.voiceBroadcastRecordingsStore,
);
jest.spyOn(playback, "toggle").mockImplementation(() => Promise.resolve());
jest.spyOn(playback, "getLiveness");
jest.spyOn(playback, "getState");
jest.spyOn(playback, "skipTo");
jest.spyOn(playback, "durationSeconds", "get").mockReturnValue(duration);
});
describe("when rendering a buffering voice broadcast", () => {
beforeEach(() => {
mocked(playback.getState).mockReturnValue(VoiceBroadcastPlaybackState.Buffering);
mocked(playback.getLiveness).mockReturnValue("live");
renderResult = render(<VoiceBroadcastPlaybackBody playback={playback} />);
});
it("should render as expected", () => {
expect(renderResult.container).toMatchSnapshot();
});
});
describe("when rendering a playing broadcast", () => {
beforeEach(() => {
mocked(playback.getState).mockReturnValue(VoiceBroadcastPlaybackState.Playing);
mocked(playback.getLiveness).mockReturnValue("not-live");
renderResult = render(<VoiceBroadcastPlaybackBody playback={playback} />);
});
it("should render as expected", () => {
expect(renderResult.container).toMatchSnapshot();
});
describe("and being in the middle of the playback", () => {
beforeEach(() => {
act(() => {
playback.emit(VoiceBroadcastPlaybackEvent.TimesChanged, {
duration,
position: 10 * 60,
timeLeft: duration - 10 * 60,
});
});
});
describe("and clicking 30s backward", () => {
beforeEach(async () => {
await act(async () => {
await userEvent.click(screen.getByLabelText("30s backward"));
});
});
it("should seek 30s backward", () => {
expect(playback.skipTo).toHaveBeenCalledWith(9 * 60 + 30);
});
});
describe("and clicking 30s forward", () => {
beforeEach(async () => {
await act(async () => {
await userEvent.click(screen.getByLabelText("30s forward"));
});
});
it("should seek 30s forward", () => {
expect(playback.skipTo).toHaveBeenCalledWith(10 * 60 + 30);
});
});
});
describe("and clicking the room name", () => {
beforeEach(async () => {
await userEvent.click(screen.getByText("My room"));
});
it("should not view the room", () => {
expect(dis.dispatch).not.toHaveBeenCalled();
});
});
});
describe("when rendering a playing broadcast in pip mode", () => {
beforeEach(() => {
mocked(playback.getState).mockReturnValue(VoiceBroadcastPlaybackState.Playing);
mocked(playback.getLiveness).mockReturnValue("not-live");
renderResult = render(<VoiceBroadcastPlaybackBody pip={true} playback={playback} />);
});
it("should render as expected", () => {
expect(renderResult.container).toMatchSnapshot();
});
describe("and clicking the room name", () => {
beforeEach(async () => {
await userEvent.click(screen.getByText("My room"));
});
it("should view the room", () => {
expect(dis.dispatch).toHaveBeenCalledWith({
action: Action.ViewRoom,
room_id: roomId,
metricsTrigger: undefined,
});
});
});
});
describe(`when rendering a stopped broadcast`, () => {
beforeEach(() => {
mocked(playback.getState).mockReturnValue(VoiceBroadcastPlaybackState.Stopped);
mocked(playback.getLiveness).mockReturnValue("not-live");
renderResult = render(<VoiceBroadcastPlaybackBody playback={playback} />);
});
it("should render as expected", () => {
expect(renderResult.container).toMatchSnapshot();
});
describe("and clicking the play button", () => {
beforeEach(async () => {
await userEvent.click(renderResult.getByLabelText("play voice broadcast"));
});
it("should toggle the recording", () => {
expect(playback.toggle).toHaveBeenCalled();
});
});
describe("and the times update", () => {
beforeEach(() => {
act(() => {
playback.emit(VoiceBroadcastPlaybackEvent.TimesChanged, {
duration,
position: 5 * 60 + 13,
timeLeft: 7 * 60 + 5,
});
});
});
it("should render the times", async () => {
expect(await screen.findByText("05:13")).toBeInTheDocument();
expect(await screen.findByText("-07:05")).toBeInTheDocument();
});
});
});
describe("when rendering an error broadcast", () => {
beforeEach(() => {
mocked(playback.getState).mockReturnValue(VoiceBroadcastPlaybackState.Error);
renderResult = render(<VoiceBroadcastPlaybackBody playback={playback} />);
});
it("should render as expected", () => {
expect(renderResult.container).toMatchSnapshot();
});
});
describe.each([
[VoiceBroadcastPlaybackState.Paused, "not-live"],
[VoiceBroadcastPlaybackState.Playing, "live"],
] satisfies [VoiceBroadcastPlaybackState, VoiceBroadcastLiveness][])(
"when rendering a %s/%s broadcast",
(state: VoiceBroadcastPlaybackState, liveness: VoiceBroadcastLiveness) => {
beforeEach(() => {
mocked(playback.getState).mockReturnValue(state);
mocked(playback.getLiveness).mockReturnValue(liveness);
renderResult = render(<VoiceBroadcastPlaybackBody playback={playback} />);
});
it("should render as expected", () => {
expect(renderResult.container).toMatchSnapshot();
});
},
);
it("when there is a broadcast without sender, it should raise an error", () => {
infoEvent.sender = null;
expect(() => {
render(<VoiceBroadcastPlaybackBody playback={playback} />);
}).toThrow(`Voice Broadcast sender not found (event ${playback.infoEvent.getId()})`);
});
});

View file

@ -0,0 +1,165 @@
/*
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
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { mocked } from "jest-mock";
import { MatrixClient, Room, RoomMember } from "matrix-js-sdk/src/matrix";
import { act, render, RenderResult, screen } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import {
VoiceBroadcastPlaybacksStore,
VoiceBroadcastPreRecording,
VoiceBroadcastPreRecordingPip,
VoiceBroadcastRecordingsStore,
} from "../../../../src/voice-broadcast";
import { flushPromises, stubClient } from "../../../test-utils";
import { requestMediaPermissions } from "../../../../src/utils/media/requestMediaPermissions";
import MediaDeviceHandler, { MediaDeviceKindEnum } from "../../../../src/MediaDeviceHandler";
import dis from "../../../../src/dispatcher/dispatcher";
import { Action } from "../../../../src/dispatcher/actions";
jest.mock("../../../../src/dispatcher/dispatcher");
jest.mock("../../../../src/utils/media/requestMediaPermissions");
// mock RoomAvatar, because it is doing too much fancy stuff
jest.mock("../../../../src/components/views/avatars/RoomAvatar", () => ({
__esModule: true,
default: jest.fn().mockImplementation(({ room }) => {
return <div data-testid="room-avatar">room avatar: {room.name}</div>;
}),
}));
describe("VoiceBroadcastPreRecordingPip", () => {
let renderResult: RenderResult;
let preRecording: VoiceBroadcastPreRecording;
let playbacksStore: VoiceBroadcastPlaybacksStore;
let recordingsStore: VoiceBroadcastRecordingsStore;
let client: MatrixClient;
let room: Room;
let sender: RoomMember;
const itShouldShowTheBroadcastRoom = () => {
it("should show the broadcast room", () => {
expect(dis.dispatch).toHaveBeenCalledWith({
action: Action.ViewRoom,
room_id: room.roomId,
metricsTrigger: undefined,
});
});
};
beforeEach(() => {
client = stubClient();
room = new Room("!room@example.com", client, client.getUserId() || "");
sender = new RoomMember(room.roomId, client.getUserId() || "");
recordingsStore = new VoiceBroadcastRecordingsStore();
playbacksStore = new VoiceBroadcastPlaybacksStore(recordingsStore);
mocked(requestMediaPermissions).mockResolvedValue({
getTracks: (): Array<MediaStreamTrack> => [],
} as unknown as MediaStream);
jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({
[MediaDeviceKindEnum.AudioInput]: [
{
deviceId: "d1",
label: "Device 1",
} as MediaDeviceInfo,
{
deviceId: "d2",
label: "Device 2",
} as MediaDeviceInfo,
],
[MediaDeviceKindEnum.AudioOutput]: [],
[MediaDeviceKindEnum.VideoInput]: [],
});
jest.spyOn(MediaDeviceHandler.instance, "setDevice").mockImplementation();
preRecording = new VoiceBroadcastPreRecording(room, sender, client, playbacksStore, recordingsStore);
jest.spyOn(preRecording, "start").mockResolvedValue();
});
afterAll(() => {
jest.resetAllMocks();
});
describe("when rendered", () => {
beforeEach(async () => {
renderResult = render(<VoiceBroadcastPreRecordingPip voiceBroadcastPreRecording={preRecording} />);
await act(async () => {
flushPromises();
});
});
it("should match the snapshot", () => {
expect(renderResult.container).toMatchSnapshot();
});
describe("and double clicking »Go live«", () => {
beforeEach(async () => {
await userEvent.click(screen.getByText("Go live"));
await userEvent.click(screen.getByText("Go live"));
});
it("should call start once", () => {
expect(preRecording.start).toHaveBeenCalledTimes(1);
});
});
describe("and clicking the room name", () => {
beforeEach(async () => {
await userEvent.click(screen.getByText(room.name));
});
itShouldShowTheBroadcastRoom();
});
describe("and clicking the room avatar", () => {
beforeEach(async () => {
await userEvent.click(screen.getByText(`room avatar: ${room.name}`));
});
itShouldShowTheBroadcastRoom();
});
describe("and clicking the device label", () => {
beforeEach(async () => {
await act(async () => {
await userEvent.click(screen.getByText("Default Device"));
});
});
it("should display the device selection", () => {
expect(screen.queryAllByText("Default Device").length).toBe(2);
expect(screen.queryByText("Device 1")).toBeInTheDocument();
expect(screen.queryByText("Device 2")).toBeInTheDocument();
});
describe("and selecting a device", () => {
beforeEach(async () => {
await act(async () => {
await userEvent.click(screen.getByText("Device 1"));
});
});
it("should set it as current device", () => {
expect(MediaDeviceHandler.instance.setDevice).toHaveBeenCalledWith(
"d1",
MediaDeviceKindEnum.AudioInput,
);
});
it("should not show the device selection", () => {
expect(screen.queryByText("Default Device")).not.toBeInTheDocument();
// expected to be one in the document, displayed in the pip directly
expect(screen.queryByText("Device 1")).toBeInTheDocument();
expect(screen.queryByText("Device 2")).not.toBeInTheDocument();
});
});
});
});
});

View file

@ -0,0 +1,79 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { render, RenderResult } from "jest-matrix-react";
import { MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
import {
VoiceBroadcastInfoEventType,
VoiceBroadcastInfoState,
VoiceBroadcastRecording,
VoiceBroadcastRecordingBody,
} from "../../../../src/voice-broadcast";
import { mkEvent, stubClient } from "../../../test-utils";
// mock RoomAvatar, because it is doing too much fancy stuff
jest.mock("../../../../src/components/views/avatars/RoomAvatar", () => ({
__esModule: true,
default: jest.fn().mockImplementation(({ room }) => {
return <div data-testid="room-avatar">room avatar: {room.name}</div>;
}),
}));
describe("VoiceBroadcastRecordingBody", () => {
const userId = "@user:example.com";
const roomId = "!room:example.com";
let client: MatrixClient;
let infoEvent: MatrixEvent;
let recording: VoiceBroadcastRecording;
beforeAll(() => {
client = stubClient();
infoEvent = mkEvent({
event: true,
type: VoiceBroadcastInfoEventType,
content: {},
room: roomId,
user: userId,
});
recording = new VoiceBroadcastRecording(infoEvent, client, VoiceBroadcastInfoState.Resumed);
});
describe("when rendering a live broadcast", () => {
let renderResult: RenderResult;
beforeEach(() => {
renderResult = render(<VoiceBroadcastRecordingBody recording={recording} />);
});
it("should render with a red live badge", () => {
expect(renderResult.container).toMatchSnapshot();
});
});
describe("when rendering a paused broadcast", () => {
let renderResult: RenderResult;
beforeEach(async () => {
await recording.pause();
renderResult = render(<VoiceBroadcastRecordingBody recording={recording} />);
});
it("should render with a grey live badge", () => {
expect(renderResult.container).toMatchSnapshot();
});
});
it("when there is a broadcast without sender, it should raise an error", () => {
infoEvent.sender = null;
expect(() => {
render(<VoiceBroadcastRecordingBody recording={recording} />);
}).toThrow(`Voice Broadcast sender not found (event ${recording.infoEvent.getId()})`);
});
});

View file

@ -0,0 +1,218 @@
/*
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
Please see LICENSE files in the repository root for full details.
*/
//
import React from "react";
import { act, render, RenderResult, screen } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import { ClientEvent, MatrixClient, MatrixEvent, SyncState } from "matrix-js-sdk/src/matrix";
import { sleep } from "matrix-js-sdk/src/utils";
import { mocked } from "jest-mock";
import {
VoiceBroadcastInfoState,
VoiceBroadcastRecording,
VoiceBroadcastRecordingPip,
} from "../../../../src/voice-broadcast";
import { flushPromises, stubClient } from "../../../test-utils";
import { mkVoiceBroadcastInfoStateEvent } from "../../utils/test-utils";
import { requestMediaPermissions } from "../../../../src/utils/media/requestMediaPermissions";
import MediaDeviceHandler, { MediaDeviceKindEnum } from "../../../../src/MediaDeviceHandler";
import dis from "../../../../src/dispatcher/dispatcher";
import { Action } from "../../../../src/dispatcher/actions";
jest.mock("../../../../src/dispatcher/dispatcher");
jest.mock("../../../../src/utils/media/requestMediaPermissions");
// mock RoomAvatar, because it is doing too much fancy stuff
jest.mock("../../../../src/components/views/avatars/RoomAvatar", () => ({
__esModule: true,
default: jest.fn().mockImplementation(({ room }) => {
return <div data-testid="room-avatar">room avatar: {room.name}</div>;
}),
}));
// mock VoiceRecording because it contains all the audio APIs
jest.mock("../../../../src/audio/VoiceRecording", () => ({
VoiceRecording: jest.fn().mockReturnValue({
disableMaxLength: jest.fn(),
liveData: {
onUpdate: jest.fn(),
},
start: jest.fn(),
}),
}));
describe("VoiceBroadcastRecordingPip", () => {
const roomId = "!room:example.com";
let client: MatrixClient;
let infoEvent: MatrixEvent;
let recording: VoiceBroadcastRecording;
let renderResult: RenderResult;
const renderPip = async (state: VoiceBroadcastInfoState) => {
infoEvent = mkVoiceBroadcastInfoStateEvent(roomId, state, client.getUserId() || "", client.getDeviceId() || "");
recording = new VoiceBroadcastRecording(infoEvent, client, state);
jest.spyOn(recording, "pause");
jest.spyOn(recording, "resume");
renderResult = render(<VoiceBroadcastRecordingPip recording={recording} />);
await act(async () => {
flushPromises();
});
};
const itShouldShowTheBroadcastRoom = () => {
it("should show the broadcast room", () => {
expect(dis.dispatch).toHaveBeenCalledWith({
action: Action.ViewRoom,
room_id: roomId,
metricsTrigger: undefined,
});
});
};
beforeAll(() => {
client = stubClient();
mocked(requestMediaPermissions).mockResolvedValue({
getTracks: (): Array<MediaStreamTrack> => [],
} as unknown as MediaStream);
jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({
[MediaDeviceKindEnum.AudioInput]: [
{
deviceId: "d1",
label: "Device 1",
} as MediaDeviceInfo,
{
deviceId: "d2",
label: "Device 2",
} as MediaDeviceInfo,
],
[MediaDeviceKindEnum.AudioOutput]: [],
[MediaDeviceKindEnum.VideoInput]: [],
});
jest.spyOn(MediaDeviceHandler.instance, "setDevice").mockImplementation();
});
describe("when rendering a started recording", () => {
beforeEach(async () => {
await renderPip(VoiceBroadcastInfoState.Started);
});
it("should render as expected", () => {
expect(renderResult.container).toMatchSnapshot();
});
describe("and selecting another input device", () => {
beforeEach(async () => {
await userEvent.click(screen.getByLabelText("Change input device"));
await userEvent.click(screen.getByText("Device 1"));
});
it("should select the device and pause and resume the broadcast", () => {
expect(MediaDeviceHandler.instance.setDevice).toHaveBeenCalledWith(
"d1",
MediaDeviceKindEnum.AudioInput,
);
expect(recording.pause).toHaveBeenCalled();
expect(recording.resume).toHaveBeenCalled();
});
});
describe("and clicking the room name", () => {
beforeEach(async () => {
await userEvent.click(screen.getByText("My room"));
});
itShouldShowTheBroadcastRoom();
});
describe("and clicking the room avatar", () => {
beforeEach(async () => {
await userEvent.click(screen.getByText("room avatar: My room"));
});
itShouldShowTheBroadcastRoom();
});
describe("and clicking the pause button", () => {
beforeEach(async () => {
await userEvent.click(screen.getByLabelText("pause voice broadcast"));
});
it("should pause the recording", () => {
expect(recording.getState()).toBe(VoiceBroadcastInfoState.Paused);
});
});
describe("and clicking the stop button", () => {
beforeEach(async () => {
await userEvent.click(screen.getByLabelText("Stop Recording"));
// modal rendering has some weird sleeps
await sleep(100);
});
it("should display the confirm end dialog", () => {
screen.getByText("Stop live broadcasting?");
});
describe("and confirming the dialog", () => {
beforeEach(async () => {
await userEvent.click(screen.getByText("Yes, stop broadcast"));
});
it("should end the recording", () => {
expect(recording.getState()).toBe(VoiceBroadcastInfoState.Stopped);
});
});
});
describe("and there is no connection and clicking the pause button", () => {
beforeEach(async () => {
mocked(client.sendStateEvent).mockImplementation(() => {
throw new Error();
});
await userEvent.click(screen.getByLabelText("pause voice broadcast"));
});
it("should show a connection error info", () => {
expect(screen.getByText("Connection error - Recording paused")).toBeInTheDocument();
});
describe("and the connection is back", () => {
beforeEach(() => {
mocked(client.sendStateEvent).mockResolvedValue({ event_id: "e1" });
client.emit(ClientEvent.Sync, SyncState.Catchup, SyncState.Error);
});
it("should render a paused recording", async () => {
await expect(screen.findByLabelText("resume voice broadcast")).resolves.toBeInTheDocument();
});
});
});
});
describe("when rendering a paused recording", () => {
beforeEach(async () => {
await renderPip(VoiceBroadcastInfoState.Paused);
});
it("should render as expected", () => {
expect(renderResult.container).toMatchSnapshot();
});
describe("and clicking the resume button", () => {
beforeEach(async () => {
await userEvent.click(screen.getByLabelText("resume voice broadcast"));
});
it("should resume the recording", () => {
expect(recording.getState()).toBe(VoiceBroadcastInfoState.Resumed);
});
});
});
});

View file

@ -0,0 +1,129 @@
/*
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
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import { MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
import { render, RenderResult } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import { mocked } from "jest-mock";
import {
VoiceBroadcastInfoState,
VoiceBroadcastLiveness,
VoiceBroadcastPlayback,
VoiceBroadcastSmallPlaybackBody,
VoiceBroadcastPlaybackState,
} from "../../../../src/voice-broadcast";
import { stubClient } from "../../../test-utils";
import { mkVoiceBroadcastInfoStateEvent } from "../../utils/test-utils";
import { SdkContextClass } from "../../../../src/contexts/SDKContext";
// mock RoomAvatar, because it is doing too much fancy stuff
jest.mock("../../../../src/components/views/avatars/RoomAvatar", () => ({
__esModule: true,
default: jest.fn().mockImplementation(({ room }) => {
return <div data-testid="room-avatar">room avatar: {room.name}</div>;
}),
}));
describe("<VoiceBroadcastSmallPlaybackBody />", () => {
const userId = "@user:example.com";
const roomId = "!room:example.com";
let client: MatrixClient;
let infoEvent: MatrixEvent;
let playback: VoiceBroadcastPlayback;
let renderResult: RenderResult;
beforeAll(() => {
client = stubClient();
mocked(client.relations).mockClear();
mocked(client.relations).mockResolvedValue({ events: [] });
infoEvent = mkVoiceBroadcastInfoStateEvent(
roomId,
VoiceBroadcastInfoState.Stopped,
userId,
client.getDeviceId()!,
);
});
beforeEach(() => {
playback = new VoiceBroadcastPlayback(
infoEvent,
client,
SdkContextClass.instance.voiceBroadcastRecordingsStore,
);
jest.spyOn(playback, "toggle").mockImplementation(() => Promise.resolve());
jest.spyOn(playback, "getLiveness");
jest.spyOn(playback, "getState");
});
describe("when rendering a buffering broadcast", () => {
beforeEach(() => {
mocked(playback.getState).mockReturnValue(VoiceBroadcastPlaybackState.Buffering);
mocked(playback.getLiveness).mockReturnValue("live");
renderResult = render(<VoiceBroadcastSmallPlaybackBody playback={playback} />);
});
it("should render as expected", () => {
expect(renderResult.container).toMatchSnapshot();
});
});
describe("when rendering a playing broadcast", () => {
beforeEach(() => {
mocked(playback.getState).mockReturnValue(VoiceBroadcastPlaybackState.Playing);
mocked(playback.getLiveness).mockReturnValue("not-live");
renderResult = render(<VoiceBroadcastSmallPlaybackBody playback={playback} />);
});
it("should render as expected", () => {
expect(renderResult.container).toMatchSnapshot();
});
});
describe(`when rendering a stopped broadcast`, () => {
beforeEach(() => {
mocked(playback.getState).mockReturnValue(VoiceBroadcastPlaybackState.Stopped);
mocked(playback.getLiveness).mockReturnValue("not-live");
renderResult = render(<VoiceBroadcastSmallPlaybackBody playback={playback} />);
});
it("should render as expected", () => {
expect(renderResult.container).toMatchSnapshot();
});
describe("and clicking the play button", () => {
beforeEach(async () => {
await userEvent.click(renderResult.getByLabelText("play voice broadcast"));
});
it("should toggle the playback", () => {
expect(playback.toggle).toHaveBeenCalled();
});
});
});
describe.each([
{ state: VoiceBroadcastPlaybackState.Paused, liveness: "not-live" },
{ state: VoiceBroadcastPlaybackState.Playing, liveness: "live" },
] as Array<{ state: VoiceBroadcastPlaybackState; liveness: VoiceBroadcastLiveness }>)(
"when rendering a %s/%s broadcast",
({ state, liveness }) => {
beforeEach(() => {
mocked(playback.getState).mockReturnValue(state);
mocked(playback.getLiveness).mockReturnValue(liveness);
renderResult = render(<VoiceBroadcastSmallPlaybackBody playback={playback} />);
});
it("should render as expected", () => {
expect(renderResult.container).toMatchSnapshot();
});
},
);
});

View file

@ -0,0 +1,914 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`VoiceBroadcastPlaybackBody when rendering a buffering voice broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
@user:example.com
</span>
</div>
<div
class="mx_VoiceBroadcastHeader_line"
>
<div
class="mx_Spinner"
>
<div
aria-label="Loading…"
class="mx_Spinner_icon"
data-testid="spinner"
role="progressbar"
style="width: 14px; height: 14px;"
/>
</div>
Buffering…
</div>
</div>
<div
class="mx_LiveBadge"
>
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
</div>
<div
class="mx_VoiceBroadcastBody_controls"
>
<div
aria-label="30s backward"
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary_content"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_24"
/>
</div>
<div
aria-label="pause voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_12"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 4a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Zm8 0a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Z"
/>
</svg>
</div>
<div
aria-label="30s forward"
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary_content"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_24"
/>
</div>
</div>
<input
aria-label="Audio seek bar"
class="mx_SeekBar"
max="1"
min="0"
step="0.001"
style="--fillTo: 0;"
tabindex="0"
type="range"
value="0"
/>
<div
class="mx_VoiceBroadcastBody_timerow"
>
<time
class="mx_Clock"
datetime="PT0S"
>
00:00
</time>
<time
class="mx_Clock"
datetime="-PT23M42S"
>
-23:42
</time>
</div>
</div>
</div>
`;
exports[`VoiceBroadcastPlaybackBody when rendering a pause/not-live broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
@user:example.com
</span>
</div>
<div
class="mx_VoiceBroadcastHeader_line"
>
<div
class="mx_Icon mx_Icon_16"
/>
Voice broadcast
</div>
</div>
</div>
<div
class="mx_VoiceBroadcastBody_controls"
>
<div
aria-label="30s backward"
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary_content"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_24"
/>
</div>
<div
aria-label="resume voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl mx_VoiceBroadcastControl-play"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m8.98 4.677 9.921 5.58c1.36.764 1.36 2.722 0 3.486l-9.92 5.58C7.647 20.073 6 19.11 6 17.58V6.42c0-1.53 1.647-2.493 2.98-1.743Z"
/>
</svg>
</div>
<div
aria-label="30s forward"
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary_content"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_24"
/>
</div>
</div>
<input
aria-label="Audio seek bar"
class="mx_SeekBar"
max="1"
min="0"
step="0.001"
style="--fillTo: 0;"
tabindex="0"
type="range"
value="0"
/>
<div
class="mx_VoiceBroadcastBody_timerow"
>
<time
class="mx_Clock"
datetime="PT0S"
>
00:00
</time>
<time
class="mx_Clock"
datetime="-PT23M42S"
>
-23:42
</time>
</div>
</div>
</div>
`;
exports[`VoiceBroadcastPlaybackBody when rendering a playing broadcast in pip mode should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
@user:example.com
</span>
</div>
<div
class="mx_VoiceBroadcastHeader_line"
>
<div
class="mx_Icon mx_Icon_16"
/>
Voice broadcast
</div>
</div>
</div>
<div
class="mx_VoiceBroadcastBody_controls"
>
<div
aria-label="30s backward"
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary_content"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_24"
/>
</div>
<div
aria-label="pause voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_12"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 4a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Zm8 0a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Z"
/>
</svg>
</div>
<div
aria-label="30s forward"
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary_content"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_24"
/>
</div>
</div>
<input
aria-label="Audio seek bar"
class="mx_SeekBar"
max="1"
min="0"
step="0.001"
style="--fillTo: 0;"
tabindex="0"
type="range"
value="0"
/>
<div
class="mx_VoiceBroadcastBody_timerow"
>
<time
class="mx_Clock"
datetime="PT0S"
>
00:00
</time>
<time
class="mx_Clock"
datetime="-PT23M42S"
>
-23:42
</time>
</div>
</div>
</div>
`;
exports[`VoiceBroadcastPlaybackBody when rendering a playing broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
@user:example.com
</span>
</div>
<div
class="mx_VoiceBroadcastHeader_line"
>
<div
class="mx_Icon mx_Icon_16"
/>
Voice broadcast
</div>
</div>
</div>
<div
class="mx_VoiceBroadcastBody_controls"
>
<div
aria-label="30s backward"
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary_content"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_24"
/>
</div>
<div
aria-label="pause voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_12"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 4a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Zm8 0a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Z"
/>
</svg>
</div>
<div
aria-label="30s forward"
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary_content"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_24"
/>
</div>
</div>
<input
aria-label="Audio seek bar"
class="mx_SeekBar"
max="1"
min="0"
step="0.001"
style="--fillTo: 0;"
tabindex="0"
type="range"
value="0"
/>
<div
class="mx_VoiceBroadcastBody_timerow"
>
<time
class="mx_Clock"
datetime="PT0S"
>
00:00
</time>
<time
class="mx_Clock"
datetime="-PT23M42S"
>
-23:42
</time>
</div>
</div>
</div>
`;
exports[`VoiceBroadcastPlaybackBody when rendering a playing/live broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
@user:example.com
</span>
</div>
<div
class="mx_VoiceBroadcastHeader_line"
>
<div
class="mx_Icon mx_Icon_16"
/>
Voice broadcast
</div>
</div>
<div
class="mx_LiveBadge"
>
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
</div>
<div
class="mx_VoiceBroadcastBody_controls"
>
<div
aria-label="30s backward"
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary_content"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_24"
/>
</div>
<div
aria-label="pause voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_12"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 4a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Zm8 0a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Z"
/>
</svg>
</div>
<div
aria-label="30s forward"
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_secondary_content"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_24"
/>
</div>
</div>
<input
aria-label="Audio seek bar"
class="mx_SeekBar"
max="1"
min="0"
step="0.001"
style="--fillTo: 0;"
tabindex="0"
type="range"
value="0"
/>
<div
class="mx_VoiceBroadcastBody_timerow"
>
<time
class="mx_Clock"
datetime="PT0S"
>
00:00
</time>
<time
class="mx_Clock"
datetime="-PT23M42S"
>
-23:42
</time>
</div>
</div>
</div>
`;
exports[`VoiceBroadcastPlaybackBody when rendering a stopped broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
@user:example.com
</span>
</div>
<div
class="mx_VoiceBroadcastHeader_line"
>
<div
class="mx_Icon mx_Icon_16"
/>
Voice broadcast
</div>
</div>
</div>
<div
class="mx_VoiceBroadcastBody_controls"
>
<div
aria-label="play voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl mx_VoiceBroadcastControl-play"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m8.98 4.677 9.921 5.58c1.36.764 1.36 2.722 0 3.486l-9.92 5.58C7.647 20.073 6 19.11 6 17.58V6.42c0-1.53 1.647-2.493 2.98-1.743Z"
/>
</svg>
</div>
</div>
<input
aria-label="Audio seek bar"
class="mx_SeekBar"
max="1"
min="0"
step="0.001"
style="--fillTo: 0;"
tabindex="0"
type="range"
value="0"
/>
<div
class="mx_VoiceBroadcastBody_timerow"
>
<time
class="mx_Clock"
datetime="PT0S"
>
00:00
</time>
<time
class="mx_Clock"
datetime="-PT23M42S"
>
-23:42
</time>
</div>
</div>
</div>
`;
exports[`VoiceBroadcastPlaybackBody when rendering an error broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
@user:example.com
</span>
</div>
<div
class="mx_VoiceBroadcastHeader_line"
>
<div
class="mx_Icon mx_Icon_16"
/>
Voice broadcast
</div>
</div>
</div>
<div
class="mx_VoiceBroadcastRecordingConnectionError"
>
<svg
class="mx_Icon mx_Icon_16"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12.713 17.713A.968.968 0 0 1 12 18a.968.968 0 0 1-.713-.287A.967.967 0 0 1 11 17a.97.97 0 0 1 .287-.712A.968.968 0 0 1 12 16a.97.97 0 0 1 .713.288A.968.968 0 0 1 13 17a.97.97 0 0 1-.287.713Zm0-4A.968.968 0 0 1 12 14a.968.968 0 0 1-.713-.287A.967.967 0 0 1 11 13V9a.97.97 0 0 1 .287-.712A.968.968 0 0 1 12 8a.97.97 0 0 1 .713.288A.968.968 0 0 1 13 9v4a.97.97 0 0 1-.287.713Z"
/>
<path
clip-rule="evenodd"
d="M10.264 3.039c.767-1.344 2.705-1.344 3.472 0l8.554 14.969c.762 1.333-.2 2.992-1.736 2.992H3.446c-1.535 0-2.498-1.659-1.736-2.992l8.553-14.969ZM3.446 19 12 4.031l8.554 14.97H3.446Z"
fill-rule="evenodd"
/>
</svg>
Unable to play this voice broadcast
</div>
</div>
</div>
`;

View file

@ -0,0 +1,98 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`VoiceBroadcastPreRecordingPip when rendered should match the snapshot 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
data-testid="room-avatar"
>
room avatar:
!room@example.com
</div>
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
!room@example.com
</div>
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line mx_VoiceBroadcastHeader_mic--clickable"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
Default Device
</span>
</div>
</div>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414Z"
/>
</svg>
</div>
</div>
<div
class="mx_AccessibleButton mx_VoiceBroadcastBody_blockButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_16"
/>
Go live
</div>
</div>
</div>
`;

View file

@ -0,0 +1,131 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`VoiceBroadcastRecordingBody when rendering a live broadcast should render with a red live badge 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
@user:example.com
</span>
</div>
</div>
<div
class="mx_LiveBadge"
>
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
</div>
</div>
</div>
`;
exports[`VoiceBroadcastRecordingBody when rendering a paused broadcast should render with a grey live badge 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
@user:example.com
</span>
</div>
</div>
<div
class="mx_LiveBadge mx_LiveBadge--grey"
>
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
</div>
</div>
</div>
`;

View file

@ -0,0 +1,238 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`VoiceBroadcastRecordingPip when rendering a paused recording should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
</div>
</div>
<div
class="mx_VoiceBroadcastHeader_line"
>
<div
class="mx_Icon mx_Icon_16"
/>
<time
class="mx_Clock"
datetime="PT4H"
>
4h 0m 0s left
</time>
</div>
</div>
<div
class="mx_LiveBadge mx_LiveBadge--grey"
>
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
</div>
<hr
class="mx_VoiceBroadcastBody_divider"
/>
<div
class="mx_VoiceBroadcastBody_controls"
>
<div
aria-label="resume voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl mx_VoiceBroadcastControl-recording"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_12"
/>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16 mx_Icon_alert"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
</div>
<div
aria-label="Stop Recording"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_16"
/>
</div>
</div>
</div>
</div>
`;
exports[`VoiceBroadcastRecordingPip when rendering a started recording should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
</div>
</div>
<div
class="mx_VoiceBroadcastHeader_line"
>
<div
class="mx_Icon mx_Icon_16"
/>
<time
class="mx_Clock"
datetime="PT4H"
>
4h 0m 0s left
</time>
</div>
</div>
<div
class="mx_LiveBadge"
>
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
</div>
<hr
class="mx_VoiceBroadcastBody_divider"
/>
<div
class="mx_VoiceBroadcastBody_controls"
>
<div
aria-label="pause voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_12"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 4a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Zm8 0a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Z"
/>
</svg>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16 mx_Icon_alert"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
</div>
<div
aria-label="Stop Recording"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_16"
/>
</div>
</div>
</div>
</div>
`;

View file

@ -0,0 +1,558 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<VoiceBroadcastSmallPlaybackBody /> when rendering a { state: 'pause', liveness: 'not-live' }/%s broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip mx_VoiceBroadcastBody--small"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
@user:example.com
</span>
</div>
</div>
</div>
<div
aria-label="resume voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl mx_VoiceBroadcastControl-play"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m8.98 4.677 9.921 5.58c1.36.764 1.36 2.722 0 3.486l-9.92 5.58C7.647 20.073 6 19.11 6 17.58V6.42c0-1.53 1.647-2.493 2.98-1.743Z"
/>
</svg>
</div>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_8 mx_VoiceBroadcastBody__small-close"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414Z"
/>
</svg>
</div>
</div>
</div>
`;
exports[`<VoiceBroadcastSmallPlaybackBody /> when rendering a { state: 'playing', liveness: 'live' }/%s broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip mx_VoiceBroadcastBody--small"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
@user:example.com
</span>
</div>
<div
class="mx_LiveBadge"
>
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
</div>
</div>
<div
aria-label="pause voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_12"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 4a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Zm8 0a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Z"
/>
</svg>
</div>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_8 mx_VoiceBroadcastBody__small-close"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414Z"
/>
</svg>
</div>
</div>
</div>
`;
exports[`<VoiceBroadcastSmallPlaybackBody /> when rendering a buffering broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip mx_VoiceBroadcastBody--small"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
<div
class="mx_Spinner"
>
<div
aria-label="Loading…"
class="mx_Spinner_icon"
data-testid="spinner"
role="progressbar"
style="width: 12px; height: 12px;"
/>
</div>
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
@user:example.com
</span>
</div>
<div
class="mx_LiveBadge"
>
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
</div>
</div>
<div
aria-label="pause voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_12"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 4a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Zm8 0a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Z"
/>
</svg>
</div>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_8 mx_VoiceBroadcastBody__small-close"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414Z"
/>
</svg>
</div>
</div>
</div>
`;
exports[`<VoiceBroadcastSmallPlaybackBody /> when rendering a playing broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip mx_VoiceBroadcastBody--small"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
@user:example.com
</span>
</div>
</div>
</div>
<div
aria-label="pause voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_12"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 4a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Zm8 0a2 2 0 0 0-2 2v12a2 2 0 1 0 4 0V6a2 2 0 0 0-2-2Z"
/>
</svg>
</div>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_8 mx_VoiceBroadcastBody__small-close"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414Z"
/>
</svg>
</div>
</div>
</div>
`;
exports[`<VoiceBroadcastSmallPlaybackBody /> when rendering a stopped broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip mx_VoiceBroadcastBody--small"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<div
class="mx_VoiceBroadcastHeader_room_wrapper"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
</div>
</div>
<div
aria-label="Change input device"
class="mx_AccessibleButton mx_VoiceBroadcastHeader_line"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
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 0V6Z"
/>
<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.001 8.001 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8.001 8.001 0 0 1 4 12a1 1 0 0 1 1-1Z"
/>
</svg>
<span>
@user:example.com
</span>
</div>
</div>
</div>
<div
aria-label="play voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl mx_VoiceBroadcastControl-play"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_16"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m8.98 4.677 9.921 5.58c1.36.764 1.36 2.722 0 3.486l-9.92 5.58C7.647 20.073 6 19.11 6 17.58V6.42c0-1.53 1.647-2.493 2.98-1.743Z"
/>
</svg>
</div>
<div
class="mx_AccessibleButton"
role="button"
tabindex="0"
>
<svg
class="mx_Icon mx_Icon_8 mx_VoiceBroadcastBody__small-close"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414Z"
/>
</svg>
</div>
</div>
</div>
`;