diff --git a/test/components/views/settings/devices/__snapshots__/SecurityRecommendations-test.tsx.snap b/test/components/views/settings/devices/__snapshots__/SecurityRecommendations-test.tsx.snap
index b122d91826..a854601344 100644
--- a/test/components/views/settings/devices/__snapshots__/SecurityRecommendations-test.tsx.snap
+++ b/test/components/views/settings/devices/__snapshots__/SecurityRecommendations-test.tsx.snap
@@ -6,11 +6,15 @@ exports[` renders both cards when user has both unver
class="mx_SettingsSubsection"
data-testid="security-recommendations-section"
>
-
- Security recommendations
-
+
+ Security recommendations
+
+
diff --git a/test/components/views/settings/shared/SettingsSubsection-test.tsx b/test/components/views/settings/shared/SettingsSubsection-test.tsx
index acc2e6db95..cd833f90af 100644
--- a/test/components/views/settings/shared/SettingsSubsection-test.tsx
+++ b/test/components/views/settings/shared/SettingsSubsection-test.tsx
@@ -27,6 +27,17 @@ describe('
', () => {
const getComponent = (props = {}): React.ReactElement =>
(
);
+ it('renders with plain text heading', () => {
+ const { container } = render(getComponent());
+ expect(container).toMatchSnapshot();
+ });
+
+ it('renders with react element heading', () => {
+ const heading =
This is the heading
;
+ const { container } = render(getComponent({ heading }));
+ expect(container).toMatchSnapshot();
+ });
+
it('renders without description', () => {
const { container } = render(getComponent());
expect(container).toMatchSnapshot();
diff --git a/test/components/views/settings/shared/SettingsSubsectionHeading-test.tsx b/test/components/views/settings/shared/SettingsSubsectionHeading-test.tsx
new file mode 100644
index 0000000000..cb6959a671
--- /dev/null
+++ b/test/components/views/settings/shared/SettingsSubsectionHeading-test.tsx
@@ -0,0 +1,41 @@
+/*
+Copyright 2022 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import { render } from '@testing-library/react';
+import React from 'react';
+
+import {
+ SettingsSubsectionHeading,
+} from '../../../../../src/components/views/settings/shared/SettingsSubsectionHeading';
+
+describe('
', () => {
+ const defaultProps = {
+ heading: 'test',
+ };
+ const getComponent = (props = {}) =>
+ render(
);
+
+ it('renders without children', () => {
+ const { container } = getComponent();
+ expect({ container }).toMatchSnapshot();
+ });
+
+ it('renders with children', () => {
+ const children =
test;
+ const { container } = getComponent({ children });
+ expect({ container }).toMatchSnapshot();
+ });
+});
diff --git a/test/components/views/settings/shared/__snapshots__/SettingsSubsection-test.tsx.snap b/test/components/views/settings/shared/__snapshots__/SettingsSubsection-test.tsx.snap
index 10309d2f67..130b825d65 100644
--- a/test/components/views/settings/shared/__snapshots__/SettingsSubsection-test.tsx.snap
+++ b/test/components/views/settings/shared/__snapshots__/SettingsSubsection-test.tsx.snap
@@ -5,11 +5,15 @@ exports[`
renders with plain text description 1`] = `
-
- Test
-
+
+ Test
+
+
@@ -26,16 +30,45 @@ exports[` renders with plain text description 1`] = `
`;
+exports[`
renders with plain text heading 1`] = `
+
+
+
+
+ Test
+
+
+
+
+ test settings content
+
+
+
+
+`;
+
exports[`
renders with react element description 1`] = `
-
- Test
-
+
+ Test
+
+
@@ -59,15 +92,13 @@ exports[` renders with react element description 1`] = `
`;
-exports[`
renders without description 1`] = `
+exports[`
renders with react element heading 1`] = `
-
- Test
+
+ This is the heading
renders without description 1`] = `
`;
+
+exports[`
renders without description 1`] = `
+
+
+
+
+ Test
+
+
+
+
+ test settings content
+
+
+
+
+`;
diff --git a/test/components/views/settings/shared/__snapshots__/SettingsSubsectionHeading-test.tsx.snap b/test/components/views/settings/shared/__snapshots__/SettingsSubsectionHeading-test.tsx.snap
new file mode 100644
index 0000000000..f23700790b
--- /dev/null
+++ b/test/components/views/settings/shared/__snapshots__/SettingsSubsectionHeading-test.tsx.snap
@@ -0,0 +1,38 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`
renders with children 1`] = `
+Object {
+ "container":
,
+}
+`;
+
+exports[`
renders without children 1`] = `
+Object {
+ "container":
,
+}
+`;
diff --git a/test/components/views/settings/tabs/user/LabsUserSettingsTab-test.tsx b/test/components/views/settings/tabs/user/LabsUserSettingsTab-test.tsx
new file mode 100644
index 0000000000..614bb4062f
--- /dev/null
+++ b/test/components/views/settings/tabs/user/LabsUserSettingsTab-test.tsx
@@ -0,0 +1,73 @@
+/*
+Copyright 2022 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from 'react';
+import { render } from '@testing-library/react';
+
+import LabsUserSettingsTab from '../../../../../../src/components/views/settings/tabs/user/LabsUserSettingsTab';
+import SettingsStore from '../../../../../../src/settings/SettingsStore';
+import {
+ getMockClientWithEventEmitter,
+ mockClientMethodsServer,
+ mockClientMethodsUser,
+} from '../../../../../test-utils';
+import SdkConfig from '../../../../../../src/SdkConfig';
+
+describe('
', () => {
+ const sdkConfigSpy = jest.spyOn(SdkConfig, 'get');
+
+ const defaultProps = {
+ closeSettingsFn: jest.fn(),
+ };
+ const getComponent = () =>
;
+
+ const userId = '@alice:server.org';
+ getMockClientWithEventEmitter({
+ ...mockClientMethodsUser(userId),
+ ...mockClientMethodsServer(),
+ });
+
+ const settingsValueSpy = jest.spyOn(SettingsStore, 'getValue');
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ settingsValueSpy.mockReturnValue(false);
+ sdkConfigSpy.mockReturnValue(false);
+ });
+
+ it('renders settings marked as beta as beta cards', () => {
+ const { getByTestId } = render(getComponent());
+ expect(getByTestId("labs-beta-section")).toMatchSnapshot();
+ });
+
+ it('does not render non-beta labs settings when disabled in config', () => {
+ const { container } = render(getComponent());
+ expect(sdkConfigSpy).toHaveBeenCalledWith('show_labs_settings');
+
+ const labsSections = container.getElementsByClassName('mx_SettingsTab_section');
+ // only section is beta section
+ expect(labsSections.length).toEqual(1);
+ });
+
+ it('renders non-beta labs settings when enabled in config', () => {
+ // enable labs
+ sdkConfigSpy.mockImplementation(configName => configName === 'show_labs_settings');
+ const { container } = render(getComponent());
+
+ const labsSections = container.getElementsByClassName('mx_SettingsTab_section');
+ expect(labsSections.length).toEqual(11);
+ });
+});
diff --git a/test/components/views/settings/tabs/user/SecurityUserSettingsTab-test.tsx b/test/components/views/settings/tabs/user/SecurityUserSettingsTab-test.tsx
new file mode 100644
index 0000000000..bddb493463
--- /dev/null
+++ b/test/components/views/settings/tabs/user/SecurityUserSettingsTab-test.tsx
@@ -0,0 +1,68 @@
+/*
+Copyright 2022 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+import { render } from '@testing-library/react';
+import React from 'react';
+
+import SecurityUserSettingsTab from "../../../../../../src/components/views/settings/tabs/user/SecurityUserSettingsTab";
+import SettingsStore from '../../../../../../src/settings/SettingsStore';
+import {
+ getMockClientWithEventEmitter,
+ mockClientMethodsServer,
+ mockClientMethodsUser,
+ mockClientMethodsCrypto,
+ mockClientMethodsDevice,
+ mockPlatformPeg,
+} from '../../../../../test-utils';
+
+describe('
', () => {
+ const defaultProps = {
+ closeSettingsFn: jest.fn(),
+ };
+ const getComponent = () =>
;
+
+ const userId = '@alice:server.org';
+ const deviceId = 'alices-device';
+ getMockClientWithEventEmitter({
+ ...mockClientMethodsUser(userId),
+ ...mockClientMethodsServer(),
+ ...mockClientMethodsDevice(deviceId),
+ ...mockClientMethodsCrypto(),
+ getRooms: jest.fn().mockReturnValue([]),
+ getIgnoredUsers: jest.fn(),
+ });
+
+ const settingsValueSpy = jest.spyOn(SettingsStore, 'getValue');
+
+ beforeEach(() => {
+ mockPlatformPeg();
+ jest.clearAllMocks();
+ settingsValueSpy.mockReturnValue(false);
+ });
+
+ it('renders sessions section when new session manager is disabled', () => {
+ settingsValueSpy.mockReturnValue(false);
+ const { getByTestId } = render(getComponent());
+
+ expect(getByTestId('devices-section')).toBeTruthy();
+ });
+
+ it('does not render sessions section when new session manager is enabled', () => {
+ settingsValueSpy.mockReturnValue(true);
+ const { queryByTestId } = render(getComponent());
+
+ expect(queryByTestId('devices-section')).toBeFalsy();
+ });
+});
diff --git a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx
index 6d900071bc..ed10e64336 100644
--- a/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx
+++ b/test/components/views/settings/tabs/user/SessionManagerTab-test.tsx
@@ -285,47 +285,6 @@ describe('
', () => {
expect(queryByTestId('device-detail-metadata-application')).toBeFalsy();
});
- it('renders current session section with an unverified session', async () => {
- mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
- const { getByTestId } = render(getComponent());
-
- await act(async () => {
- await flushPromisesWithFakeTimers();
- });
-
- expect(getByTestId('current-session-section')).toMatchSnapshot();
- });
-
- it('opens encryption setup dialog when verifiying current session', async () => {
- mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
- const { getByTestId } = render(getComponent());
- const modalSpy = jest.spyOn(Modal, 'createDialog');
-
- await act(async () => {
- await flushPromisesWithFakeTimers();
- });
-
- // click verify button from current session section
- fireEvent.click(getByTestId(`verification-status-button-${alicesDevice.device_id}`));
-
- expect(modalSpy).toHaveBeenCalled();
- });
-
- it('renders current session section with a verified session', async () => {
- mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
- mockClient.getStoredDevice.mockImplementation(() => new DeviceInfo(alicesDevice.device_id));
- mockCrossSigningInfo.checkDeviceTrust
- .mockReturnValue(new DeviceTrustLevel(true, true, false, false));
-
- const { getByTestId } = render(getComponent());
-
- await act(async () => {
- await flushPromisesWithFakeTimers();
- });
-
- expect(getByTestId('current-session-section')).toMatchSnapshot();
- });
-
it('does not render other sessions section when user has only one device', async () => {
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice] });
const { queryByTestId } = render(getComponent());
@@ -367,6 +326,64 @@ describe('
', () => {
expect(container.querySelector('.mx_FilteredDeviceListHeader')).toMatchSnapshot();
});
+ describe('current session section', () => {
+ it('disables current session context menu while devices are loading', () => {
+ const { getByTestId } = render(getComponent());
+ expect(getByTestId('current-session-menu').getAttribute('aria-disabled')).toBeTruthy();
+ });
+
+ it('disables current session context menu when there is no current device', async () => {
+ mockClient.getDevices.mockResolvedValue({ devices: [] });
+ const { getByTestId } = render(getComponent());
+ await act(async () => {
+ await flushPromisesWithFakeTimers();
+ });
+
+ expect(getByTestId('current-session-menu').getAttribute('aria-disabled')).toBeTruthy();
+ });
+
+ it('renders current session section with an unverified session', async () => {
+ mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
+ const { getByTestId } = render(getComponent());
+
+ await act(async () => {
+ await flushPromisesWithFakeTimers();
+ });
+
+ expect(getByTestId('current-session-section')).toMatchSnapshot();
+ });
+
+ it('opens encryption setup dialog when verifiying current session', async () => {
+ mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
+ const { getByTestId } = render(getComponent());
+ const modalSpy = jest.spyOn(Modal, 'createDialog');
+
+ await act(async () => {
+ await flushPromisesWithFakeTimers();
+ });
+
+ // click verify button from current session section
+ fireEvent.click(getByTestId(`verification-status-button-${alicesDevice.device_id}`));
+
+ expect(modalSpy).toHaveBeenCalled();
+ });
+
+ it('renders current session section with a verified session', async () => {
+ mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
+ mockClient.getStoredDevice.mockImplementation(() => new DeviceInfo(alicesDevice.device_id));
+ mockCrossSigningInfo.checkDeviceTrust
+ .mockReturnValue(new DeviceTrustLevel(true, true, false, false));
+
+ const { getByTestId } = render(getComponent());
+
+ await act(async () => {
+ await flushPromisesWithFakeTimers();
+ });
+
+ expect(getByTestId('current-session-section')).toMatchSnapshot();
+ });
+ });
+
describe('device detail expansion', () => {
it('renders no devices expanded by default', async () => {
mockClient.getDevices.mockResolvedValue({
@@ -520,6 +537,53 @@ describe('
', () => {
expect(modalSpy).toHaveBeenCalledWith(LogoutDialog, {}, undefined, false, true);
});
+ it('Signs out of current device from kebab menu', async () => {
+ const modalSpy = jest.spyOn(Modal, 'createDialog');
+ mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice] });
+ const { getByTestId, getByLabelText } = render(getComponent());
+
+ await act(async () => {
+ await flushPromisesWithFakeTimers();
+ });
+
+ fireEvent.click(getByTestId('current-session-menu'));
+ fireEvent.click(getByLabelText('Sign out'));
+
+ // logout dialog opened
+ expect(modalSpy).toHaveBeenCalledWith(LogoutDialog, {}, undefined, false, true);
+ });
+
+ it('does not render sign out other devices option when only one device', async () => {
+ mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice] });
+ const { getByTestId, queryByLabelText } = render(getComponent());
+
+ await act(async () => {
+ await flushPromisesWithFakeTimers();
+ });
+
+ fireEvent.click(getByTestId('current-session-menu'));
+ expect(queryByLabelText('Sign out all other sessions')).toBeFalsy();
+ });
+
+ it('signs out of all other devices from current session context menu', async () => {
+ mockClient.getDevices.mockResolvedValue({ devices: [
+ alicesDevice, alicesMobileDevice, alicesOlderMobileDevice,
+ ] });
+ const { getByTestId, getByLabelText } = render(getComponent());
+
+ await act(async () => {
+ await flushPromisesWithFakeTimers();
+ });
+
+ fireEvent.click(getByTestId('current-session-menu'));
+ fireEvent.click(getByLabelText('Sign out all other sessions'));
+
+ // other devices deleted, excluding current device
+ expect(mockClient.deleteMultipleDevices).toHaveBeenCalledWith([
+ alicesMobileDevice.device_id, alicesOlderMobileDevice.device_id,
+ ], undefined);
+ });
+
describe('other devices', () => {
const interactiveAuthError = { httpStatus: 401, data: { flows: [{ stages: ["m.login.password"] }] } };
diff --git a/test/components/views/settings/tabs/user/__snapshots__/LabsUserSettingsTab-test.tsx.snap b/test/components/views/settings/tabs/user/__snapshots__/LabsUserSettingsTab-test.tsx.snap
new file mode 100644
index 0000000000..b2ff84a823
--- /dev/null
+++ b/test/components/views/settings/tabs/user/__snapshots__/LabsUserSettingsTab-test.tsx.snap
@@ -0,0 +1,196 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`
renders settings marked as beta as beta cards 1`] = `
+
+
+
+
+
+
+ Video rooms
+
+
+ Beta
+
+
+
+
+ A new way to chat over voice and video in .
+
+
+ Video rooms are always-on VoIP channels embedded within a room in .
+
+
+
+
+ Joining the beta will reload .
+
+
+
+
+

+
+
+
+
+
+
+
+
+ Threads
+
+
+ Beta
+
+
+
+
+ Keep discussions organised with threads.
+
+
+
+ Threads help keep conversations on-topic and easy to track.
+
+ Learn more
+
+ .
+
+
+
+
+
+ Joining the beta will reload .
+
+
+
+
+

+
+
+
+
+
+
+
+
+ New session manager
+
+
+ Beta
+
+
+
+
+ Have greater visibility and control over all your sessions.
+
+
+ Our new sessions manager provides better visibility of all your sessions, and greater control over them including the ability to remotely toggle push notifications.
+
+
+
+
+
+
![]()
+
+
+
+
+`;
diff --git a/test/components/views/settings/tabs/user/__snapshots__/SessionManagerTab-test.tsx.snap b/test/components/views/settings/tabs/user/__snapshots__/SessionManagerTab-test.tsx.snap
index 3cee723f28..e4a16f35fd 100644
--- a/test/components/views/settings/tabs/user/__snapshots__/SessionManagerTab-test.tsx.snap
+++ b/test/components/views/settings/tabs/user/__snapshots__/SessionManagerTab-test.tsx.snap
@@ -15,77 +15,32 @@ exports[`
Sign out Signs out of current device 1`] = `
`;
-exports[`
goes to filtered list from security recommendations 1`] = `
-
-`;
-
-exports[`
renders current session section with a verified session 1`] = `
+exports[`
current session section renders current session section with a verified session 1`] = `
-
- Current session
-
+
+ Current session
+
+
+
+
+
@@ -182,16 +137,32 @@ exports[` renders current session section with a verified s
`;
-exports[`
renders current session section with an unverified session 1`] = `
+exports[`
current session section renders current session section with an unverified session 1`] = `
-
- Current session
-
+
+ Current session
+
+
+
+
+
@@ -300,6 +271,67 @@ exports[` renders current session section with an unverifie
`;
+exports[`
goes to filtered list from security recommendations 1`] = `
+
+`;
+
exports[`
sets device verification status correctly 1`] = `
{
expect(callPower).toBe(100);
expect(callMemberPower).toBe(100);
});
+
+ it("should upload avatar if one is passed", async () => {
+ client.uploadContent.mockResolvedValue({ content_uri: "mxc://foobar" });
+ const avatar = new File([], "avatar.png");
+ await createRoom({ avatar });
+ expect(client.createRoom).toHaveBeenCalledWith(expect.objectContaining({
+ initial_state: expect.arrayContaining([{
+ content: {
+ url: "mxc://foobar",
+ },
+ type: "m.room.avatar",
+ }]),
+ }));
+ });
});
describe("canEncryptToAllUsers", () => {
diff --git a/test/events/EventTileFactory-test.ts b/test/events/EventTileFactory-test.ts
index ebda574dfc..9625ffe448 100644
--- a/test/events/EventTileFactory-test.ts
+++ b/test/events/EventTileFactory-test.ts
@@ -1,12 +1,9 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
-
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
http://www.apache.org/licenses/LICENSE-2.0
-
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -14,23 +11,70 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { EventType, MatrixEvent } from "matrix-js-sdk/src/matrix";
+import { EventType, MatrixClient, MatrixEvent, MsgType } from "matrix-js-sdk/src/matrix";
import { JSONEventFactory, pickFactory } from "../../src/events/EventTileFactory";
-import { createTestClient } from "../test-utils";
+import { VoiceBroadcastChunkEventType } from "../../src/voice-broadcast";
+import { createTestClient, mkEvent } from "../test-utils";
const roomId = "!room:example.com";
describe("pickFactory", () => {
+ let voiceBroadcastChunkEvent: MatrixEvent;
+ let audioMessageEvent: MatrixEvent;
+ let client: MatrixClient;
+
+ beforeAll(() => {
+ client = createTestClient();
+ voiceBroadcastChunkEvent = mkEvent({
+ event: true,
+ type: EventType.RoomMessage,
+ user: client.getUserId(),
+ room: roomId,
+ content: {
+ msgtype: MsgType.Audio,
+ [VoiceBroadcastChunkEventType]: {},
+ },
+ });
+ audioMessageEvent = mkEvent({
+ event: true,
+ type: EventType.RoomMessage,
+ user: client.getUserId(),
+ room: roomId,
+ content: {
+ msgtype: MsgType.Audio,
+ },
+ });
+ });
+
it("should return JSONEventFactory for a no-op m.room.power_levels event", () => {
- const cli = createTestClient();
const event = new MatrixEvent({
type: EventType.RoomPowerLevels,
state_key: "",
content: {},
- sender: cli.getUserId(),
+ sender: client.getUserId(),
room_id: roomId,
});
- expect(pickFactory(event, cli, true)).toBe(JSONEventFactory);
+ expect(pickFactory(event, client, true)).toBe(JSONEventFactory);
+ });
+
+ describe("when showing hidden events", () => {
+ it("should return a function for a voice broadcast event", () => {
+ expect(pickFactory(voiceBroadcastChunkEvent, client, true)).toBeInstanceOf(Function);
+ });
+
+ it("should return a function for an audio message event", () => {
+ expect(pickFactory(audioMessageEvent, client, true)).toBeInstanceOf(Function);
+ });
+ });
+
+ describe("when not showing hidden events", () => {
+ it("should return undefined for a voice broadcast event", () => {
+ expect(pickFactory(voiceBroadcastChunkEvent, client, false)).toBeUndefined();
+ });
+
+ it("should return a function for an audio message event", () => {
+ expect(pickFactory(audioMessageEvent, client, false)).toBeInstanceOf(Function);
+ });
});
});
diff --git a/test/i18n-test/languageHandler-test.tsx b/test/i18n-test/languageHandler-test.tsx
index 9c15bfd3fe..d492011032 100644
--- a/test/i18n-test/languageHandler-test.tsx
+++ b/test/i18n-test/languageHandler-test.tsx
@@ -27,10 +27,7 @@ import {
import { stubClient } from '../test-utils';
describe('languageHandler', function() {
- /*
- See /__mocks__/browser-request.js/ for how we are stubbing out translations
- to provide fixture data for these tests
- */
+ // See setupLanguage.ts for how we are stubbing out translations to provide fixture data for these tests
const basicString = 'Rooms';
const selfClosingTagSub = 'Accept
to continue:';
const textInTagSub = '
Upgrade to your own domain';
diff --git a/test/setup/setupLanguage.ts b/test/setup/setupLanguage.ts
index 5c6834d012..5efd8786cd 100644
--- a/test/setup/setupLanguage.ts
+++ b/test/setup/setupLanguage.ts
@@ -14,7 +14,70 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
+import fetchMock from "fetch-mock-jest";
+
import * as languageHandler from "../../src/languageHandler";
+import en from "../../src/i18n/strings/en_EN.json";
+import de from "../../src/i18n/strings/de_DE.json";
+
+fetchMock.config.overwriteRoutes = false;
+fetchMock.catch("");
+window.fetch = fetchMock.sandbox();
+
+const lv = {
+ "Save": "Saglabāt",
+ "Uploading %(filename)s and %(count)s others|one": "Качване на %(filename)s и %(count)s друг",
+};
+
+// Fake languages.json containing references to en_EN, de_DE and lv
+// en_EN.json
+// de_DE.json
+// lv.json - mock version with few translations, used to test fallback translation
+
+function weblateToCounterpart(inTrs: object): object {
+ const outTrs = {};
+
+ for (const key of Object.keys(inTrs)) {
+ const keyParts = key.split('|', 2);
+ if (keyParts.length === 2) {
+ let obj = outTrs[keyParts[0]];
+ if (obj === undefined) {
+ obj = outTrs[keyParts[0]] = {};
+ } else if (typeof obj === "string") {
+ // This is a transitional edge case if a string went from singular to pluralised and both still remain
+ // in the translation json file. Use the singular translation as `other` and merge pluralisation atop.
+ obj = outTrs[keyParts[0]] = {
+ "other": inTrs[key],
+ };
+ console.warn("Found entry in i18n file in both singular and pluralised form", keyParts[0]);
+ }
+ obj[keyParts[1]] = inTrs[key];
+ } else {
+ outTrs[key] = inTrs[key];
+ }
+ }
+
+ return outTrs;
+}
+
+fetchMock
+ .get("/i18n/languages.json", {
+ "en": {
+ "fileName": "en_EN.json",
+ "label": "English",
+ },
+ "de": {
+ "fileName": "de_DE.json",
+ "label": "German",
+ },
+ "lv": {
+ "fileName": "lv.json",
+ "label": "Latvian",
+ },
+ })
+ .get("end:en_EN.json", weblateToCounterpart(en))
+ .get("end:de_DE.json", weblateToCounterpart(de))
+ .get("end:lv.json", weblateToCounterpart(lv));
languageHandler.setLanguage('en');
languageHandler.setMissingEntryGenerator(key => key.split("|", 2)[1]);
diff --git a/test/setup/setupManualMocks.ts b/test/setup/setupManualMocks.ts
index 162529ef64..31c716e375 100644
--- a/test/setup/setupManualMocks.ts
+++ b/test/setup/setupManualMocks.ts
@@ -45,6 +45,7 @@ global.matchMedia = mockMatchMedia;
// maplibre requires a createObjectURL mock
global.URL.createObjectURL = jest.fn();
+global.URL.revokeObjectURL = jest.fn();
// polyfilling TextEncoder as it is not available on JSDOM
// view https://github.com/facebook/jest/issues/9983
diff --git a/test/setupTests.js b/test/setupTests.js
index a84c7bef78..48625ae52f 100644
--- a/test/setupTests.js
+++ b/test/setupTests.js
@@ -20,8 +20,7 @@ import Adapter from "@wojtekmaj/enzyme-adapter-react-17";
import { configure } from "enzyme";
import "blob-polyfill"; // https://github.com/jsdom/jsdom/issues/2555
-// Enable the jest & enzyme mocks
-require('jest-fetch-mock').enableMocks();
+// Enable the enzyme mocks
configure({ adapter: new Adapter() });
// Very carefully enable the mocks for everything else in
diff --git a/test/stores/widgets/StopGapWidgetDriver-test.ts b/test/stores/widgets/StopGapWidgetDriver-test.ts
index 4e8764a854..d1816bdac3 100644
--- a/test/stores/widgets/StopGapWidgetDriver-test.ts
+++ b/test/stores/widgets/StopGapWidgetDriver-test.ts
@@ -266,7 +266,7 @@ describe("StopGapWidgetDriver", () => {
limit: 25,
from: 'from-token',
to: 'to-token',
- direction: Direction.Forward,
+ dir: Direction.Forward,
},
);
});
diff --git a/test/test-utils/client.ts b/test/test-utils/client.ts
index 4a5b318491..d6ec3f2fd3 100644
--- a/test/test-utils/client.ts
+++ b/test/test-utils/client.ts
@@ -15,7 +15,7 @@ limitations under the License.
*/
import EventEmitter from "events";
-import { MethodKeysOf, mocked, MockedObject } from "jest-mock";
+import { MethodKeysOf, mocked, MockedObject, PropertyKeysOf } from "jest-mock";
import { MatrixClient, User } from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "../../src/MatrixClientPeg";
@@ -71,6 +71,7 @@ export const mockClientMethodsUser = (userId = '@alice:domain') => ({
credentials: { userId },
getThreePids: jest.fn().mockResolvedValue({ threepids: [] }),
getAccessToken: jest.fn(),
+ getDeviceId: jest.fn(),
});
/**
@@ -94,6 +95,35 @@ export const mockClientMethodsServer = (): Partial
, unknown>> => ({
+ getDeviceId: jest.fn().mockReturnValue(deviceId),
+ getDeviceEd25519Key: jest.fn(),
+ getDevices: jest.fn().mockResolvedValue({ devices: [] }),
+});
+
+export const mockClientMethodsCrypto = (): Partial & PropertyKeysOf, unknown>
+> => ({
+ isCryptoEnabled: jest.fn(),
+ isSecretStorageReady: jest.fn(),
+ isCrossSigningReady: jest.fn(),
+ isKeyBackupKeyStored: jest.fn(),
+ getCrossSigningCacheCallbacks: jest.fn().mockReturnValue({ getCrossSigningKeyCache: jest.fn() }),
+ getStoredCrossSigningForUser: jest.fn(),
+ checkKeyBackup: jest.fn().mockReturnValue({}),
+ crypto: {
+ getSessionBackupPrivateKey: jest.fn(),
+ secretStorage: { hasKey: jest.fn() },
+ crossSigningInfo: {
+ getId: jest.fn(),
+ isStoredInSecretStorage: jest.fn(),
+ },
+ },
+});
+
diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts
index 2e01f3a4d8..60091be2a7 100644
--- a/test/test-utils/test-utils.ts
+++ b/test/test-utils/test-utils.ts
@@ -158,7 +158,7 @@ export function createTestClient(): MatrixClient {
getOpenIdToken: jest.fn().mockResolvedValue(undefined),
registerWithIdentityServer: jest.fn().mockResolvedValue({}),
getIdentityAccount: jest.fn().mockResolvedValue({}),
- getTerms: jest.fn().mockResolvedValueOnce(undefined),
+ getTerms: jest.fn().mockResolvedValue({ policies: [] }),
doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(undefined),
isVersionSupported: jest.fn().mockResolvedValue(undefined),
getPushRules: jest.fn().mockResolvedValue(undefined),
@@ -182,6 +182,7 @@ export function createTestClient(): MatrixClient {
setVideoInput: jest.fn(),
setAudioInput: jest.fn(),
} as unknown as MediaHandler),
+ uploadContent: jest.fn(),
} as unknown as MatrixClient;
client.reEmitter = new ReEmitter(client);
diff --git a/test/utils/MultiInviter-test.ts b/test/utils/MultiInviter-test.ts
index 5663efe42f..0e87e8d6d6 100644
--- a/test/utils/MultiInviter-test.ts
+++ b/test/utils/MultiInviter-test.ts
@@ -98,9 +98,9 @@ describe('MultiInviter', () => {
const result = await inviter.invite([MXID1, MXID2, MXID3]);
expect(client.invite).toHaveBeenCalledTimes(3);
- expect(client.invite).toHaveBeenNthCalledWith(1, ROOMID, MXID1, undefined, undefined);
- expect(client.invite).toHaveBeenNthCalledWith(2, ROOMID, MXID2, undefined, undefined);
- expect(client.invite).toHaveBeenNthCalledWith(3, ROOMID, MXID3, undefined, undefined);
+ expect(client.invite).toHaveBeenNthCalledWith(1, ROOMID, MXID1, undefined);
+ expect(client.invite).toHaveBeenNthCalledWith(2, ROOMID, MXID2, undefined);
+ expect(client.invite).toHaveBeenNthCalledWith(3, ROOMID, MXID3, undefined);
expectAllInvitedResult(result);
});
@@ -116,9 +116,9 @@ describe('MultiInviter', () => {
const result = await inviter.invite([MXID1, MXID2, MXID3]);
expect(client.invite).toHaveBeenCalledTimes(3);
- expect(client.invite).toHaveBeenNthCalledWith(1, ROOMID, MXID1, undefined, undefined);
- expect(client.invite).toHaveBeenNthCalledWith(2, ROOMID, MXID2, undefined, undefined);
- expect(client.invite).toHaveBeenNthCalledWith(3, ROOMID, MXID3, undefined, undefined);
+ expect(client.invite).toHaveBeenNthCalledWith(1, ROOMID, MXID1, undefined);
+ expect(client.invite).toHaveBeenNthCalledWith(2, ROOMID, MXID2, undefined);
+ expect(client.invite).toHaveBeenNthCalledWith(3, ROOMID, MXID3, undefined);
expectAllInvitedResult(result);
});
@@ -131,7 +131,7 @@ describe('MultiInviter', () => {
const result = await inviter.invite([MXID1, MXID2, MXID3]);
expect(client.invite).toHaveBeenCalledTimes(1);
- expect(client.invite).toHaveBeenNthCalledWith(1, ROOMID, MXID1, undefined, undefined);
+ expect(client.invite).toHaveBeenNthCalledWith(1, ROOMID, MXID1, undefined);
// The resolved state is 'invited' for all users.
// With the above client expectations, the test ensures that only the first user is invited.
diff --git a/test/utils/notifications-test.ts b/test/utils/notifications-test.ts
index ba134c1480..c44b496608 100644
--- a/test/utils/notifications-test.ts
+++ b/test/utils/notifications-test.ts
@@ -81,8 +81,8 @@ describe('notifications', () => {
});
describe('localNotificationsAreSilenced', () => {
- it('defaults to true when no setting exists', () => {
- expect(localNotificationsAreSilenced(mockClient)).toBeTruthy();
+ it('defaults to false when no setting exists', () => {
+ expect(localNotificationsAreSilenced(mockClient)).toBeFalsy();
});
it('checks the persisted value', () => {
mockClient.setAccountData(accountDataEventKey, { is_silenced: true });
diff --git a/test/voice-broadcast/audio/VoiceBroadcastRecorder-test.ts b/test/voice-broadcast/audio/VoiceBroadcastRecorder-test.ts
new file mode 100644
index 0000000000..e60d7e2d96
--- /dev/null
+++ b/test/voice-broadcast/audio/VoiceBroadcastRecorder-test.ts
@@ -0,0 +1,209 @@
+/*
+Copyright 2022 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import { mocked } from "jest-mock";
+
+import { VoiceRecording } from "../../../src/audio/VoiceRecording";
+import SdkConfig from "../../../src/SdkConfig";
+import { concat } from "../../../src/utils/arrays";
+import {
+ ChunkRecordedPayload,
+ createVoiceBroadcastRecorder,
+ VoiceBroadcastRecorder,
+ VoiceBroadcastRecorderEvent,
+} from "../../../src/voice-broadcast";
+
+describe("VoiceBroadcastRecorder", () => {
+ describe("createVoiceBroadcastRecorder", () => {
+ beforeEach(() => {
+ jest.spyOn(SdkConfig, "get").mockImplementation((key: string) => {
+ if (key === "voice_broadcast") {
+ return {
+ chunk_length: 1337,
+ };
+ }
+ });
+ });
+
+ afterEach(() => {
+ mocked(SdkConfig.get).mockRestore();
+ });
+
+ it("should return a VoiceBroadcastRecorder instance with targetChunkLength from config", () => {
+ const voiceBroadcastRecorder = createVoiceBroadcastRecorder();
+ expect(voiceBroadcastRecorder).toBeInstanceOf(VoiceBroadcastRecorder);
+ expect(voiceBroadcastRecorder.targetChunkLength).toBe(1337);
+ });
+ });
+
+ describe("instance", () => {
+ const chunkLength = 30;
+ const headers1 = new Uint8Array([1, 2]);
+ const headers2 = new Uint8Array([3, 4]);
+ const chunk1 = new Uint8Array([5, 6]);
+ const chunk2a = new Uint8Array([7, 8]);
+ const chunk2b = new Uint8Array([9, 10]);
+ const contentType = "test content type";
+
+ let voiceRecording: VoiceRecording;
+ let voiceBroadcastRecorder: VoiceBroadcastRecorder;
+ let onChunkRecorded: (chunk: ChunkRecordedPayload) => void;
+
+ const itShouldNotEmitAChunkRecordedEvent = () => {
+ it("should not emit a ChunkRecorded event", () => {
+ expect(voiceRecording.emit).not.toHaveBeenCalledWith(
+ VoiceBroadcastRecorderEvent.ChunkRecorded,
+ expect.anything(),
+ );
+ });
+ };
+
+ beforeEach(() => {
+ voiceRecording = {
+ contentType,
+ start: jest.fn().mockResolvedValue(undefined),
+ stop: jest.fn().mockResolvedValue(undefined),
+ on: jest.fn(),
+ off: jest.fn(),
+ emit: jest.fn(),
+ destroy: jest.fn(),
+ recorderSeconds: 23,
+ } as unknown as VoiceRecording;
+ voiceBroadcastRecorder = new VoiceBroadcastRecorder(voiceRecording, chunkLength);
+ jest.spyOn(voiceBroadcastRecorder, "removeAllListeners");
+ onChunkRecorded = jest.fn();
+ voiceBroadcastRecorder.on(VoiceBroadcastRecorderEvent.ChunkRecorded, onChunkRecorded);
+ });
+
+ afterEach(() => {
+ voiceBroadcastRecorder.destroy();
+ });
+
+ it("start should forward the call to VoiceRecording.start", async () => {
+ await voiceBroadcastRecorder.start();
+ expect(voiceRecording.start).toHaveBeenCalled();
+ });
+
+ describe("stop", () => {
+ beforeEach(async () => {
+ await voiceBroadcastRecorder.stop();
+ });
+
+ it("should forward the call to VoiceRecording.stop", async () => {
+ expect(voiceRecording.stop).toHaveBeenCalled();
+ });
+
+ itShouldNotEmitAChunkRecordedEvent();
+ });
+
+ describe("when calling destroy", () => {
+ beforeEach(() => {
+ voiceBroadcastRecorder.destroy();
+ });
+
+ it("should call VoiceRecording.destroy", () => {
+ expect(voiceRecording.destroy).toHaveBeenCalled();
+ });
+
+ it("should remove all listeners", () => {
+ expect(voiceBroadcastRecorder.removeAllListeners).toHaveBeenCalled();
+ });
+ });
+
+ it("contentType should return the value from VoiceRecording", () => {
+ expect(voiceBroadcastRecorder.contentType).toBe(contentType);
+ });
+
+ describe("when the first page from recorder has been received", () => {
+ beforeEach(() => {
+ voiceRecording.onDataAvailable(headers1);
+ });
+
+ itShouldNotEmitAChunkRecordedEvent();
+ });
+
+ describe("when a second page from recorder has been received", () => {
+ beforeEach(() => {
+ voiceRecording.onDataAvailable(headers1);
+ voiceRecording.onDataAvailable(headers2);
+ });
+
+ itShouldNotEmitAChunkRecordedEvent();
+ });
+
+ describe("when a third page from recorder has been received", () => {
+ beforeEach(() => {
+ voiceRecording.onDataAvailable(headers1);
+ voiceRecording.onDataAvailable(headers2);
+ voiceRecording.onDataAvailable(chunk1);
+ });
+
+ itShouldNotEmitAChunkRecordedEvent();
+
+ describe("stop", () => {
+ let stopPayload: ChunkRecordedPayload;
+
+ beforeEach(async () => {
+ stopPayload = await voiceBroadcastRecorder.stop();
+ });
+
+ it("should return the remaining chunk", () => {
+ expect(stopPayload).toEqual({
+ buffer: concat(headers1, headers2, chunk1),
+ length: 23,
+ });
+ });
+ });
+ });
+
+ describe("when some chunks have been received", () => {
+ beforeEach(() => {
+ // simulate first chunk
+ voiceRecording.onDataAvailable(headers1);
+ voiceRecording.onDataAvailable(headers2);
+ // set recorder seconds to something greater than the test chunk length of 30
+ // @ts-ignore
+ voiceRecording.recorderSeconds = 42;
+ voiceRecording.onDataAvailable(chunk1);
+
+ // simulate a second chunk
+ voiceRecording.onDataAvailable(chunk2a);
+ // add another 30 seconds for the next chunk
+ // @ts-ignore
+ voiceRecording.recorderSeconds = 72;
+ voiceRecording.onDataAvailable(chunk2b);
+ });
+
+ it("should emit ChunkRecorded events", () => {
+ expect(onChunkRecorded).toHaveBeenNthCalledWith(
+ 1,
+ {
+ buffer: concat(headers1, headers2, chunk1),
+ length: 42,
+ },
+ );
+
+ expect(onChunkRecorded).toHaveBeenNthCalledWith(
+ 2,
+ {
+ buffer: concat(headers1, headers2, chunk2a, chunk2b),
+ length: 72 - 42, // 72 (position at second chunk) - 42 (position of first chunk)
+ },
+ );
+ });
+ });
+ });
+});
diff --git a/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx b/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx
index fed396a683..d6f9da6a68 100644
--- a/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx
+++ b/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx
@@ -72,9 +72,8 @@ describe("VoiceBroadcastBody", () => {
{
onClick: expect.any(Function),
live: true,
- member: infoEvent.sender,
- userId: client.getUserId(),
- title: "@userId:matrix.org • test room",
+ sender: infoEvent.sender,
+ roomName: room.name,
},
{},
);
@@ -89,9 +88,8 @@ describe("VoiceBroadcastBody", () => {
{
onClick: expect.any(Function),
live: false,
- member: infoEvent.sender,
- userId: client.getUserId(),
- title: "@userId:matrix.org • test room",
+ sender: infoEvent.sender,
+ roomName: room.name,
},
{},
);
@@ -105,17 +103,17 @@ describe("VoiceBroadcastBody", () => {
mocked(VoiceBroadcastRecordingBody).mockImplementation(
({
live,
- member: _member,
+ sender,
onClick,
- title,
- userId: _userId,
+ roomName,
}) => {
return (
-
{ title }
+
{ sender.name }
+
{ roomName }
{ live && "Live" }
);
@@ -155,7 +153,7 @@ describe("VoiceBroadcastBody", () => {
itShouldRenderANonLiveVoiceBroadcast();
it("should call stop on the recording", () => {
- expect(recording.state).toBe(VoiceBroadcastInfoState.Stopped);
+ expect(recording.getState()).toBe(VoiceBroadcastInfoState.Stopped);
expect(onRecordingStateChanged).toHaveBeenCalledWith(VoiceBroadcastInfoState.Stopped);
});
});
diff --git a/test/voice-broadcast/components/atoms/VoiceBroadcastHeader-test.tsx b/test/voice-broadcast/components/atoms/VoiceBroadcastHeader-test.tsx
new file mode 100644
index 0000000000..6b07b213f7
--- /dev/null
+++ b/test/voice-broadcast/components/atoms/VoiceBroadcastHeader-test.tsx
@@ -0,0 +1,60 @@
+/*
+Copyright 2022 The Matrix.org Foundation C.I.C.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from "react";
+import { Container } from "react-dom";
+import { RoomMember } from "matrix-js-sdk/src/matrix";
+import { render, RenderResult } from "@testing-library/react";
+
+import { VoiceBroadcastHeader } from "../../../../src/voice-broadcast";
+
+describe("VoiceBroadcastHeader", () => {
+ const userId = "@user:example.com";
+ const roomId = "!room:example.com";
+ const roomName = "test room";
+ const sender = new RoomMember(roomId, userId);
+ let container: Container;
+
+ const renderHeader = (live: boolean, showBroadcast: boolean = undefined): RenderResult => {
+ return render();
+ };
+
+ beforeAll(() => {
+ sender.name = "test user";
+ });
+
+ describe("when rendering a live broadcast header with broadcast info", () => {
+ beforeEach(() => {
+ container = renderHeader(true, true).container;
+ });
+
+ it("should render the header with a live badge", () => {
+ expect(container).toMatchSnapshot();
+ });
+ });
+
+ describe("when rendering a non-live broadcast header", () => {
+ beforeEach(() => {
+ container = renderHeader(false).container;
+ });
+
+ it("should render the header without a live badge", () => {
+ expect(container).toMatchSnapshot();
+ });
+ });
+});
diff --git a/test/voice-broadcast/components/atoms/__snapshots__/VoiceBroadcastHeader-test.tsx.snap b/test/voice-broadcast/components/atoms/__snapshots__/VoiceBroadcastHeader-test.tsx.snap
new file mode 100644
index 0000000000..3e3bd2b1d2
--- /dev/null
+++ b/test/voice-broadcast/components/atoms/__snapshots__/VoiceBroadcastHeader-test.tsx.snap
@@ -0,0 +1,109 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`VoiceBroadcastHeader when rendering a live broadcast header with broadcast info should render the header with a live badge 1`] = `
+
+
+
+`;
+
+exports[`VoiceBroadcastHeader when rendering a non-live broadcast header should render the header without a live badge 1`] = `
+
+
+
+`;
diff --git a/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody-test.tsx b/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody-test.tsx
index 3f797025fb..d9cb83642b 100644
--- a/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody-test.tsx
+++ b/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody-test.tsx
@@ -18,23 +18,27 @@ import React, { MouseEventHandler } from "react";
import { render, RenderResult } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { RoomMember } from "matrix-js-sdk/src/matrix";
-import { mocked } from "jest-mock";
-import { VoiceBroadcastRecordingBody } from "../../../../src/voice-broadcast";
-import MemberAvatar from "../../../../src/components/views/avatars/MemberAvatar";
+import { VoiceBroadcastHeader, VoiceBroadcastRecordingBody } from "../../../../src/voice-broadcast";
-jest.mock("../../../../src/components/views/avatars/MemberAvatar", () => jest.fn());
+jest.mock("../../../../src/voice-broadcast/components/atoms/VoiceBroadcastHeader", () => ({
+ VoiceBroadcastHeader: ({ live, sender, roomName }: React.ComponentProps) => {
+ return
+ live: { live },
+ sender: { sender.userId },
+ room name: { roomName }
+
;
+ },
+}));
describe("VoiceBroadcastRecordingBody", () => {
- const title = "Test Title";
+ const testRoomName = "test room name";
const userId = "@user:example.com";
const roomMember = new RoomMember("!room:example.com", userId);
let onClick: MouseEventHandler;
beforeEach(() => {
onClick = jest.fn();
- // @ts-ignore
- mocked(MemberAvatar).mockReturnValue();
});
describe("when rendered", () => {
@@ -44,10 +48,9 @@ describe("VoiceBroadcastRecordingBody", () => {
renderResult = render(
,
);
});
@@ -56,19 +59,9 @@ describe("VoiceBroadcastRecordingBody", () => {
expect(renderResult.container).toMatchSnapshot();
});
- it("should pass the props to MemberAvatar", () => {
- expect(mocked(MemberAvatar)).toHaveBeenCalledWith(
- {
- member: roomMember,
- fallbackUserId: userId,
- },
- {},
- );
- });
-
describe("and clicked", () => {
beforeEach(async () => {
- await userEvent.click(renderResult.getByText(title));
+ await userEvent.click(renderResult.getByTestId("voice-broadcast-header"));
});
it("should call the onClick prop", () => {
@@ -84,10 +77,9 @@ describe("VoiceBroadcastRecordingBody", () => {
renderResult = render(
,
);
});
diff --git a/test/voice-broadcast/components/molecules/__snapshots__/VoiceBroadcastRecordingBody-test.tsx.snap b/test/voice-broadcast/components/molecules/__snapshots__/VoiceBroadcastRecordingBody-test.tsx.snap
index 6ea866ec56..a642e8dffc 100644
--- a/test/voice-broadcast/components/molecules/__snapshots__/VoiceBroadcastRecordingBody-test.tsx.snap
+++ b/test/voice-broadcast/components/molecules/__snapshots__/VoiceBroadcastRecordingBody-test.tsx.snap
@@ -6,27 +6,13 @@ exports[`VoiceBroadcastRecordingBody when rendered should render the expected HT
class="mx_VoiceBroadcastRecordingBody"
>
-
-
-
- Live
+ live:
+ , sender:
+ @user:example.com
+ , room name:
+ test room name
diff --git a/test/voice-broadcast/models/VoiceBroadcastRecording-test.ts b/test/voice-broadcast/models/VoiceBroadcastRecording-test.ts
index b0e9939164..5fca34e035 100644
--- a/test/voice-broadcast/models/VoiceBroadcastRecording-test.ts
+++ b/test/voice-broadcast/models/VoiceBroadcastRecording-test.ts
@@ -15,25 +15,57 @@ limitations under the License.
*/
import { mocked } from "jest-mock";
-import { EventTimelineSet, EventType, MatrixClient, MatrixEvent, RelationType, Room } from "matrix-js-sdk/src/matrix";
+import {
+ EventTimelineSet,
+ EventType,
+ MatrixClient,
+ MatrixEvent,
+ MsgType,
+ RelationType,
+ Room,
+} from "matrix-js-sdk/src/matrix";
import { Relations } from "matrix-js-sdk/src/models/relations";
+import { uploadFile } from "../../../src/ContentMessages";
+import { IEncryptedFile } from "../../../src/customisations/models/IMediaEventContent";
+import { createVoiceMessageContent } from "../../../src/utils/createVoiceMessageContent";
import {
+ ChunkRecordedPayload,
+ createVoiceBroadcastRecorder,
VoiceBroadcastInfoEventContent,
VoiceBroadcastInfoEventType,
VoiceBroadcastInfoState,
+ VoiceBroadcastRecorder,
+ VoiceBroadcastRecorderEvent,
VoiceBroadcastRecording,
VoiceBroadcastRecordingEvent,
} from "../../../src/voice-broadcast";
import { mkEvent, mkStubRoom, stubClient } from "../../test-utils";
+jest.mock("../../../src/voice-broadcast/audio/VoiceBroadcastRecorder", () => ({
+ ...jest.requireActual("../../../src/voice-broadcast/audio/VoiceBroadcastRecorder") as object,
+ createVoiceBroadcastRecorder: jest.fn(),
+}));
+
+jest.mock("../../../src/ContentMessages", () => ({
+ uploadFile: jest.fn(),
+}));
+
+jest.mock("../../../src/utils/createVoiceMessageContent", () => ({
+ createVoiceMessageContent: jest.fn(),
+}));
+
describe("VoiceBroadcastRecording", () => {
const roomId = "!room:example.com";
+ const uploadedUrl = "mxc://example.com/vb";
+ const uploadedFile = { file: true } as unknown as IEncryptedFile;
let room: Room;
let client: MatrixClient;
let infoEvent: MatrixEvent;
let voiceBroadcastRecording: VoiceBroadcastRecording;
let onStateChanged: (state: VoiceBroadcastInfoState) => void;
+ let voiceBroadcastRecorder: VoiceBroadcastRecorder;
+ let onChunkRecorded: (chunk: ChunkRecordedPayload) => Promise