1
0
mirror of https://github.com/matrix-org/matrix-react-sdk.git synced 2025-08-07 21:23:00 +03:00

Merge branch 'develop' into feat/add-plain-text-mode

This commit is contained in:
Florian Duros
2022-10-26 17:16:34 +02:00
committed by GitHub
65 changed files with 1008 additions and 296 deletions

View File

@@ -19,6 +19,7 @@ import { ClientEvent, MatrixClient } from "matrix-js-sdk/src/client";
import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { SyncState } from "matrix-js-sdk/src/sync";
import { waitFor } from "@testing-library/react";
import BasePlatform from "../src/BasePlatform";
import { ElementCall } from "../src/models/Call";
@@ -29,8 +30,15 @@ import {
createLocalNotificationSettingsIfNeeded,
getLocalNotificationAccountDataEventType,
} from "../src/utils/notifications";
import { getMockClientWithEventEmitter, mkEvent, mkRoom, mockClientMethodsUser, mockPlatformPeg } from "./test-utils";
import { getMockClientWithEventEmitter, mkEvent, mockClientMethodsUser, mockPlatformPeg } from "./test-utils";
import { IncomingCallToast } from "../src/toasts/IncomingCallToast";
import { SdkContextClass } from "../src/contexts/SDKContext";
import UserActivity from "../src/UserActivity";
import Modal from "../src/Modal";
import { mkThread } from "./test-utils/threads";
import dis from "../src/dispatcher/dispatcher";
import { ThreadPayload } from "../src/dispatcher/payloads/ThreadPayload";
import { Action } from "../src/dispatcher/actions";
jest.mock("../src/utils/notifications", () => ({
// @ts-ignore
@@ -50,10 +58,12 @@ describe("Notifier", () => {
let MockPlatform: MockedObject<BasePlatform>;
let mockClient: MockedObject<MatrixClient>;
let testRoom: MockedObject<Room>;
let testRoom: Room;
let accountDataEventKey: string;
let accountDataStore = {};
let mockSettings: Record<string, boolean> = {};
const userId = "@bob:example.org";
beforeEach(() => {
@@ -78,7 +88,7 @@ describe("Notifier", () => {
};
accountDataEventKey = getLocalNotificationAccountDataEventType(mockClient.deviceId);
testRoom = mkRoom(mockClient, roomId);
testRoom = new Room(roomId, mockClient, mockClient.getUserId());
MockPlatform = mockPlatformPeg({
supportsNotifications: jest.fn().mockReturnValue(true),
@@ -89,7 +99,9 @@ describe("Notifier", () => {
Notifier.isBodyEnabled = jest.fn().mockReturnValue(true);
mockClient.getRoom.mockReturnValue(testRoom);
mockClient.getRoom.mockImplementation(id => {
return id === roomId ? testRoom : new Room(id, mockClient, mockClient.getUserId());
});
});
describe('triggering notification from events', () => {
@@ -121,13 +133,14 @@ describe("Notifier", () => {
},
});
const enabledSettings = [
'notificationsEnabled',
'audioNotificationsEnabled',
];
mockSettings = {
'notificationsEnabled': true,
'audioNotificationsEnabled': true,
};
// enable notifications by default
jest.spyOn(SettingsStore, "getValue").mockImplementation(
settingName => enabledSettings.includes(settingName),
jest.spyOn(SettingsStore, "getValue").mockReset().mockImplementation(
settingName => mockSettings[settingName] ?? false,
);
});
@@ -253,16 +266,13 @@ describe("Notifier", () => {
});
const callOnEvent = (type?: string) => {
const callEvent = {
getContent: () => { },
getRoomId: () => roomId,
isBeingDecrypted: () => false,
isDecryptionFailure: () => false,
getSender: () => "@alice:foo",
getType: () => type ?? ElementCall.CALL_EVENT_TYPE.name,
getStateKey: () => "state_key",
} as unknown as MatrixEvent;
const callEvent = mkEvent({
type: type ?? ElementCall.CALL_EVENT_TYPE.name,
user: "@alice:foo",
room: roomId,
content: {},
event: true,
});
Notifier.onEvent(callEvent);
return callEvent;
};
@@ -345,4 +355,72 @@ describe("Notifier", () => {
expect(createLocalNotificationSettingsIfNeededMock).toHaveBeenCalled();
});
});
describe('_evaluateEvent', () => {
beforeEach(() => {
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId")
.mockReturnValue(testRoom.roomId);
jest.spyOn(UserActivity.sharedInstance(), "userActiveRecently")
.mockReturnValue(true);
jest.spyOn(Modal, "hasDialogs").mockReturnValue(false);
jest.spyOn(Notifier, "_displayPopupNotification").mockReset();
jest.spyOn(Notifier, "isEnabled").mockReturnValue(true);
mockClient.getPushActionsForEvent.mockReturnValue({
notify: true,
tweaks: {
sound: true,
},
});
});
it("should show a pop-up", () => {
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(0);
Notifier._evaluateEvent(testEvent);
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(0);
const eventFromOtherRoom = mkEvent({
event: true,
type: "m.room.message",
user: "@user1:server",
room: "!otherroom:example.org",
content: {},
});
Notifier._evaluateEvent(eventFromOtherRoom);
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(1);
});
it("should a pop-up for thread event", async () => {
const { events, rootEvent } = mkThread({
room: testRoom,
client: mockClient,
authorId: "@bob:example.org",
participantUserIds: ["@bob:example.org"],
});
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(0);
Notifier._evaluateEvent(rootEvent);
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(0);
Notifier._evaluateEvent(events[1]);
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(1);
dis.dispatch<ThreadPayload>({
action: Action.ViewThread,
thread_id: rootEvent.getId(),
});
await waitFor(() =>
expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBe(rootEvent.getId()),
);
Notifier._evaluateEvent(events[1]);
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(1);
});
});
});

View File

@@ -28,6 +28,7 @@ import { act } from "react-dom/test-utils";
import ThreadView from "../../../src/components/structures/ThreadView";
import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
import RoomContext from "../../../src/contexts/RoomContext";
import { SdkContextClass } from "../../../src/contexts/SDKContext";
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
import DMRoomMap from "../../../src/utils/DMRoomMap";
import ResizeNotifier from "../../../src/utils/ResizeNotifier";
@@ -155,4 +156,13 @@ describe("ThreadView", () => {
ROOM_ID, rootEvent2.getId(), expectedMessageBody(rootEvent2, "yolo"),
);
});
it("sets the correct thread in the room view store", async () => {
// expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBeNull();
const { unmount } = await getComponent();
expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBe(rootEvent.getId());
unmount();
await waitFor(() => expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBeNull());
});
});

View File

@@ -0,0 +1,57 @@
/*
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 { fireEvent, render } from '@testing-library/react';
import LearnMore from '../../../../src/components/views/elements/LearnMore';
import Modal from '../../../../src/Modal';
import InfoDialog from '../../../../src/components/views/dialogs/InfoDialog';
describe('<LearnMore />', () => {
const defaultProps = {
title: 'Test',
description: 'test test test',
['data-testid']: 'testid',
};
const getComponent = (props = {}) =>
(<LearnMore {...defaultProps} {...props} />);
const modalSpy = jest.spyOn(Modal, 'createDialog').mockReturnValue(undefined);
beforeEach(() => {
jest.clearAllMocks();
});
it('renders button', () => {
const { container } = render(getComponent());
expect(container).toMatchSnapshot();
});
it('opens modal on click', async () => {
const { getByTestId } = render(getComponent());
fireEvent.click(getByTestId('testid'));
expect(modalSpy).toHaveBeenCalledWith(
InfoDialog,
{
button: 'Got it',
description: defaultProps.description,
hasCloseButton: true,
title: defaultProps.title,
});
});
});

View File

@@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<LearnMore /> renders button 1`] = `
<div>
<div
class="mx_AccessibleButton mx_LearnMore_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
data-testid="testid"
role="button"
tabindex="0"
>
Learn more
</div>
</div>
`;

View File

@@ -37,7 +37,16 @@ HTMLCollection [
<p
class="mx_DeviceSecurityCard_description"
>
Consider signing out from old sessions (90 days or older) you don't use anymore
<span>
Consider signing out from old sessions (90 days or older) you don't use anymore.
<div
class="mx_AccessibleButton mx_LearnMore_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
role="button"
tabindex="0"
>
Learn more
</div>
</span>
</p>
</div>
</div>
@@ -72,7 +81,16 @@ HTMLCollection [
<p
class="mx_DeviceSecurityCard_description"
>
Verify your sessions for enhanced secure messaging or sign out from those you don't recognize or use anymore.
<span>
Verify your sessions for enhanced secure messaging or sign out from those you don't recognize or use anymore.
<div
class="mx_AccessibleButton mx_LearnMore_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
role="button"
tabindex="0"
>
Learn more
</div>
</span>
</p>
</div>
</div>
@@ -107,7 +125,16 @@ HTMLCollection [
<p
class="mx_DeviceSecurityCard_description"
>
For best security, sign out from any session that you don't recognize or use anymore.
<span>
For best security, sign out from any session that you don't recognize or use anymore.
<div
class="mx_AccessibleButton mx_LearnMore_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
role="button"
tabindex="0"
>
Learn more
</div>
</span>
</p>
</div>
</div>

View File

@@ -20,10 +20,6 @@ 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 друг",

View File

@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import fetchMock from "fetch-mock-jest";
import { TextDecoder, TextEncoder } from "util";
// jest 27 removes setImmediate from jsdom
@@ -54,3 +55,9 @@ global.TextDecoder = TextDecoder;
// prevent errors whenever a component tries to manually scroll.
window.HTMLElement.prototype.scrollIntoView = jest.fn();
// set up fetch API mock
fetchMock.config.overwriteRoutes = false;
fetchMock.catch("");
fetchMock.get("/image-file-stub", "image file stub");
window.fetch = fetchMock.sandbox();

View File

@@ -0,0 +1,319 @@
/*
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 { SlidingSync, SlidingSyncEvent } from 'matrix-js-sdk/src/sliding-sync';
import { Room } from 'matrix-js-sdk/src/matrix';
import {
LISTS_UPDATE_EVENT,
SlidingRoomListStoreClass,
SlidingSyncSortToFilter,
} from "../../../src/stores/room-list/SlidingRoomListStore";
import { SpaceStoreClass } from "../../../src/stores/spaces/SpaceStore";
import { MockEventEmitter, stubClient, untilEmission } from "../../test-utils";
import { TestSdkContext } from '../../TestSdkContext';
import { SlidingSyncManager } from '../../../src/SlidingSyncManager';
import { RoomViewStore } from '../../../src/stores/RoomViewStore';
import { MatrixDispatcher } from '../../../src/dispatcher/dispatcher';
import { SortAlgorithm } from '../../../src/stores/room-list/algorithms/models';
import { DefaultTagID, TagID } from '../../../src/stores/room-list/models';
import { UPDATE_SELECTED_SPACE } from '../../../src/stores/spaces';
import { LISTS_LOADING_EVENT } from '../../../src/stores/room-list/RoomListStore';
import { UPDATE_EVENT } from '../../../src/stores/AsyncStore';
jest.mock('../../../src/SlidingSyncManager');
const MockSlidingSyncManager = <jest.Mock<SlidingSyncManager>><unknown>SlidingSyncManager;
describe("SlidingRoomListStore", () => {
let store: SlidingRoomListStoreClass;
let context: TestSdkContext;
let dis: MatrixDispatcher;
let activeSpace: string;
let tagIdToIndex = {};
beforeEach(async () => {
context = new TestSdkContext();
context.client = stubClient();
context._SpaceStore = new MockEventEmitter<SpaceStoreClass>({
traverseSpace: jest.fn(),
get activeSpace() {
return activeSpace;
},
}) as SpaceStoreClass;
context._SlidingSyncManager = new MockSlidingSyncManager();
context._SlidingSyncManager.slidingSync = mocked(new MockEventEmitter({
getListData: jest.fn(),
}) as unknown as SlidingSync);
context._RoomViewStore = mocked(new MockEventEmitter({
getRoomId: jest.fn(),
}) as unknown as RoomViewStore);
// mock implementations to allow the store to map tag IDs to sliding sync list indexes and vice versa
let index = 0;
tagIdToIndex = {};
mocked(context._SlidingSyncManager.getOrAllocateListIndex).mockImplementation((listId: string): number => {
if (tagIdToIndex[listId] != null) {
return tagIdToIndex[listId];
}
tagIdToIndex[listId] = index;
index++;
return index;
});
mocked(context.slidingSyncManager.listIdForIndex).mockImplementation((i) => {
for (const tagId in tagIdToIndex) {
const j = tagIdToIndex[tagId];
if (i === j) {
return tagId;
}
}
return null;
});
mocked(context._SlidingSyncManager.ensureListRegistered).mockResolvedValue({
ranges: [[0, 10]],
});
dis = new MatrixDispatcher();
store = new SlidingRoomListStoreClass(dis, context);
});
describe("spaces", () => {
it("alters 'filters.spaces' on the DefaultTagID.Untagged list when the selected space changes", async () => {
await store.start(); // call onReady
const spaceRoomId = "!foo:bar";
const p = untilEmission(store, LISTS_LOADING_EVENT, (listName, isLoading) => {
return listName === DefaultTagID.Untagged && !isLoading;
});
// change the active space
activeSpace = spaceRoomId;
context._SpaceStore.emit(UPDATE_SELECTED_SPACE, spaceRoomId, false);
await p;
expect(context._SlidingSyncManager.ensureListRegistered).toHaveBeenCalledWith(
tagIdToIndex[DefaultTagID.Untagged],
{
filters: expect.objectContaining({
spaces: [spaceRoomId],
}),
},
);
});
it("alters 'filters.spaces' on the DefaultTagID.Untagged list if it loads with an active space", async () => {
// change the active space before we are ready
const spaceRoomId = "!foo2:bar";
activeSpace = spaceRoomId;
const p = untilEmission(store, LISTS_LOADING_EVENT, (listName, isLoading) => {
return listName === DefaultTagID.Untagged && !isLoading;
});
await store.start(); // call onReady
await p;
expect(context._SlidingSyncManager.ensureListRegistered).toHaveBeenCalledWith(
tagIdToIndex[DefaultTagID.Untagged],
expect.objectContaining({
filters: expect.objectContaining({
spaces: [spaceRoomId],
}),
}),
);
});
it("includes subspaces in 'filters.spaces' when the selected space has subspaces", async () => {
await store.start(); // call onReady
const spaceRoomId = "!foo:bar";
const subSpace1 = "!ss1:bar";
const subSpace2 = "!ss2:bar";
const p = untilEmission(store, LISTS_LOADING_EVENT, (listName, isLoading) => {
return listName === DefaultTagID.Untagged && !isLoading;
});
mocked(context._SpaceStore.traverseSpace).mockImplementation(
(spaceId: string, fn: (roomId: string) => void) => {
if (spaceId === spaceRoomId) {
fn(subSpace1);
fn(subSpace2);
}
},
);
// change the active space
activeSpace = spaceRoomId;
context._SpaceStore.emit(UPDATE_SELECTED_SPACE, spaceRoomId, false);
await p;
expect(context._SlidingSyncManager.ensureListRegistered).toHaveBeenCalledWith(
tagIdToIndex[DefaultTagID.Untagged],
{
filters: expect.objectContaining({
spaces: [spaceRoomId, subSpace1, subSpace2],
}),
},
);
});
});
it("setTagSorting alters the 'sort' option in the list", async () => {
mocked(context._SlidingSyncManager.getOrAllocateListIndex).mockReturnValue(0);
const tagId: TagID = "foo";
await store.setTagSorting(tagId, SortAlgorithm.Alphabetic);
expect(context._SlidingSyncManager.ensureListRegistered).toBeCalledWith(0, {
sort: SlidingSyncSortToFilter[SortAlgorithm.Alphabetic],
});
expect(store.getTagSorting(tagId)).toEqual(SortAlgorithm.Alphabetic);
await store.setTagSorting(tagId, SortAlgorithm.Recent);
expect(context._SlidingSyncManager.ensureListRegistered).toBeCalledWith(0, {
sort: SlidingSyncSortToFilter[SortAlgorithm.Recent],
});
expect(store.getTagSorting(tagId)).toEqual(SortAlgorithm.Recent);
});
it("getTagsForRoom gets the tags for the room", async () => {
await store.start();
const untaggedIndex = context._SlidingSyncManager.getOrAllocateListIndex(DefaultTagID.Untagged);
const favIndex = context._SlidingSyncManager.getOrAllocateListIndex(DefaultTagID.Favourite);
const roomA = "!a:localhost";
const roomB = "!b:localhost";
const indexToListData = {
[untaggedIndex]: {
joinedCount: 10,
roomIndexToRoomId: {
0: roomA,
1: roomB,
},
},
[favIndex]: {
joinedCount: 2,
roomIndexToRoomId: {
0: roomB,
},
},
};
mocked(context._SlidingSyncManager.slidingSync.getListData).mockImplementation((i: number) => {
return indexToListData[i] || null;
});
expect(store.getTagsForRoom(new Room(roomA, context.client, context.client.getUserId()))).toEqual(
[DefaultTagID.Untagged],
);
expect(store.getTagsForRoom(new Room(roomB, context.client, context.client.getUserId()))).toEqual(
[DefaultTagID.Favourite, DefaultTagID.Untagged],
);
});
it("emits LISTS_UPDATE_EVENT when slidingSync lists update", async () => {
await store.start();
const roomA = "!a:localhost";
const roomB = "!b:localhost";
const roomC = "!c:localhost";
const tagId = DefaultTagID.Favourite;
const listIndex = context.slidingSyncManager.getOrAllocateListIndex(tagId);
const joinCount = 10;
const roomIndexToRoomId = { // mixed to ensure we sort
1: roomB,
2: roomC,
0: roomA,
};
const rooms = [
new Room(roomA, context.client, context.client.getUserId()),
new Room(roomB, context.client, context.client.getUserId()),
new Room(roomC, context.client, context.client.getUserId()),
];
mocked(context.client.getRoom).mockImplementation((roomId: string) => {
switch (roomId) {
case roomA:
return rooms[0];
case roomB:
return rooms[1];
case roomC:
return rooms[2];
}
return null;
});
const p = untilEmission(store, LISTS_UPDATE_EVENT);
context.slidingSyncManager.slidingSync.emit(SlidingSyncEvent.List, listIndex, joinCount, roomIndexToRoomId);
await p;
expect(store.getCount(tagId)).toEqual(joinCount);
expect(store.orderedLists[tagId]).toEqual(rooms);
});
it("sets the sticky room on the basis of the viewed room in RoomViewStore", async () => {
await store.start();
// seed the store with 3 rooms
const roomIdA = "!a:localhost";
const roomIdB = "!b:localhost";
const roomIdC = "!c:localhost";
const tagId = DefaultTagID.Favourite;
const listIndex = context.slidingSyncManager.getOrAllocateListIndex(tagId);
const joinCount = 10;
const roomIndexToRoomId = { // mixed to ensure we sort
1: roomIdB,
2: roomIdC,
0: roomIdA,
};
const roomA = new Room(roomIdA, context.client, context.client.getUserId());
const roomB = new Room(roomIdB, context.client, context.client.getUserId());
const roomC = new Room(roomIdC, context.client, context.client.getUserId());
mocked(context.client.getRoom).mockImplementation((roomId: string) => {
switch (roomId) {
case roomIdA:
return roomA;
case roomIdB:
return roomB;
case roomIdC:
return roomC;
}
return null;
});
mocked(context._SlidingSyncManager.slidingSync.getListData).mockImplementation((i: number) => {
if (i !== listIndex) {
return null;
}
return {
roomIndexToRoomId: roomIndexToRoomId,
joinedCount: joinCount,
};
});
let p = untilEmission(store, LISTS_UPDATE_EVENT);
context.slidingSyncManager.slidingSync.emit(SlidingSyncEvent.List, listIndex, joinCount, roomIndexToRoomId);
await p;
expect(store.orderedLists[tagId]).toEqual([roomA, roomB, roomC]);
// make roomB sticky and inform the store
mocked(context.roomViewStore.getRoomId).mockReturnValue(roomIdB);
context.roomViewStore.emit(UPDATE_EVENT);
// bump room C to the top, room B should not move from i=1 despite the list update saying to
roomIndexToRoomId[0] = roomIdC;
roomIndexToRoomId[1] = roomIdA;
roomIndexToRoomId[2] = roomIdB;
p = untilEmission(store, LISTS_UPDATE_EVENT);
context.slidingSyncManager.slidingSync.emit(SlidingSyncEvent.List, listIndex, joinCount, roomIndexToRoomId);
await p;
// check that B didn't move and that A was put below B
expect(store.orderedLists[tagId]).toEqual([roomC, roomB, roomA]);
// make room C sticky: rooms should move as a result, without needing an additional list update
mocked(context.roomViewStore.getRoomId).mockReturnValue(roomIdC);
p = untilEmission(store, LISTS_UPDATE_EVENT);
context.roomViewStore.emit(UPDATE_EVENT);
await p;
expect(store.orderedLists[tagId].map((r) => r.roomId)).toEqual([roomC, roomA, roomB].map((r) => r.roomId));
});
});

View File

@@ -6,7 +6,7 @@ Array [
Array [
Object {
"deviceInfo": DeviceInfo {
"algorithms": undefined,
"algorithms": Array [],
"deviceId": "aliceWeb",
"keys": Object {},
"known": false,
@@ -18,7 +18,7 @@ Array [
},
Object {
"deviceInfo": DeviceInfo {
"algorithms": undefined,
"algorithms": Array [],
"deviceId": "aliceMobile",
"keys": Object {},
"known": false,
@@ -37,7 +37,7 @@ Array [
Array [
Object {
"deviceInfo": DeviceInfo {
"algorithms": undefined,
"algorithms": Array [],
"deviceId": "bobDesktop",
"keys": Object {},
"known": false,

View File

@@ -21,6 +21,26 @@ import { MatrixClient, User } from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "../../src/MatrixClientPeg";
/**
* Mocked generic class with a real EventEmitter.
* Useful for mocks which need event emitters.
*/
export class MockEventEmitter<T> extends EventEmitter {
/**
* Construct a new event emitter with additional properties/functions. The event emitter functions
* like .emit and .on will be real.
* @param mockProperties An object with the mock property or function implementations. 'getters'
* are correctly cloned to this event emitter.
*/
constructor(mockProperties: Partial<Record<MethodKeysOf<T>|PropertyKeysOf<T>, unknown>> = {}) {
super();
// We must use defineProperties and not assign as the former clones getters correctly,
// whereas the latter invokes the getter and sets the return value permanently on the
// destination object.
Object.defineProperties(this, Object.getOwnPropertyDescriptors(mockProperties));
}
}
/**
* Mock client with real event emitter
* useful for testing code that listens

View File

@@ -28,7 +28,7 @@ import { isSelfLocation } from "../../../src/utils/location";
describe("isSelfLocation", () => {
it("Returns true for a full m.asset event", () => {
const content = makeLocationContent("", '0');
const content = makeLocationContent("", '0', Date.now());
expect(isSelfLocation(content)).toBe(true);
});

View File

@@ -50,7 +50,7 @@ describe("VoiceBroadcastRecordingBody", () => {
room: roomId,
user: userId,
});
recording = new VoiceBroadcastRecording(infoEvent, client, VoiceBroadcastInfoState.Running);
recording = new VoiceBroadcastRecording(infoEvent, client, VoiceBroadcastInfoState.Resumed);
});
describe("when rendering a live broadcast", () => {

View File

@@ -118,7 +118,7 @@ describe("VoiceBroadcastRecordingPip", () => {
});
it("should resume the recording", () => {
expect(recording.getState()).toBe(VoiceBroadcastInfoState.Running);
expect(recording.getState()).toBe(VoiceBroadcastInfoState.Resumed);
});
});
});

View File

@@ -3,7 +3,7 @@
exports[`VoiceBroadcastPlaybackBody when rendering a 0 broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastPlaybackBody"
class="mx_VoiceBroadcastBody"
>
<div
class="mx_VoiceBroadcastHeader"
@@ -51,7 +51,7 @@ exports[`VoiceBroadcastPlaybackBody when rendering a 0 broadcast should render a
</div>
</div>
<div
class="mx_VoiceBroadcastPlaybackBody_controls"
class="mx_VoiceBroadcastBody_controls"
>
<div
aria-label="resume voice broadcast"
@@ -71,7 +71,7 @@ exports[`VoiceBroadcastPlaybackBody when rendering a 0 broadcast should render a
exports[`VoiceBroadcastPlaybackBody when rendering a 1 broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastPlaybackBody"
class="mx_VoiceBroadcastBody"
>
<div
class="mx_VoiceBroadcastHeader"
@@ -119,7 +119,7 @@ exports[`VoiceBroadcastPlaybackBody when rendering a 1 broadcast should render a
</div>
</div>
<div
class="mx_VoiceBroadcastPlaybackBody_controls"
class="mx_VoiceBroadcastBody_controls"
>
<div
aria-label="pause voice broadcast"
@@ -139,7 +139,7 @@ exports[`VoiceBroadcastPlaybackBody when rendering a 1 broadcast should render a
exports[`VoiceBroadcastPlaybackBody when rendering a buffering voice broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastPlaybackBody"
class="mx_VoiceBroadcastBody"
>
<div
class="mx_VoiceBroadcastHeader"
@@ -187,7 +187,7 @@ exports[`VoiceBroadcastPlaybackBody when rendering a buffering voice broadcast s
</div>
</div>
<div
class="mx_VoiceBroadcastPlaybackBody_controls"
class="mx_VoiceBroadcastBody_controls"
>
<div
class="mx_Spinner"

View File

@@ -3,7 +3,7 @@
exports[`VoiceBroadcastRecordingBody when rendering a live broadcast should render the expected HTML 1`] = `
<div>
<div
class="mx_VoiceBroadcastRecordingBody"
class="mx_VoiceBroadcastBody"
>
<div
class="mx_VoiceBroadcastHeader"

View File

@@ -3,7 +3,7 @@
exports[`VoiceBroadcastRecordingPip when rendering a paused recording should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastRecordingPip"
class="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip"
>
<div
class="mx_VoiceBroadcastHeader"
@@ -43,10 +43,10 @@ exports[`VoiceBroadcastRecordingPip when rendering a paused recording should ren
</div>
</div>
<hr
class="mx_VoiceBroadcastRecordingPip_divider"
class="mx_VoiceBroadcastBody_divider"
/>
<div
class="mx_VoiceBroadcastRecordingPip_controls"
class="mx_VoiceBroadcastBody_controls"
>
<div
aria-label="resume voice broadcast"
@@ -76,7 +76,7 @@ exports[`VoiceBroadcastRecordingPip when rendering a paused recording should ren
exports[`VoiceBroadcastRecordingPip when rendering a started recording should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastRecordingPip"
class="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip"
>
<div
class="mx_VoiceBroadcastHeader"
@@ -116,10 +116,10 @@ exports[`VoiceBroadcastRecordingPip when rendering a started recording should re
</div>
</div>
<hr
class="mx_VoiceBroadcastRecordingPip_divider"
class="mx_VoiceBroadcastBody_divider"
/>
<div
class="mx_VoiceBroadcastRecordingPip_controls"
class="mx_VoiceBroadcastBody_controls"
>
<div
aria-label="pause voice broadcast"

View File

@@ -190,9 +190,9 @@ describe("VoiceBroadcastPlayback", () => {
onStateChanged = jest.fn();
});
describe("when there is a running broadcast without chunks yet", () => {
describe(`when there is a ${VoiceBroadcastInfoState.Resumed} broadcast without chunks yet`, () => {
beforeEach(() => {
infoEvent = mkInfoEvent(VoiceBroadcastInfoState.Running);
infoEvent = mkInfoEvent(VoiceBroadcastInfoState.Resumed);
playback = mkPlayback();
setUpChunkEvents([]);
});
@@ -236,9 +236,9 @@ describe("VoiceBroadcastPlayback", () => {
});
});
describe("when there is a running voice broadcast with some chunks", () => {
describe(`when there is a ${VoiceBroadcastInfoState.Resumed} voice broadcast with some chunks`, () => {
beforeEach(() => {
infoEvent = mkInfoEvent(VoiceBroadcastInfoState.Running);
infoEvent = mkInfoEvent(VoiceBroadcastInfoState.Resumed);
playback = mkPlayback();
setUpChunkEvents([chunk2Event, chunk0Event, chunk1Event]);
});

View File

@@ -423,15 +423,15 @@ describe("VoiceBroadcastRecording", () => {
await action();
});
itShouldBeInState(VoiceBroadcastInfoState.Running);
itShouldSendAnInfoEvent(VoiceBroadcastInfoState.Running);
itShouldBeInState(VoiceBroadcastInfoState.Resumed);
itShouldSendAnInfoEvent(VoiceBroadcastInfoState.Resumed);
it("should start the recorder", () => {
expect(mocked(voiceBroadcastRecorder.start)).toHaveBeenCalled();
});
it("should emit a running state changed event", () => {
expect(onStateChanged).toHaveBeenCalledWith(VoiceBroadcastInfoState.Running);
it(`should emit a ${VoiceBroadcastInfoState.Resumed} state changed event`, () => {
expect(onStateChanged).toHaveBeenCalledWith(VoiceBroadcastInfoState.Resumed);
});
});
});

View File

@@ -121,7 +121,7 @@ describe("hasRoomLiveVoiceBroadcast", () => {
// all there are kind of live states
VoiceBroadcastInfoState.Started,
VoiceBroadcastInfoState.Paused,
VoiceBroadcastInfoState.Running,
VoiceBroadcastInfoState.Resumed,
])("when there is a live broadcast (%s) from the current user", (state: VoiceBroadcastInfoState) => {
beforeEach(() => {
addVoiceBroadcastInfoEvent(state, client.getUserId());
@@ -132,7 +132,7 @@ describe("hasRoomLiveVoiceBroadcast", () => {
describe("when there was a live broadcast, that has been stopped", () => {
beforeEach(() => {
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Running, client.getUserId());
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Resumed, client.getUserId());
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Stopped, client.getUserId());
});
@@ -141,7 +141,7 @@ describe("hasRoomLiveVoiceBroadcast", () => {
describe("when there is a live broadcast from another user", () => {
beforeEach(() => {
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Running, otherUserId);
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Resumed, otherUserId);
});
itShouldReturnTrueFalse();

View File

@@ -40,7 +40,7 @@ const testCases = [
[
"@user1:example.com",
"@user1:example.com",
VoiceBroadcastInfoState.Running,
VoiceBroadcastInfoState.Resumed,
true,
],
[

View File

@@ -128,7 +128,7 @@ describe("shouldDisplayAsVoiceBroadcastTile", () => {
describe.each(
[
VoiceBroadcastInfoState.Paused,
VoiceBroadcastInfoState.Running,
VoiceBroadcastInfoState.Resumed,
VoiceBroadcastInfoState.Stopped,
],
)("when a voice broadcast info event in state %s occurs", (state: VoiceBroadcastInfoState) => {

View File

@@ -161,7 +161,7 @@ describe("startNewVoiceBroadcastRecording", () => {
room.currentState.setStateEvents([
mkVoiceBroadcastInfoStateEvent(
roomId,
VoiceBroadcastInfoState.Running,
VoiceBroadcastInfoState.Resumed,
client.getUserId(),
client.getDeviceId(),
),
@@ -184,7 +184,7 @@ describe("startNewVoiceBroadcastRecording", () => {
room.currentState.setStateEvents([
mkVoiceBroadcastInfoStateEvent(
roomId,
VoiceBroadcastInfoState.Running,
VoiceBroadcastInfoState.Resumed,
otherUserId,
"ASD123",
),