Merge branch 'develop' into t3chguy/fix/27078
This commit is contained in:
@@ -44,17 +44,20 @@ export class MockedCall extends Call {
|
||||
}
|
||||
|
||||
public static create(room: Room, id: string) {
|
||||
room.addLiveEvents([
|
||||
mkEvent({
|
||||
event: true,
|
||||
type: this.EVENT_TYPE,
|
||||
room: room.roomId,
|
||||
user: "@alice:example.org",
|
||||
content: { "m.type": "m.video", "m.intent": "m.prompt" },
|
||||
skey: id,
|
||||
ts: Date.now(),
|
||||
}),
|
||||
]);
|
||||
room.addLiveEvents(
|
||||
[
|
||||
mkEvent({
|
||||
event: true,
|
||||
type: this.EVENT_TYPE,
|
||||
room: room.roomId,
|
||||
user: "@alice:example.org",
|
||||
content: { "m.type": "m.video", "m.intent": "m.prompt" },
|
||||
skey: id,
|
||||
ts: Date.now(),
|
||||
}),
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
// @ts-ignore deliberately calling a private method
|
||||
// Let CallStore know that a call might now exist
|
||||
CallStore.instance.updateRoom(room);
|
||||
@@ -81,17 +84,20 @@ export class MockedCall extends Call {
|
||||
|
||||
public destroy() {
|
||||
// Terminate the call for good measure
|
||||
this.room.addLiveEvents([
|
||||
mkEvent({
|
||||
event: true,
|
||||
type: MockedCall.EVENT_TYPE,
|
||||
room: this.room.roomId,
|
||||
user: "@alice:example.org",
|
||||
content: { ...this.event.getContent(), "m.terminated": "Call ended" },
|
||||
skey: this.widget.id,
|
||||
ts: Date.now(),
|
||||
}),
|
||||
]);
|
||||
this.room.addLiveEvents(
|
||||
[
|
||||
mkEvent({
|
||||
event: true,
|
||||
type: MockedCall.EVENT_TYPE,
|
||||
room: this.room.roomId,
|
||||
user: "@alice:example.org",
|
||||
content: { ...this.event.getContent(), "m.terminated": "Call ended" },
|
||||
skey: this.widget.id,
|
||||
ts: Date.now(),
|
||||
}),
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ export function getRoomContext(room: Room, override: Partial<IRoomState>): IRoom
|
||||
canAskToJoin: false,
|
||||
promptAskToJoin: false,
|
||||
viewRoomOpts: { buttons: [] },
|
||||
|
||||
isRoomEncrypted: false,
|
||||
...override,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ export function createTestClient(): MatrixClient {
|
||||
|
||||
getCrypto: jest.fn().mockReturnValue({
|
||||
getOwnDeviceKeys: jest.fn(),
|
||||
getUserDeviceInfo: jest.fn(),
|
||||
getUserDeviceInfo: jest.fn().mockResolvedValue(new Map()),
|
||||
getUserVerificationStatus: jest.fn(),
|
||||
getDeviceVerificationStatus: jest.fn(),
|
||||
resetKeyBackup: jest.fn(),
|
||||
@@ -135,6 +135,7 @@ export function createTestClient(): MatrixClient {
|
||||
loadSessionBackupPrivateKeyFromSecretStorage: jest.fn(),
|
||||
storeSessionBackupPrivateKey: jest.fn(),
|
||||
getKeyBackupInfo: jest.fn().mockResolvedValue(null),
|
||||
getEncryptionInfoForEvent: jest.fn().mockResolvedValue(null),
|
||||
}),
|
||||
|
||||
getPushActionsForEvent: jest.fn(),
|
||||
|
||||
@@ -157,6 +157,6 @@ export const populateThread = async ({
|
||||
// that it is already loaded, and send the events again to the room
|
||||
// so they are added to the thread timeline.
|
||||
ret.thread.initialEventsFetched = true;
|
||||
await room.addLiveEvents(ret.events);
|
||||
await room.addLiveEvents(ret.events, { addToState: false });
|
||||
return ret;
|
||||
};
|
||||
|
||||
@@ -39,10 +39,6 @@ import { Action } from "../../src/dispatcher/actions";
|
||||
import { getFunctionalMembers } from "../../src/utils/room/getFunctionalMembers";
|
||||
import SettingsStore from "../../src/settings/SettingsStore";
|
||||
import { UIFeature } from "../../src/settings/UIFeature";
|
||||
import { VoiceBroadcastInfoState, VoiceBroadcastPlayback, VoiceBroadcastRecording } from "../../src/voice-broadcast";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "./voice-broadcast/utils/test-utils";
|
||||
import { SdkContextClass } from "../../src/contexts/SDKContext";
|
||||
import Modal from "../../src/Modal";
|
||||
import { createAudioContext } from "../../src/audio/compat";
|
||||
import * as ManagedHybrid from "../../src/widgets/ManagedHybrid";
|
||||
|
||||
@@ -403,53 +399,6 @@ describe("LegacyCallHandler", () => {
|
||||
await callHandler.placeCall(NATIVE_ROOM_ALICE, CallType.Voice);
|
||||
expect(spy).toHaveBeenCalledWith(MatrixClientPeg.safeGet().getRoom(NATIVE_ROOM_ALICE));
|
||||
});
|
||||
|
||||
describe("when listening to a voice broadcast", () => {
|
||||
let voiceBroadcastPlayback: VoiceBroadcastPlayback;
|
||||
|
||||
beforeEach(() => {
|
||||
voiceBroadcastPlayback = new VoiceBroadcastPlayback(
|
||||
mkVoiceBroadcastInfoStateEvent(
|
||||
"!room:example.com",
|
||||
VoiceBroadcastInfoState.Started,
|
||||
MatrixClientPeg.safeGet().getSafeUserId(),
|
||||
"d42",
|
||||
),
|
||||
MatrixClientPeg.safeGet(),
|
||||
SdkContextClass.instance.voiceBroadcastRecordingsStore,
|
||||
);
|
||||
SdkContextClass.instance.voiceBroadcastPlaybacksStore.setCurrent(voiceBroadcastPlayback);
|
||||
jest.spyOn(voiceBroadcastPlayback, "pause").mockImplementation();
|
||||
});
|
||||
|
||||
it("and placing a call should pause the broadcast", async () => {
|
||||
callHandler.placeCall(NATIVE_ROOM_ALICE, CallType.Voice);
|
||||
await untilCallHandlerEvent(callHandler, LegacyCallHandlerEvent.CallState);
|
||||
|
||||
expect(voiceBroadcastPlayback.pause).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when recording a voice broadcast", () => {
|
||||
beforeEach(() => {
|
||||
SdkContextClass.instance.voiceBroadcastRecordingsStore.setCurrent(
|
||||
new VoiceBroadcastRecording(
|
||||
mkVoiceBroadcastInfoStateEvent(
|
||||
"!room:example.com",
|
||||
VoiceBroadcastInfoState.Started,
|
||||
MatrixClientPeg.safeGet().getSafeUserId(),
|
||||
"d42",
|
||||
),
|
||||
MatrixClientPeg.safeGet(),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("and placing a call should show the info dialog", async () => {
|
||||
callHandler.placeCall(NATIVE_ROOM_ALICE, CallType.Voice);
|
||||
expect(Modal.createDialog).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("LegacyCallHandler without third party protocols", () => {
|
||||
@@ -528,9 +477,6 @@ describe("LegacyCallHandler without third party protocols", () => {
|
||||
audioElement.id = "remoteAudio";
|
||||
document.body.appendChild(audioElement);
|
||||
|
||||
SdkContextClass.instance.voiceBroadcastPlaybacksStore.clearCurrent();
|
||||
SdkContextClass.instance.voiceBroadcastRecordingsStore.clearCurrent();
|
||||
|
||||
fetchMock.get(
|
||||
"/media/ring.mp3",
|
||||
{ body: new Blob(["1", "2", "3", "4"], { type: "audio/mpeg" }) },
|
||||
|
||||
@@ -11,8 +11,6 @@ import fetchMockJest from "fetch-mock-jest";
|
||||
|
||||
import { advanceDateAndTime, stubClient } from "../test-utils";
|
||||
import { IMatrixClientPeg, MatrixClientPeg as peg } from "../../src/MatrixClientPeg";
|
||||
import SettingsStore from "../../src/settings/SettingsStore";
|
||||
import { SettingLevel } from "../../src/settings/SettingLevel";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
@@ -81,27 +79,18 @@ describe("MatrixClientPeg", () => {
|
||||
});
|
||||
|
||||
it("should initialise the rust crypto library by default", async () => {
|
||||
const mockSetValue = jest.spyOn(SettingsStore, "setValue").mockResolvedValue(undefined);
|
||||
|
||||
const mockInitRustCrypto = jest.spyOn(testPeg.safeGet(), "initRustCrypto").mockResolvedValue(undefined);
|
||||
|
||||
const cryptoStoreKey = new Uint8Array([1, 2, 3, 4]);
|
||||
await testPeg.start({ rustCryptoStoreKey: cryptoStoreKey });
|
||||
expect(mockInitRustCrypto).toHaveBeenCalledWith({ storageKey: cryptoStoreKey });
|
||||
|
||||
// we should have stashed the setting in the settings store
|
||||
expect(mockSetValue).toHaveBeenCalledWith("feature_rust_crypto", null, SettingLevel.DEVICE, true);
|
||||
});
|
||||
|
||||
it("Should migrate existing login", async () => {
|
||||
const mockSetValue = jest.spyOn(SettingsStore, "setValue").mockResolvedValue(undefined);
|
||||
const mockInitRustCrypto = jest.spyOn(testPeg.safeGet(), "initRustCrypto").mockResolvedValue(undefined);
|
||||
|
||||
await testPeg.start();
|
||||
expect(mockInitRustCrypto).toHaveBeenCalledTimes(1);
|
||||
|
||||
// we should have stashed the setting in the settings store
|
||||
expect(mockSetValue).toHaveBeenCalledWith("feature_rust_crypto", null, SettingLevel.DEVICE, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,8 +43,6 @@ 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";
|
||||
import { VoiceBroadcastChunkEventType, VoiceBroadcastInfoState } from "../../src/voice-broadcast";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "./voice-broadcast/utils/test-utils";
|
||||
import { addReplyToMessageContent } from "../../src/utils/Reply";
|
||||
|
||||
jest.mock("../../src/utils/notifications", () => ({
|
||||
@@ -85,16 +83,13 @@ describe("Notifier", () => {
|
||||
});
|
||||
};
|
||||
|
||||
const mkAudioEvent = (broadcastChunkContent?: object): MatrixEvent => {
|
||||
const chunkContent = broadcastChunkContent ? { [VoiceBroadcastChunkEventType]: broadcastChunkContent } : {};
|
||||
|
||||
const mkAudioEvent = (): MatrixEvent => {
|
||||
return mkEvent({
|
||||
event: true,
|
||||
type: EventType.RoomMessage,
|
||||
user: "@user:example.com",
|
||||
room: "!room:example.com",
|
||||
content: {
|
||||
...chunkContent,
|
||||
msgtype: MsgType.Audio,
|
||||
body: "test audio message",
|
||||
},
|
||||
@@ -320,24 +315,6 @@ describe("Notifier", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("should display the expected notification for a broadcast chunk with sequence = 1", () => {
|
||||
const audioEvent = mkAudioEvent({ sequence: 1 });
|
||||
Notifier.displayPopupNotification(audioEvent, testRoom);
|
||||
expect(MockPlatform.displayNotification).toHaveBeenCalledWith(
|
||||
"@user:example.com (!room1:server)",
|
||||
"@user:example.com started a voice broadcast",
|
||||
"data:image/png;base64,00",
|
||||
testRoom,
|
||||
audioEvent,
|
||||
);
|
||||
});
|
||||
|
||||
it("should display the expected notification for a broadcast chunk with sequence = 2", () => {
|
||||
const audioEvent = mkAudioEvent({ sequence: 2 });
|
||||
Notifier.displayPopupNotification(audioEvent, testRoom);
|
||||
expect(MockPlatform.displayNotification).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should strip reply fallback", () => {
|
||||
const event = mkMessage({
|
||||
msg: "Test",
|
||||
@@ -581,24 +558,6 @@ describe("Notifier", () => {
|
||||
Notifier.evaluateEvent(mkAudioEvent());
|
||||
expect(Notifier.displayPopupNotification).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should not show a notification for broadcast info events in any case", () => {
|
||||
// Let client decide to show a notification
|
||||
mockClient.getPushActionsForEvent.mockReturnValue({
|
||||
notify: true,
|
||||
tweaks: {},
|
||||
});
|
||||
|
||||
const broadcastStartedEvent = mkVoiceBroadcastInfoStateEvent(
|
||||
"!other:example.org",
|
||||
VoiceBroadcastInfoState.Started,
|
||||
"@user:example.com",
|
||||
"ABC123",
|
||||
);
|
||||
|
||||
Notifier.evaluateEvent(broadcastStartedEvent);
|
||||
expect(Notifier.displayPopupNotification).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("setPromptHidden", () => {
|
||||
@@ -624,8 +583,7 @@ describe("Notifier", () => {
|
||||
content: { body: "this is a thread root" },
|
||||
}),
|
||||
testRoom.threadsTimelineSets[0]!.getLiveTimeline(),
|
||||
false,
|
||||
false,
|
||||
{ toStartOfTimeline: false, fromCache: false, addToState: true },
|
||||
);
|
||||
|
||||
expect(fn).not.toHaveBeenCalled();
|
||||
|
||||
@@ -147,7 +147,7 @@ describe("RoomNotifs test", () => {
|
||||
|
||||
const itShouldCountPredecessorHighlightWhenThereIsAPredecessorInTheCreateEvent = (): void => {
|
||||
it("and there is a predecessor in the create event, it should count predecessor highlight", () => {
|
||||
room.addLiveEvents([mkCreateEvent(OLD_ROOM_ID)]);
|
||||
room.addLiveEvents([mkCreateEvent(OLD_ROOM_ID)], { addToState: true });
|
||||
|
||||
expect(getUnreadNotificationCount(room, NotificationCountType.Total, false)).toBe(8);
|
||||
expect(getUnreadNotificationCount(room, NotificationCountType.Highlight, false)).toBe(7);
|
||||
@@ -157,7 +157,7 @@ describe("RoomNotifs test", () => {
|
||||
const itShouldCountPredecessorHighlightWhenThereIsAPredecessorEvent = (): void => {
|
||||
it("and there is a predecessor event, it should count predecessor highlight", () => {
|
||||
client.getVisibleRooms();
|
||||
room.addLiveEvents([mkCreateEvent(OLD_ROOM_ID)]);
|
||||
room.addLiveEvents([mkCreateEvent(OLD_ROOM_ID)], { addToState: true });
|
||||
upsertRoomStateEvents(room, [mkPredecessorEvent(OLD_ROOM_ID)]);
|
||||
|
||||
expect(getUnreadNotificationCount(room, NotificationCountType.Total, false)).toBe(8);
|
||||
@@ -185,7 +185,7 @@ describe("RoomNotifs test", () => {
|
||||
itShouldCountPredecessorHighlightWhenThereIsAPredecessorEvent();
|
||||
|
||||
it("and there is only a predecessor event, it should not count predecessor highlight", () => {
|
||||
room.addLiveEvents([mkCreateEvent()]);
|
||||
room.addLiveEvents([mkCreateEvent()], { addToState: true });
|
||||
upsertRoomStateEvents(room, [mkPredecessorEvent(OLD_ROOM_ID)]);
|
||||
|
||||
expect(getUnreadNotificationCount(room, NotificationCountType.Total, false)).toBe(2);
|
||||
@@ -204,7 +204,7 @@ describe("RoomNotifs test", () => {
|
||||
itShouldCountPredecessorHighlightWhenThereIsAPredecessorEvent();
|
||||
|
||||
it("and there is only a predecessor event, it should count predecessor highlight", () => {
|
||||
room.addLiveEvents([mkCreateEvent()]);
|
||||
room.addLiveEvents([mkCreateEvent()], { addToState: true });
|
||||
upsertRoomStateEvents(room, [mkPredecessorEvent(OLD_ROOM_ID)]);
|
||||
|
||||
expect(getUnreadNotificationCount(room, NotificationCountType.Total, false)).toBe(8);
|
||||
@@ -212,7 +212,7 @@ describe("RoomNotifs test", () => {
|
||||
});
|
||||
|
||||
it("and there is an unknown room in the predecessor event, it should not count predecessor highlight", () => {
|
||||
room.addLiveEvents([mkCreateEvent()]);
|
||||
room.addLiveEvents([mkCreateEvent()], { addToState: true });
|
||||
upsertRoomStateEvents(room, [mkPredecessorEvent("!unknon:example.com")]);
|
||||
|
||||
expect(getUnreadNotificationCount(room, NotificationCountType.Total, false)).toBe(2);
|
||||
|
||||
@@ -18,10 +18,6 @@ describe("SdkConfig", () => {
|
||||
describe("with custom values", () => {
|
||||
beforeEach(() => {
|
||||
SdkConfig.put({
|
||||
voice_broadcast: {
|
||||
chunk_length: 42,
|
||||
max_length: 1337,
|
||||
},
|
||||
feedback: {
|
||||
existing_issues_url: "https://existing",
|
||||
} as any,
|
||||
@@ -30,8 +26,6 @@ describe("SdkConfig", () => {
|
||||
|
||||
it("should return the custom config", () => {
|
||||
const customConfig = JSON.parse(JSON.stringify(DEFAULTS));
|
||||
customConfig.voice_broadcast.chunk_length = 42;
|
||||
customConfig.voice_broadcast.max_length = 1337;
|
||||
customConfig.feedback.existing_issues_url = "https://existing";
|
||||
expect(SdkConfig.get()).toEqual(customConfig);
|
||||
});
|
||||
|
||||
@@ -66,10 +66,10 @@ describe("SupportedBrowser", () => {
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15",
|
||||
// Firefox 131 on macOS Sonoma
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:131.0) Gecko/20100101 Firefox/131.0",
|
||||
// Edge 129 on Windows
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/129.0.2792.79",
|
||||
// Edge 129 on macOS
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/129.0.2792.79",
|
||||
// Edge 131 on Windows
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.2903.70",
|
||||
// Edge 131 on macOS
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.2903.70",
|
||||
// Firefox 131 on Windows
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0",
|
||||
// Firefox 131 on Linux
|
||||
|
||||
@@ -16,29 +16,21 @@ import { SpaceStoreClass } from "../../src/stores/spaces/SpaceStore";
|
||||
import { WidgetLayoutStore } from "../../src/stores/widgets/WidgetLayoutStore";
|
||||
import { WidgetPermissionStore } from "../../src/stores/widgets/WidgetPermissionStore";
|
||||
import WidgetStore from "../../src/stores/WidgetStore";
|
||||
import {
|
||||
VoiceBroadcastPlaybacksStore,
|
||||
VoiceBroadcastPreRecordingStore,
|
||||
VoiceBroadcastRecordingsStore,
|
||||
} from "../../src/voice-broadcast";
|
||||
|
||||
/**
|
||||
* A class which provides the same API as SdkContextClass but adds additional unsafe setters which can
|
||||
* replace individual stores. This is useful for tests which need to mock out stores.
|
||||
*/
|
||||
export class TestSdkContext extends SdkContextClass {
|
||||
public declare _RightPanelStore?: RightPanelStore;
|
||||
public declare _RoomNotificationStateStore?: RoomNotificationStateStore;
|
||||
public declare _RoomViewStore?: RoomViewStore;
|
||||
public declare _WidgetPermissionStore?: WidgetPermissionStore;
|
||||
public declare _WidgetLayoutStore?: WidgetLayoutStore;
|
||||
public declare _WidgetStore?: WidgetStore;
|
||||
public declare _PosthogAnalytics?: PosthogAnalytics;
|
||||
public declare _SlidingSyncManager?: SlidingSyncManager;
|
||||
public declare _SpaceStore?: SpaceStoreClass;
|
||||
public declare _VoiceBroadcastRecordingsStore?: VoiceBroadcastRecordingsStore;
|
||||
public declare _VoiceBroadcastPreRecordingStore?: VoiceBroadcastPreRecordingStore;
|
||||
public declare _VoiceBroadcastPlaybacksStore?: VoiceBroadcastPlaybacksStore;
|
||||
declare public _RightPanelStore?: RightPanelStore;
|
||||
declare public _RoomNotificationStateStore?: RoomNotificationStateStore;
|
||||
declare public _RoomViewStore?: RoomViewStore;
|
||||
declare public _WidgetPermissionStore?: WidgetPermissionStore;
|
||||
declare public _WidgetLayoutStore?: WidgetLayoutStore;
|
||||
declare public _WidgetStore?: WidgetStore;
|
||||
declare public _PosthogAnalytics?: PosthogAnalytics;
|
||||
declare public _SlidingSyncManager?: SlidingSyncManager;
|
||||
declare public _SpaceStore?: SpaceStoreClass;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -138,7 +138,7 @@ describe("Unread", () => {
|
||||
room: roomId,
|
||||
content: {},
|
||||
});
|
||||
room.addLiveEvents([event]);
|
||||
room.addLiveEvents([event], { addToState: true });
|
||||
|
||||
// Don't care about the code path of hidden events.
|
||||
mocked(haveRendererForEvent).mockClear().mockReturnValue(true);
|
||||
@@ -157,7 +157,7 @@ describe("Unread", () => {
|
||||
content: {},
|
||||
});
|
||||
// Only for timeline events.
|
||||
room.addLiveEvents([event]);
|
||||
room.addLiveEvents([event], { addToState: true });
|
||||
|
||||
expect(doesRoomHaveUnreadMessages(room, false)).toBe(false);
|
||||
});
|
||||
@@ -201,7 +201,7 @@ describe("Unread", () => {
|
||||
content: {},
|
||||
});
|
||||
// Only for timeline events.
|
||||
room.addLiveEvents([event2]);
|
||||
room.addLiveEvents([event2], { addToState: true });
|
||||
|
||||
expect(doesRoomHaveUnreadMessages(room, false)).toBe(true);
|
||||
});
|
||||
@@ -403,7 +403,7 @@ describe("Unread", () => {
|
||||
redactedEvent.makeRedacted(redactedEvent, room);
|
||||
console.log("Event Id", redactedEvent.getId());
|
||||
// Only for timeline events.
|
||||
room.addLiveEvents([redactedEvent]);
|
||||
room.addLiveEvents([redactedEvent], { addToState: true });
|
||||
|
||||
expect(doesRoomHaveUnreadMessages(room, true)).toBe(true);
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
@@ -448,7 +448,7 @@ describe("Unread", () => {
|
||||
room: roomId,
|
||||
content: {},
|
||||
});
|
||||
room.addLiveEvents([event]);
|
||||
room.addLiveEvents([event], { addToState: true });
|
||||
});
|
||||
|
||||
it("an unthreaded receipt for the event makes the room read", () => {
|
||||
@@ -502,7 +502,7 @@ describe("Unread", () => {
|
||||
ts: 100,
|
||||
currentUserId: myId,
|
||||
});
|
||||
room.addLiveEvents(events);
|
||||
room.addLiveEvents(events, { addToState: true });
|
||||
threadEvent = events[1];
|
||||
});
|
||||
|
||||
@@ -555,7 +555,7 @@ describe("Unread", () => {
|
||||
room: roomId,
|
||||
content: {},
|
||||
});
|
||||
room.addLiveEvents([event]);
|
||||
room.addLiveEvents([event], { addToState: true });
|
||||
|
||||
// It still returns false
|
||||
expect(doesRoomHaveUnreadThreads(room)).toBe(false);
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`LegacyCallHandler when recording a voice broadcast and placing a call should show the info dialog 1`] = `
|
||||
[MockFunction] {
|
||||
"calls": [
|
||||
[
|
||||
[Function],
|
||||
{
|
||||
"description": <p>
|
||||
You can’t start a call as you are currently recording a live broadcast. Please end your live broadcast in order to start a call.
|
||||
</p>,
|
||||
"hasCloseButton": true,
|
||||
"title": "Can’t start a call",
|
||||
},
|
||||
],
|
||||
],
|
||||
"results": [
|
||||
{
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -7,13 +7,13 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { EventTimelineSet, PendingEventOrdering, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { EventTimelineSet, PendingEventOrdering, Room, RoomEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { screen, render, waitFor } from "jest-matrix-react";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import FilePanel from "../../../../src/components/structures/FilePanel";
|
||||
import ResizeNotifier from "../../../../src/utils/ResizeNotifier";
|
||||
import { stubClient } from "../../../test-utils";
|
||||
import { mkEvent, stubClient } from "../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
|
||||
jest.mock("matrix-js-sdk/src/matrix", () => ({
|
||||
@@ -47,4 +47,43 @@ describe("FilePanel", () => {
|
||||
});
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("addEncryptedLiveEvent", () => {
|
||||
it("should add file msgtype event to filtered timelineSet", async () => {
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
const room = new Room("!room:server", cli, cli.getSafeUserId(), {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
cli.reEmitter.reEmit(room, [RoomEvent.Timeline]);
|
||||
const timelineSet = new EventTimelineSet(room);
|
||||
room.getOrCreateFilteredTimelineSet = jest.fn().mockReturnValue(timelineSet);
|
||||
mocked(cli.getRoom).mockReturnValue(room);
|
||||
|
||||
let filePanel: FilePanel | null;
|
||||
render(
|
||||
<FilePanel
|
||||
roomId={room.roomId}
|
||||
onClose={jest.fn()}
|
||||
resizeNotifier={new ResizeNotifier()}
|
||||
ref={(ref) => (filePanel = ref)}
|
||||
/>,
|
||||
);
|
||||
await screen.findByText("No files visible in this room");
|
||||
|
||||
const event = mkEvent({
|
||||
type: "m.room.message",
|
||||
user: cli.getSafeUserId(),
|
||||
room: room.roomId,
|
||||
content: {
|
||||
body: "hello",
|
||||
url: "mxc://matrix.org/1234",
|
||||
msgtype: "m.file",
|
||||
},
|
||||
event: true,
|
||||
});
|
||||
filePanel!.addEncryptedLiveEvent(event);
|
||||
|
||||
expect(timelineSet.getLiveTimeline().getEvents()).toContain(event);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -44,7 +44,6 @@ import {
|
||||
} from "../../../test-utils";
|
||||
import * as leaveRoomUtils from "../../../../src/utils/leave-behaviour";
|
||||
import { OidcClientError } from "../../../../src/utils/oidc/error";
|
||||
import * as voiceBroadcastUtils from "../../../../src/voice-broadcast/utils/cleanUpBroadcasts";
|
||||
import LegacyCallHandler from "../../../../src/LegacyCallHandler";
|
||||
import { CallStore } from "../../../../src/stores/CallStore";
|
||||
import { Call } from "../../../../src/models/Call";
|
||||
@@ -811,7 +810,6 @@ describe("<MatrixChat />", () => {
|
||||
jest.spyOn(LegacyCallHandler.instance, "hangupAllCalls")
|
||||
.mockClear()
|
||||
.mockImplementation(() => {});
|
||||
jest.spyOn(voiceBroadcastUtils, "cleanUpBroadcasts").mockImplementation(async () => {});
|
||||
jest.spyOn(PosthogAnalytics.instance, "logout").mockImplementation(() => {});
|
||||
jest.spyOn(EventIndexPeg, "deleteEventIndex").mockImplementation(async () => {});
|
||||
|
||||
@@ -831,22 +829,12 @@ describe("<MatrixChat />", () => {
|
||||
jest.spyOn(logger, "warn").mockClear();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.spyOn(voiceBroadcastUtils, "cleanUpBroadcasts").mockRestore();
|
||||
});
|
||||
|
||||
it("should hangup all legacy calls", async () => {
|
||||
await getComponentAndWaitForReady();
|
||||
await dispatchLogoutAndWait();
|
||||
expect(LegacyCallHandler.instance.hangupAllCalls).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should cleanup broadcasts", async () => {
|
||||
await getComponentAndWaitForReady();
|
||||
await dispatchLogoutAndWait();
|
||||
expect(voiceBroadcastUtils.cleanUpBroadcasts).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should disconnect all calls", async () => {
|
||||
await getComponentAndWaitForReady();
|
||||
await dispatchLogoutAndWait();
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
import ResizeNotifier from "../../../../src/utils/ResizeNotifier";
|
||||
import { IRoomState } from "../../../../src/components/structures/RoomView";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import { ScopedRoomContextProvider } from "../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
jest.mock("../../../../src/utils/beacon", () => ({
|
||||
useBeacon: jest.fn(),
|
||||
@@ -91,9 +92,9 @@ describe("MessagePanel", function () {
|
||||
|
||||
const getComponent = (props = {}, roomContext: Partial<IRoomState> = {}) => (
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<RoomContext.Provider value={{ ...defaultRoomContext, ...roomContext }}>
|
||||
<ScopedRoomContextProvider {...defaultRoomContext} {...roomContext}>
|
||||
<MessagePanel {...defaultProps} {...props} />
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>
|
||||
);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import React from "react";
|
||||
import { mocked, Mocked } from "jest-mock";
|
||||
import { screen, render, act, cleanup } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { MatrixClient, PendingEventOrdering, Room, MatrixEvent, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { MatrixClient, PendingEventOrdering, Room, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { Widget, ClientWidgetApi } from "matrix-widget-api";
|
||||
import { UserEvent } from "@testing-library/user-event/dist/types/setup/setup";
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
wrapInSdkContext,
|
||||
mkRoomCreateEvent,
|
||||
mockPlatformPeg,
|
||||
flushPromises,
|
||||
useMockMediaDevices,
|
||||
} from "../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
@@ -39,17 +38,7 @@ import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../src/dispatcher/actions";
|
||||
import { ViewRoomPayload } from "../../../../src/dispatcher/payloads/ViewRoomPayload";
|
||||
import { TestSdkContext } from "../../TestSdkContext";
|
||||
import {
|
||||
VoiceBroadcastInfoState,
|
||||
VoiceBroadcastPlaybacksStore,
|
||||
VoiceBroadcastPreRecording,
|
||||
VoiceBroadcastPreRecordingStore,
|
||||
VoiceBroadcastRecording,
|
||||
VoiceBroadcastRecordingsStore,
|
||||
} from "../../../../src/voice-broadcast";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../../voice-broadcast/utils/test-utils";
|
||||
import { RoomViewStore } from "../../../../src/stores/RoomViewStore";
|
||||
import { IRoomStateEventsActionPayload } from "../../../../src/actions/MatrixActionCreators";
|
||||
import { Container, WidgetLayoutStore } from "../../../../src/stores/widgets/WidgetLayoutStore";
|
||||
import WidgetStore from "../../../../src/stores/WidgetStore";
|
||||
import { WidgetType } from "../../../../src/widgets/WidgetType";
|
||||
@@ -76,13 +65,6 @@ describe("PipContainer", () => {
|
||||
let room: Room;
|
||||
let room2: Room;
|
||||
let alice: RoomMember;
|
||||
let voiceBroadcastRecordingsStore: VoiceBroadcastRecordingsStore;
|
||||
let voiceBroadcastPreRecordingStore: VoiceBroadcastPreRecordingStore;
|
||||
let voiceBroadcastPlaybacksStore: VoiceBroadcastPlaybacksStore;
|
||||
|
||||
const actFlushPromises = async () => {
|
||||
await flushPromises();
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
useMockMediaDevices();
|
||||
@@ -125,13 +107,7 @@ describe("PipContainer", () => {
|
||||
sdkContext = new TestSdkContext();
|
||||
// @ts-ignore PipContainer uses SDKContext in the constructor
|
||||
SdkContextClass.instance = sdkContext;
|
||||
voiceBroadcastRecordingsStore = new VoiceBroadcastRecordingsStore();
|
||||
voiceBroadcastPreRecordingStore = new VoiceBroadcastPreRecordingStore();
|
||||
voiceBroadcastPlaybacksStore = new VoiceBroadcastPlaybacksStore(voiceBroadcastRecordingsStore);
|
||||
sdkContext.client = client;
|
||||
sdkContext._VoiceBroadcastRecordingsStore = voiceBroadcastRecordingsStore;
|
||||
sdkContext._VoiceBroadcastPreRecordingStore = voiceBroadcastPreRecordingStore;
|
||||
sdkContext._VoiceBroadcastPlaybacksStore = voiceBroadcastPlaybacksStore;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -190,51 +166,10 @@ describe("PipContainer", () => {
|
||||
ActiveWidgetStore.instance.destroyPersistentWidget("1", room.roomId);
|
||||
};
|
||||
|
||||
const makeVoiceBroadcastInfoStateEvent = (): MatrixEvent => {
|
||||
return mkVoiceBroadcastInfoStateEvent(
|
||||
room.roomId,
|
||||
VoiceBroadcastInfoState.Started,
|
||||
alice.userId,
|
||||
client.getDeviceId() || "",
|
||||
);
|
||||
};
|
||||
|
||||
const setUpVoiceBroadcastRecording = () => {
|
||||
const infoEvent = makeVoiceBroadcastInfoStateEvent();
|
||||
const voiceBroadcastRecording = new VoiceBroadcastRecording(infoEvent, client);
|
||||
voiceBroadcastRecordingsStore.setCurrent(voiceBroadcastRecording);
|
||||
};
|
||||
|
||||
const setUpVoiceBroadcastPreRecording = () => {
|
||||
const voiceBroadcastPreRecording = new VoiceBroadcastPreRecording(
|
||||
room,
|
||||
alice,
|
||||
client,
|
||||
voiceBroadcastPlaybacksStore,
|
||||
voiceBroadcastRecordingsStore,
|
||||
);
|
||||
voiceBroadcastPreRecordingStore.setCurrent(voiceBroadcastPreRecording);
|
||||
};
|
||||
|
||||
const setUpRoomViewStore = () => {
|
||||
sdkContext._RoomViewStore = new RoomViewStore(defaultDispatcher, sdkContext);
|
||||
};
|
||||
|
||||
const mkVoiceBroadcast = (room: Room): MatrixEvent => {
|
||||
const infoEvent = makeVoiceBroadcastInfoStateEvent();
|
||||
room.currentState.setStateEvents([infoEvent]);
|
||||
defaultDispatcher.dispatch<IRoomStateEventsActionPayload>(
|
||||
{
|
||||
action: "MatrixActions.RoomState.events",
|
||||
event: infoEvent,
|
||||
state: room.currentState,
|
||||
lastStateEvent: null,
|
||||
},
|
||||
true,
|
||||
);
|
||||
return infoEvent;
|
||||
};
|
||||
|
||||
it("hides if there's no content", () => {
|
||||
renderPip();
|
||||
expect(screen.queryByRole("complementary")).toBeNull();
|
||||
@@ -339,138 +274,4 @@ describe("PipContainer", () => {
|
||||
|
||||
WidgetStore.instance.removeVirtualWidget("1", room.roomId);
|
||||
});
|
||||
|
||||
describe("when there is a voice broadcast recording and pre-recording", () => {
|
||||
beforeEach(async () => {
|
||||
setUpVoiceBroadcastPreRecording();
|
||||
setUpVoiceBroadcastRecording();
|
||||
renderPip();
|
||||
await actFlushPromises();
|
||||
});
|
||||
|
||||
it("should render the voice broadcast recording PiP", () => {
|
||||
// check for the „Live“ badge to be present
|
||||
expect(screen.queryByText("Live")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("and a call it should show both, the call and the recording", async () => {
|
||||
await withCall(async () => {
|
||||
// Broadcast: Check for the „Live“ badge to be present
|
||||
expect(screen.queryByText("Live")).toBeInTheDocument();
|
||||
// Call: Check for the „Leave“ button to be present
|
||||
screen.getByRole("button", { name: "Leave" });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when there is a voice broadcast playback and pre-recording", () => {
|
||||
beforeEach(async () => {
|
||||
mkVoiceBroadcast(room);
|
||||
setUpVoiceBroadcastPreRecording();
|
||||
renderPip();
|
||||
await actFlushPromises();
|
||||
});
|
||||
|
||||
it("should render the voice broadcast pre-recording PiP", () => {
|
||||
// check for the „Go live“ button
|
||||
expect(screen.queryByText("Go live")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when there is a voice broadcast pre-recording", () => {
|
||||
beforeEach(async () => {
|
||||
setUpVoiceBroadcastPreRecording();
|
||||
renderPip();
|
||||
await actFlushPromises();
|
||||
});
|
||||
|
||||
it("should render the voice broadcast pre-recording PiP", () => {
|
||||
// check for the „Go live“ button
|
||||
expect(screen.queryByText("Go live")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when listening to a voice broadcast in a room and then switching to another room", () => {
|
||||
beforeEach(async () => {
|
||||
setUpRoomViewStore();
|
||||
viewRoom(room.roomId);
|
||||
mkVoiceBroadcast(room);
|
||||
await actFlushPromises();
|
||||
|
||||
expect(voiceBroadcastPlaybacksStore.getCurrent()).toBeTruthy();
|
||||
|
||||
await voiceBroadcastPlaybacksStore.getCurrent()?.start();
|
||||
viewRoom(room2.roomId);
|
||||
renderPip();
|
||||
});
|
||||
|
||||
it("should render the small voice broadcast playback PiP", () => {
|
||||
// check for the „pause voice broadcast“ button
|
||||
expect(screen.getByLabelText("pause voice broadcast")).toBeInTheDocument();
|
||||
// check for the absence of the „30s forward“ button
|
||||
expect(screen.queryByLabelText("30s forward")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when viewing a room with a live voice broadcast", () => {
|
||||
let startEvent!: MatrixEvent;
|
||||
|
||||
beforeEach(async () => {
|
||||
setUpRoomViewStore();
|
||||
viewRoom(room.roomId);
|
||||
startEvent = mkVoiceBroadcast(room);
|
||||
renderPip();
|
||||
await actFlushPromises();
|
||||
});
|
||||
|
||||
it("should render the voice broadcast playback pip", () => {
|
||||
// check for the „resume voice broadcast“ button
|
||||
expect(screen.queryByLabelText("play voice broadcast")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe("and the broadcast stops", () => {
|
||||
beforeEach(async () => {
|
||||
const stopEvent = mkVoiceBroadcastInfoStateEvent(
|
||||
room.roomId,
|
||||
VoiceBroadcastInfoState.Stopped,
|
||||
alice.userId,
|
||||
client.getDeviceId() || "",
|
||||
startEvent,
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
room.currentState.setStateEvents([stopEvent]);
|
||||
defaultDispatcher.dispatch<IRoomStateEventsActionPayload>(
|
||||
{
|
||||
action: "MatrixActions.RoomState.events",
|
||||
event: stopEvent,
|
||||
state: room.currentState,
|
||||
lastStateEvent: stopEvent,
|
||||
},
|
||||
true,
|
||||
);
|
||||
await flushPromises();
|
||||
});
|
||||
});
|
||||
|
||||
it("should not render the voice broadcast playback pip", () => {
|
||||
// check for the „resume voice broadcast“ button
|
||||
expect(screen.queryByLabelText("play voice broadcast")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("and leaving the room", () => {
|
||||
beforeEach(async () => {
|
||||
await act(async () => {
|
||||
viewRoom(room2.roomId);
|
||||
await flushPromises();
|
||||
});
|
||||
});
|
||||
|
||||
it("should not render the voice broadcast playback pip", () => {
|
||||
// check for the „resume voice broadcast“ button
|
||||
expect(screen.queryByLabelText("play voice broadcast")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,7 +91,7 @@ describe("RightPanel", () => {
|
||||
if (name !== "RightPanel.phases") return realGetValue(name, roomId);
|
||||
if (roomId === "r1") {
|
||||
return {
|
||||
history: [{ phase: RightPanelPhases.RoomMemberList }],
|
||||
history: [{ phase: RightPanelPhases.MemberList }],
|
||||
isOpen: true,
|
||||
};
|
||||
}
|
||||
@@ -123,7 +123,7 @@ describe("RightPanel", () => {
|
||||
await rpsUpdated;
|
||||
await waitFor(() => expect(screen.queryByTestId("spinner")).not.toBeInTheDocument());
|
||||
|
||||
// room one will be in the RoomMemberList phase - confirm this is rendered
|
||||
// room one will be in the MemberList phase - confirm this is rendered
|
||||
expect(container.getElementsByClassName("mx_MemberList")).toHaveLength(1);
|
||||
|
||||
// wait for RPS room 2 updates to fire, then rerender
|
||||
|
||||
@@ -10,18 +10,19 @@ import React, { createRef, RefObject } from "react";
|
||||
import { mocked, MockedObject } from "jest-mock";
|
||||
import {
|
||||
ClientEvent,
|
||||
EventTimeline,
|
||||
EventType,
|
||||
IEvent,
|
||||
JoinRule,
|
||||
MatrixClient,
|
||||
MatrixError,
|
||||
MatrixEvent,
|
||||
Room,
|
||||
RoomEvent,
|
||||
EventType,
|
||||
JoinRule,
|
||||
MatrixError,
|
||||
RoomStateEvent,
|
||||
MatrixEvent,
|
||||
SearchResult,
|
||||
IEvent,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { CryptoApi, UserVerificationStatus } from "matrix-js-sdk/src/crypto-api";
|
||||
import { CryptoApi, UserVerificationStatus, CryptoEvent } from "matrix-js-sdk/src/crypto-api";
|
||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||
import {
|
||||
fireEvent,
|
||||
@@ -34,6 +35,7 @@ import {
|
||||
cleanup,
|
||||
} from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { defer } from "matrix-js-sdk/src/utils";
|
||||
|
||||
import {
|
||||
stubClient,
|
||||
@@ -73,6 +75,7 @@ import { ViewRoomErrorPayload } from "../../../../src/dispatcher/payloads/ViewRo
|
||||
import { SearchScope } from "../../../../src/Searching";
|
||||
import { MEGOLM_ENCRYPTION_ALGORITHM } from "../../../../src/utils/crypto";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import { ViewUserPayload } from "../../../../src/dispatcher/payloads/ViewUserPayload.ts";
|
||||
|
||||
describe("RoomView", () => {
|
||||
let cli: MockedObject<MatrixClient>;
|
||||
@@ -87,8 +90,7 @@ describe("RoomView", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
mockPlatformPeg({ reload: () => {} });
|
||||
stubClient();
|
||||
cli = mocked(MatrixClientPeg.safeGet());
|
||||
cli = mocked(stubClient());
|
||||
|
||||
room = new Room(`!${roomCount++}:example.org`, cli, "@alice:example.org");
|
||||
jest.spyOn(room, "findPredecessor");
|
||||
@@ -201,6 +203,21 @@ describe("RoomView", () => {
|
||||
return ref.current!;
|
||||
};
|
||||
|
||||
it("should show member list right panel phase on Action.ViewUser without `payload.member`", async () => {
|
||||
const spy = jest.spyOn(stores.rightPanelStore, "showOrHidePhase");
|
||||
await renderRoomView(false);
|
||||
|
||||
defaultDispatcher.dispatch<ViewUserPayload>(
|
||||
{
|
||||
action: Action.ViewUser,
|
||||
member: undefined,
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(RightPanelPhases.MemberList);
|
||||
});
|
||||
|
||||
it("when there is no room predecessor, getHiddenHighlightCount should return 0", async () => {
|
||||
const instance = await getRoomViewInstance();
|
||||
expect(instance.getHiddenHighlightCount()).toBe(0);
|
||||
@@ -247,8 +264,9 @@ describe("RoomView", () => {
|
||||
|
||||
it("updates url preview visibility on encryption state change", async () => {
|
||||
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join);
|
||||
jest.spyOn(cli, "getCrypto").mockReturnValue(crypto);
|
||||
// we should be starting unencrypted
|
||||
expect(cli.isRoomEncrypted(room.roomId)).toEqual(false);
|
||||
expect(await cli.getCrypto()?.isEncryptionEnabledInRoom(room.roomId)).toEqual(false);
|
||||
|
||||
const roomViewInstance = await getRoomViewInstance();
|
||||
|
||||
@@ -263,23 +281,38 @@ describe("RoomView", () => {
|
||||
expect(roomViewInstance.state.showUrlPreview).toBe(true);
|
||||
|
||||
// now enable encryption
|
||||
cli.isRoomEncrypted.mockReturnValue(true);
|
||||
jest.spyOn(cli.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
|
||||
|
||||
// and fake an encryption event into the room to prompt it to re-check
|
||||
await act(() =>
|
||||
room.addLiveEvents([
|
||||
new MatrixEvent({
|
||||
type: "m.room.encryption",
|
||||
sender: cli.getUserId()!,
|
||||
content: {},
|
||||
event_id: "someid",
|
||||
room_id: room.roomId,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
act(() => {
|
||||
const encryptionEvent = new MatrixEvent({
|
||||
type: EventType.RoomEncryption,
|
||||
sender: cli.getUserId()!,
|
||||
content: {},
|
||||
event_id: "someid",
|
||||
room_id: room.roomId,
|
||||
});
|
||||
const roomState = room.getLiveTimeline().getState(EventTimeline.FORWARDS)!;
|
||||
cli.emit(RoomStateEvent.Events, encryptionEvent, roomState, null);
|
||||
});
|
||||
|
||||
// URL previews should now be disabled
|
||||
expect(roomViewInstance.state.showUrlPreview).toBe(false);
|
||||
await waitFor(() => expect(roomViewInstance.state.showUrlPreview).toBe(false));
|
||||
});
|
||||
|
||||
it("should not display the timeline when the room encryption is loading", async () => {
|
||||
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Join);
|
||||
jest.spyOn(cli, "getCrypto").mockReturnValue(crypto);
|
||||
const deferred = defer<boolean>();
|
||||
jest.spyOn(cli.getCrypto()!, "isEncryptionEnabledInRoom").mockImplementation(() => deferred.promise);
|
||||
|
||||
const { asFragment, container } = await mountRoomView();
|
||||
expect(container.querySelector(".mx_RoomView_messagePanel")).toBeNull();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
|
||||
deferred.resolve(true);
|
||||
await waitFor(() => expect(container.querySelector(".mx_RoomView_messagePanel")).not.toBeNull());
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("updates live timeline when a timeline reset happens", async () => {
|
||||
@@ -290,6 +323,32 @@ describe("RoomView", () => {
|
||||
expect(roomViewInstance.state.liveTimeline).not.toEqual(oldTimeline);
|
||||
});
|
||||
|
||||
it("should update when the e2e status when the user verification changed", async () => {
|
||||
room.currentState.setStateEvents([
|
||||
mkRoomMemberJoinEvent(cli.getSafeUserId(), room.roomId),
|
||||
mkRoomMemberJoinEvent("user@example.com", room.roomId),
|
||||
]);
|
||||
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join);
|
||||
// Not all the calls to cli.isRoomEncrypted are migrated, so we need to mock both.
|
||||
mocked(cli.isRoomEncrypted).mockReturnValue(true);
|
||||
jest.spyOn(cli, "getCrypto").mockReturnValue(crypto);
|
||||
jest.spyOn(cli.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
|
||||
jest.spyOn(cli.getCrypto()!, "getUserVerificationStatus").mockResolvedValue(
|
||||
new UserVerificationStatus(false, false, false),
|
||||
);
|
||||
jest.spyOn(cli.getCrypto()!, "getUserDeviceInfo").mockResolvedValue(
|
||||
new Map([["user@example.com", new Map<string, any>()]]),
|
||||
);
|
||||
|
||||
const { container } = await renderRoomView();
|
||||
await waitFor(() => expect(container.querySelector(".mx_E2EIcon_normal")).toBeInTheDocument());
|
||||
|
||||
const verificationStatus = new UserVerificationStatus(true, true, false);
|
||||
jest.spyOn(cli.getCrypto()!, "getUserVerificationStatus").mockResolvedValue(verificationStatus);
|
||||
cli.emit(CryptoEvent.UserTrustStatusChanged, cli.getSafeUserId(), verificationStatus);
|
||||
await waitFor(() => expect(container.querySelector(".mx_E2EIcon_verified")).toBeInTheDocument());
|
||||
});
|
||||
|
||||
describe("with virtual rooms", () => {
|
||||
it("checks for a virtual room on initial load", async () => {
|
||||
const { container } = await renderRoomView();
|
||||
@@ -427,7 +486,8 @@ describe("RoomView", () => {
|
||||
]);
|
||||
jest.spyOn(DMRoomMap.shared(), "getUserIdForRoomId").mockReturnValue(cli.getSafeUserId());
|
||||
jest.spyOn(DMRoomMap.shared(), "getRoomIds").mockReturnValue(new Set([room.roomId]));
|
||||
mocked(cli).isRoomEncrypted.mockReturnValue(true);
|
||||
jest.spyOn(cli, "getCrypto").mockReturnValue(crypto);
|
||||
jest.spyOn(cli.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
|
||||
await renderRoomView();
|
||||
});
|
||||
|
||||
@@ -653,7 +713,7 @@ describe("RoomView", () => {
|
||||
skey: id,
|
||||
ts,
|
||||
});
|
||||
room.addLiveEvents([widgetEvent]);
|
||||
room.addLiveEvents([widgetEvent], { addToState: false });
|
||||
room.currentState.setStateEvents([widgetEvent]);
|
||||
cli.emit(RoomStateEvent.Events, widgetEvent, room.currentState, null);
|
||||
await flushPromises();
|
||||
|
||||
117
test/unit-tests/components/structures/SpaceRoomView-test.tsx
Normal file
117
test/unit-tests/components/structures/SpaceRoomView-test.tsx
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
|
||||
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, MockedObject } from "jest-mock";
|
||||
import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { render, cleanup, screen, fireEvent } from "jest-matrix-react";
|
||||
|
||||
import { stubClient, mockPlatformPeg, unmockPlatformPeg, withClientContextRenderOptions } from "../../../test-utils";
|
||||
import { RightPanelPhases } from "../../../../src/stores/right-panel/RightPanelStorePhases";
|
||||
import SpaceRoomView from "../../../../src/components/structures/SpaceRoomView.tsx";
|
||||
import ResizeNotifier from "../../../../src/utils/ResizeNotifier.ts";
|
||||
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks.ts";
|
||||
import RightPanelStore from "../../../../src/stores/right-panel/RightPanelStore.ts";
|
||||
import DMRoomMap from "../../../../src/utils/DMRoomMap.ts";
|
||||
|
||||
describe("SpaceRoomView", () => {
|
||||
let cli: MockedObject<MatrixClient>;
|
||||
let space: Room;
|
||||
|
||||
beforeEach(() => {
|
||||
mockPlatformPeg({ reload: () => {} });
|
||||
cli = mocked(stubClient());
|
||||
|
||||
space = new Room(`!space:example.org`, cli, cli.getSafeUserId());
|
||||
space.currentState.setStateEvents([
|
||||
new MatrixEvent({
|
||||
type: "m.room.create",
|
||||
room_id: space.roomId,
|
||||
sender: cli.getSafeUserId(),
|
||||
state_key: "",
|
||||
content: {
|
||||
creator: cli.getSafeUserId(),
|
||||
type: "m.space",
|
||||
},
|
||||
}),
|
||||
new MatrixEvent({
|
||||
type: "m.room.member",
|
||||
room_id: space.roomId,
|
||||
sender: cli.getSafeUserId(),
|
||||
state_key: cli.getSafeUserId(),
|
||||
content: {
|
||||
membership: "join",
|
||||
},
|
||||
}),
|
||||
new MatrixEvent({
|
||||
type: "m.room.member",
|
||||
room_id: space.roomId,
|
||||
sender: "@userA:server",
|
||||
state_key: "@userA:server",
|
||||
content: {
|
||||
membership: "join",
|
||||
},
|
||||
}),
|
||||
new MatrixEvent({
|
||||
type: "m.room.member",
|
||||
room_id: space.roomId,
|
||||
sender: "@userB:server",
|
||||
state_key: "@userB:server",
|
||||
content: {
|
||||
membership: "join",
|
||||
},
|
||||
}),
|
||||
new MatrixEvent({
|
||||
type: "m.room.member",
|
||||
room_id: space.roomId,
|
||||
sender: "@userC:server",
|
||||
state_key: "@userC:server",
|
||||
content: {
|
||||
membership: "join",
|
||||
},
|
||||
}),
|
||||
]);
|
||||
space.updateMyMembership("join");
|
||||
|
||||
DMRoomMap.makeShared(cli);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
unmockPlatformPeg();
|
||||
jest.clearAllMocks();
|
||||
cleanup();
|
||||
});
|
||||
|
||||
const renderSpaceRoomView = async (): Promise<ReturnType<typeof render>> => {
|
||||
const resizeNotifier = new ResizeNotifier();
|
||||
const permalinkCreator = new RoomPermalinkCreator(space);
|
||||
|
||||
const spaceRoomView = render(
|
||||
<SpaceRoomView
|
||||
space={space}
|
||||
resizeNotifier={resizeNotifier}
|
||||
permalinkCreator={permalinkCreator}
|
||||
onJoinButtonClicked={jest.fn()}
|
||||
onRejectButtonClicked={jest.fn()}
|
||||
/>,
|
||||
withClientContextRenderOptions(cli),
|
||||
);
|
||||
return spaceRoomView;
|
||||
};
|
||||
|
||||
describe("SpaceLanding", () => {
|
||||
it("should show member list right panel phase on members click on landing", async () => {
|
||||
const spy = jest.spyOn(RightPanelStore.instance, "setCard");
|
||||
const { container } = await renderSpaceRoomView();
|
||||
|
||||
await expect(screen.findByText("Welcome to")).resolves.toBeVisible();
|
||||
fireEvent.click(container.querySelector(".mx_FacePile")!);
|
||||
|
||||
expect(spy).toHaveBeenCalledWith({ phase: RightPanelPhases.MemberList });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
|
||||
import ThreadPanel, { ThreadFilterType, ThreadPanelHeader } from "../../../../src/components/structures/ThreadPanel";
|
||||
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
|
||||
import RoomContext from "../../../../src/contexts/RoomContext";
|
||||
import { _t } from "../../../../src/languageHandler";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
|
||||
@@ -28,6 +27,7 @@ import ResizeNotifier from "../../../../src/utils/ResizeNotifier";
|
||||
import { createTestClient, getRoomContext, mkRoom, mockPlatformPeg, stubClient } from "../../../test-utils";
|
||||
import { mkThread } from "../../../test-utils/threads";
|
||||
import { IRoomState } from "../../../../src/components/structures/RoomView";
|
||||
import { ScopedRoomContextProvider } from "../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
jest.mock("../../../../src/utils/Feedback");
|
||||
|
||||
@@ -81,11 +81,11 @@ describe("ThreadPanel", () => {
|
||||
room: mockRoom,
|
||||
} as unknown as IRoomState;
|
||||
const { container } = render(
|
||||
<RoomContext.Provider value={roomContextObject}>
|
||||
<ScopedRoomContextProvider {...roomContextObject}>
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<ThreadPanelHeader filterOption={ThreadFilterType.All} setFilterOption={() => undefined} />
|
||||
</MatrixClientContext.Provider>
|
||||
</RoomContext.Provider>,
|
||||
</ScopedRoomContextProvider>,
|
||||
);
|
||||
fireEvent.click(getByRole(container, "button", { name: "Mark all as read" }));
|
||||
await waitFor(() =>
|
||||
@@ -114,8 +114,8 @@ describe("ThreadPanel", () => {
|
||||
|
||||
const TestThreadPanel = () => (
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider
|
||||
value={getRoomContext(room, {
|
||||
<ScopedRoomContextProvider
|
||||
{...getRoomContext(room, {
|
||||
canSendMessages: true,
|
||||
})}
|
||||
>
|
||||
@@ -125,7 +125,7 @@ describe("ThreadPanel", () => {
|
||||
resizeNotifier={new ResizeNotifier()}
|
||||
permalinkCreator={new RoomPermalinkCreator(room)}
|
||||
/>
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>
|
||||
);
|
||||
|
||||
@@ -209,11 +209,11 @@ describe("ThreadPanel", () => {
|
||||
return event ? Promise.resolve(event) : Promise.reject();
|
||||
});
|
||||
const [allThreads, myThreads] = room.threadsTimelineSets;
|
||||
allThreads!.addLiveEvent(otherThread.rootEvent);
|
||||
allThreads!.addLiveEvent(mixedThread.rootEvent);
|
||||
allThreads!.addLiveEvent(ownThread.rootEvent);
|
||||
myThreads!.addLiveEvent(mixedThread.rootEvent);
|
||||
myThreads!.addLiveEvent(ownThread.rootEvent);
|
||||
allThreads!.addLiveEvent(otherThread.rootEvent, { addToState: true });
|
||||
allThreads!.addLiveEvent(mixedThread.rootEvent, { addToState: true });
|
||||
allThreads!.addLiveEvent(ownThread.rootEvent, { addToState: true });
|
||||
myThreads!.addLiveEvent(mixedThread.rootEvent, { addToState: true });
|
||||
myThreads!.addLiveEvent(ownThread.rootEvent, { addToState: true });
|
||||
|
||||
const renderResult = render(<TestThreadPanel />);
|
||||
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
||||
@@ -258,7 +258,7 @@ describe("ThreadPanel", () => {
|
||||
return event ? Promise.resolve(event) : Promise.reject();
|
||||
});
|
||||
const [allThreads] = room.threadsTimelineSets;
|
||||
allThreads!.addLiveEvent(otherThread.rootEvent);
|
||||
allThreads!.addLiveEvent(otherThread.rootEvent, { addToState: true });
|
||||
|
||||
const renderResult = render(<TestThreadPanel />);
|
||||
await waitFor(() => expect(renderResult.container.querySelector(".mx_AutoHideScrollbar")).toBeFalsy());
|
||||
|
||||
@@ -23,7 +23,6 @@ import React, { useState } from "react";
|
||||
|
||||
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 { Action } from "../../../../src/dispatcher/actions";
|
||||
import dispatcher from "../../../../src/dispatcher/dispatcher";
|
||||
@@ -34,6 +33,7 @@ import { mockPlatformPeg } from "../../../test-utils/platform";
|
||||
import { getRoomContext } from "../../../test-utils/room";
|
||||
import { mkMessage, stubClient } from "../../../test-utils/test-utils";
|
||||
import { mkThread } from "../../../test-utils/threads";
|
||||
import { ScopedRoomContextProvider } from "../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
describe("ThreadView", () => {
|
||||
const ROOM_ID = "!roomId:example.org";
|
||||
@@ -51,8 +51,8 @@ describe("ThreadView", () => {
|
||||
|
||||
return (
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider
|
||||
value={getRoomContext(room, {
|
||||
<ScopedRoomContextProvider
|
||||
{...getRoomContext(room, {
|
||||
canSendMessages: true,
|
||||
})}
|
||||
>
|
||||
@@ -63,7 +63,7 @@ describe("ThreadView", () => {
|
||||
initialEvent={initialEvent}
|
||||
resizeNotifier={new ResizeNotifier()}
|
||||
/>
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
,
|
||||
</MatrixClientContext.Provider>
|
||||
);
|
||||
|
||||
@@ -66,7 +66,7 @@ const mkTimeline = (room: Room, events: MatrixEvent[]): [EventTimeline, EventTim
|
||||
getPendingEvents: () => [] as MatrixEvent[],
|
||||
} as unknown as EventTimelineSet;
|
||||
const timeline = new EventTimeline(timelineSet);
|
||||
events.forEach((event) => timeline.addEvent(event, { toStartOfTimeline: false }));
|
||||
events.forEach((event) => timeline.addEvent(event, { toStartOfTimeline: false, addToState: true }));
|
||||
|
||||
return [timeline, timelineSet];
|
||||
};
|
||||
@@ -150,9 +150,11 @@ const setupPagination = (
|
||||
mocked(client).paginateEventTimeline.mockImplementation(async (tl, { backwards }) => {
|
||||
if (tl === timeline) {
|
||||
if (backwards) {
|
||||
forEachRight(previousPage ?? [], (event) => tl.addEvent(event, { toStartOfTimeline: true }));
|
||||
forEachRight(previousPage ?? [], (event) =>
|
||||
tl.addEvent(event, { toStartOfTimeline: true, addToState: true }),
|
||||
);
|
||||
} else {
|
||||
(nextPage ?? []).forEach((event) => tl.addEvent(event, { toStartOfTimeline: false }));
|
||||
(nextPage ?? []).forEach((event) => tl.addEvent(event, { toStartOfTimeline: false, addToState: true }));
|
||||
}
|
||||
// Prevent any further pagination attempts in this direction
|
||||
tl.setPaginationToken(null, backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS);
|
||||
@@ -256,7 +258,7 @@ describe("TimelinePanel", () => {
|
||||
describe("and reading the timeline", () => {
|
||||
beforeEach(async () => {
|
||||
await renderTimelinePanel();
|
||||
timelineSet.addLiveEvent(ev1, {});
|
||||
timelineSet.addLiveEvent(ev1, { addToState: true });
|
||||
await flushPromises();
|
||||
// @ts-ignore
|
||||
await timelinePanel.sendReadReceipts();
|
||||
@@ -284,11 +286,11 @@ describe("TimelinePanel", () => {
|
||||
});
|
||||
|
||||
it("and forgetting the read markers, should send the stored marker again", async () => {
|
||||
timelineSet.addLiveEvent(ev2, {});
|
||||
timelineSet.addLiveEvent(ev2, { addToState: true });
|
||||
// Add the event to the room as well as the timeline, so we can find it when we
|
||||
// call findEventById in getEventReadUpTo. This is odd because in our test
|
||||
// setup, timelineSet is not actually the timelineSet of the room.
|
||||
await room.addLiveEvents([ev2], {});
|
||||
await room.addLiveEvents([ev2], { addToState: true });
|
||||
room.addEphemeralEvents([newReceipt(ev2.getId()!, userId, 222, 200)]);
|
||||
await timelinePanel!.forgetReadMarker();
|
||||
expect(client.setRoomReadMarkers).toHaveBeenCalledWith(roomId, ev2.getId());
|
||||
@@ -314,7 +316,7 @@ describe("TimelinePanel", () => {
|
||||
|
||||
it("should send a fully read marker and a private receipt", async () => {
|
||||
await renderTimelinePanel();
|
||||
act(() => timelineSet.addLiveEvent(ev1, {}));
|
||||
act(() => timelineSet.addLiveEvent(ev1, { addToState: true }));
|
||||
await flushPromises();
|
||||
|
||||
// @ts-ignore
|
||||
@@ -361,7 +363,7 @@ describe("TimelinePanel", () => {
|
||||
|
||||
it("should send receipts but no fully_read when reading the thread timeline", async () => {
|
||||
await renderTimelinePanel();
|
||||
act(() => timelineSet.addLiveEvent(threadEv1, {}));
|
||||
act(() => timelineSet.addLiveEvent(threadEv1, { addToState: true }));
|
||||
await flushPromises();
|
||||
|
||||
// @ts-ignore
|
||||
@@ -871,7 +873,9 @@ describe("TimelinePanel", () => {
|
||||
// @ts-ignore
|
||||
thread.fetchEditsWhereNeeded = () => Promise.resolve();
|
||||
await thread.addEvent(reply1, false, true);
|
||||
await allThreads.getLiveTimeline().addEvent(thread.rootEvent!, { toStartOfTimeline: true });
|
||||
await allThreads
|
||||
.getLiveTimeline()
|
||||
.addEvent(thread.rootEvent!, { toStartOfTimeline: true, addToState: true });
|
||||
const replyToEvent = jest.spyOn(thread, "replyToEvent", "get");
|
||||
|
||||
const dom = render(
|
||||
@@ -907,7 +911,9 @@ describe("TimelinePanel", () => {
|
||||
// @ts-ignore
|
||||
realThread.fetchEditsWhereNeeded = () => Promise.resolve();
|
||||
await realThread.addEvent(reply1, true);
|
||||
await allThreads.getLiveTimeline().addEvent(realThread.rootEvent!, { toStartOfTimeline: true });
|
||||
await allThreads
|
||||
.getLiveTimeline()
|
||||
.addEvent(realThread.rootEvent!, { toStartOfTimeline: true, addToState: true });
|
||||
const replyToEvent = jest.spyOn(realThread, "replyToEvent", "get");
|
||||
|
||||
// @ts-ignore
|
||||
@@ -968,7 +974,9 @@ describe("TimelinePanel", () => {
|
||||
|
||||
events.push(rootEvent);
|
||||
|
||||
events.forEach((event) => timelineSet.getLiveTimeline().addEvent(event, { toStartOfTimeline: true }));
|
||||
events.forEach((event) =>
|
||||
timelineSet.getLiveTimeline().addEvent(event, { toStartOfTimeline: true, addToState: true }),
|
||||
);
|
||||
|
||||
const roomMembership = mkMembership({
|
||||
mship: KnownMembership.Join,
|
||||
@@ -988,7 +996,10 @@ describe("TimelinePanel", () => {
|
||||
jest.spyOn(roomState, "getMember").mockReturnValue(member);
|
||||
|
||||
jest.spyOn(timelineSet.getLiveTimeline(), "getState").mockReturnValue(roomState);
|
||||
timelineSet.addEventToTimeline(roomMembership, timelineSet.getLiveTimeline(), { toStartOfTimeline: false });
|
||||
timelineSet.addEventToTimeline(roomMembership, timelineSet.getLiveTimeline(), {
|
||||
toStartOfTimeline: false,
|
||||
addToState: true,
|
||||
});
|
||||
|
||||
for (const event of events) {
|
||||
jest.spyOn(event, "isDecryptionFailure").mockReturnValue(true);
|
||||
|
||||
@@ -7,20 +7,14 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { act, render, RenderResult, screen, waitFor } from "jest-matrix-react";
|
||||
import { DEVICE_CODE_SCOPE, MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { render, screen, waitFor } from "jest-matrix-react";
|
||||
import { DEVICE_CODE_SCOPE, MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { CryptoApi } from "matrix-js-sdk/src/crypto-api";
|
||||
import { mocked } from "jest-mock";
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
|
||||
import UnwrappedUserMenu from "../../../../src/components/structures/UserMenu";
|
||||
import { stubClient, wrapInSdkContext } from "../../../test-utils";
|
||||
import {
|
||||
VoiceBroadcastInfoState,
|
||||
VoiceBroadcastRecording,
|
||||
VoiceBroadcastRecordingsStore,
|
||||
} from "../../../../src/voice-broadcast";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../../voice-broadcast/utils/test-utils";
|
||||
import { TestSdkContext } from "../../TestSdkContext";
|
||||
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
|
||||
import LogoutDialog from "../../../../src/components/views/dialogs/LogoutDialog";
|
||||
@@ -34,71 +28,12 @@ import { UserTab } from "../../../../src/components/views/dialogs/UserTab";
|
||||
|
||||
describe("<UserMenu>", () => {
|
||||
let client: MatrixClient;
|
||||
let renderResult: RenderResult;
|
||||
let sdkContext: TestSdkContext;
|
||||
|
||||
beforeEach(() => {
|
||||
sdkContext = new TestSdkContext();
|
||||
});
|
||||
|
||||
describe("<UserMenu> when video broadcast", () => {
|
||||
let voiceBroadcastInfoEvent: MatrixEvent;
|
||||
let voiceBroadcastRecording: VoiceBroadcastRecording;
|
||||
let voiceBroadcastRecordingsStore: VoiceBroadcastRecordingsStore;
|
||||
|
||||
beforeAll(() => {
|
||||
client = stubClient();
|
||||
voiceBroadcastInfoEvent = mkVoiceBroadcastInfoStateEvent(
|
||||
"!room:example.com",
|
||||
VoiceBroadcastInfoState.Started,
|
||||
client.getUserId() || "",
|
||||
client.getDeviceId() || "",
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
voiceBroadcastRecordingsStore = new VoiceBroadcastRecordingsStore();
|
||||
sdkContext._VoiceBroadcastRecordingsStore = voiceBroadcastRecordingsStore;
|
||||
|
||||
voiceBroadcastRecording = new VoiceBroadcastRecording(voiceBroadcastInfoEvent, client);
|
||||
});
|
||||
|
||||
describe("when rendered", () => {
|
||||
beforeEach(() => {
|
||||
const UserMenu = wrapInSdkContext(UnwrappedUserMenu, sdkContext);
|
||||
renderResult = render(<UserMenu isPanelCollapsed={true} />);
|
||||
});
|
||||
|
||||
it("should render as expected", () => {
|
||||
expect(renderResult.container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("and a live voice broadcast starts", () => {
|
||||
beforeEach(() => {
|
||||
act(() => {
|
||||
voiceBroadcastRecordingsStore.setCurrent(voiceBroadcastRecording);
|
||||
});
|
||||
});
|
||||
|
||||
it("should render the live voice broadcast avatar addon", () => {
|
||||
expect(renderResult.queryByTestId("user-menu-live-vb")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe("and the broadcast ends", () => {
|
||||
beforeEach(() => {
|
||||
act(() => {
|
||||
voiceBroadcastRecordingsStore.clearCurrent();
|
||||
});
|
||||
});
|
||||
|
||||
it("should not render the live voice broadcast avatar addon", () => {
|
||||
expect(renderResult.queryByTestId("user-menu-live-vb")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("<UserMenu> logout", () => {
|
||||
beforeEach(() => {
|
||||
client = stubClient();
|
||||
@@ -106,7 +41,7 @@ describe("<UserMenu>", () => {
|
||||
|
||||
it("should logout directly if no crypto", async () => {
|
||||
const UserMenu = wrapInSdkContext(UnwrappedUserMenu, sdkContext);
|
||||
renderResult = render(<UserMenu isPanelCollapsed={true} />);
|
||||
render(<UserMenu isPanelCollapsed={true} />);
|
||||
|
||||
mocked(client.getRooms).mockReturnValue([
|
||||
{
|
||||
@@ -128,7 +63,7 @@ describe("<UserMenu>", () => {
|
||||
|
||||
it("should logout directly if no encrypted rooms", async () => {
|
||||
const UserMenu = wrapInSdkContext(UnwrappedUserMenu, sdkContext);
|
||||
renderResult = render(<UserMenu isPanelCollapsed={true} />);
|
||||
render(<UserMenu isPanelCollapsed={true} />);
|
||||
|
||||
mocked(client.getRooms).mockReturnValue([
|
||||
{
|
||||
@@ -152,7 +87,7 @@ describe("<UserMenu>", () => {
|
||||
|
||||
it("should show dialog if some encrypted rooms", async () => {
|
||||
const UserMenu = wrapInSdkContext(UnwrappedUserMenu, sdkContext);
|
||||
renderResult = render(<UserMenu isPanelCollapsed={true} />);
|
||||
render(<UserMenu isPanelCollapsed={true} />);
|
||||
|
||||
mocked(client.getRooms).mockReturnValue([
|
||||
{
|
||||
|
||||
@@ -62,7 +62,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby=":rbc:"
|
||||
aria-labelledby=":rg4:"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -78,7 +78,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-label="Voice call"
|
||||
aria-labelledby=":rbh:"
|
||||
aria-labelledby=":rg9:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -103,7 +103,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
|
||||
</button>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
aria-labelledby=":rbm:"
|
||||
aria-labelledby=":rge:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -128,7 +128,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
|
||||
</button>
|
||||
<button
|
||||
aria-label="Threads"
|
||||
aria-labelledby=":rbr:"
|
||||
aria-labelledby=":rgj:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -157,7 +157,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
|
||||
>
|
||||
<div
|
||||
aria-label="2 members"
|
||||
aria-labelledby=":rc0:"
|
||||
aria-labelledby=":rgo:"
|
||||
class="mx_AccessibleButton mx_FacePile"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@@ -280,7 +280,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby=":rca:"
|
||||
aria-labelledby=":rh2:"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -296,7 +296,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-label="Voice call"
|
||||
aria-labelledby=":rcf:"
|
||||
aria-labelledby=":rh7:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -321,7 +321,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
||||
</button>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
aria-labelledby=":rck:"
|
||||
aria-labelledby=":rhc:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -346,7 +346,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
||||
</button>
|
||||
<button
|
||||
aria-label="Threads"
|
||||
aria-labelledby=":rcp:"
|
||||
aria-labelledby=":rhh:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -375,7 +375,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
|
||||
>
|
||||
<div
|
||||
aria-label="2 members"
|
||||
aria-labelledby=":rcu:"
|
||||
aria-labelledby=":rhm:"
|
||||
class="mx_AccessibleButton mx_FacePile"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@@ -583,7 +583,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby=":r70:"
|
||||
aria-labelledby=":rbo:"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -599,7 +599,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-label="Voice call"
|
||||
aria-labelledby=":r75:"
|
||||
aria-labelledby=":rbt:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -624,7 +624,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
|
||||
</button>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
aria-labelledby=":r7a:"
|
||||
aria-labelledby=":rc2:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -649,7 +649,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
|
||||
</button>
|
||||
<button
|
||||
aria-label="Threads"
|
||||
aria-labelledby=":r7f:"
|
||||
aria-labelledby=":rc7:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -678,7 +678,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
|
||||
>
|
||||
<div
|
||||
aria-label="2 members"
|
||||
aria-labelledby=":r7k:"
|
||||
aria-labelledby=":rcc:"
|
||||
class="mx_AccessibleButton mx_FacePile"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@@ -963,7 +963,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby=":r96:"
|
||||
aria-labelledby=":rdu:"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -979,7 +979,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-label="Voice call"
|
||||
aria-labelledby=":r9b:"
|
||||
aria-labelledby=":re3:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -1004,7 +1004,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||
</button>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
aria-labelledby=":r9g:"
|
||||
aria-labelledby=":re8:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -1029,7 +1029,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||
</button>
|
||||
<button
|
||||
aria-label="Threads"
|
||||
aria-labelledby=":r9l:"
|
||||
aria-labelledby=":red:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -1058,7 +1058,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||
>
|
||||
<div
|
||||
aria-label="2 members"
|
||||
aria-labelledby=":r9q:"
|
||||
aria-labelledby=":rei:"
|
||||
class="mx_AccessibleButton mx_FacePile"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@@ -1276,6 +1276,571 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`RoomView should not display the timeline when the room encryption is loading 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_RoomView"
|
||||
>
|
||||
<canvas
|
||||
aria-hidden="true"
|
||||
height="768"
|
||||
style="display: block; z-index: 999999; pointer-events: none; position: fixed; top: 0px; right: 0px;"
|
||||
width="0"
|
||||
/>
|
||||
<div
|
||||
class="mx_MainSplit"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomView_body mx_MainSplit_timeline"
|
||||
data-layout="group"
|
||||
>
|
||||
<header
|
||||
class="mx_Flex mx_RoomHeader light-panel"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x);"
|
||||
>
|
||||
<button
|
||||
aria-label="Open room settings"
|
||||
aria-live="off"
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="2"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="button"
|
||||
style="--cpd-avatar-size: 40px;"
|
||||
tabindex="-1"
|
||||
>
|
||||
!
|
||||
</button>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
class="mx_RoomHeader_infoWrapper"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_Box mx_RoomHeader_info mx_Box--flex"
|
||||
style="--mx-box-flex: 1;"
|
||||
>
|
||||
<div
|
||||
aria-level="1"
|
||||
class="_typography_yh5dq_162 _font-body-lg-semibold_yh5dq_83 mx_RoomHeader_heading"
|
||||
dir="auto"
|
||||
role="heading"
|
||||
>
|
||||
<span
|
||||
class="mx_RoomHeader_truncated mx_lineClamp"
|
||||
>
|
||||
!6:example.org
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
class="mx_Flex"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
||||
>
|
||||
<button
|
||||
aria-disabled="true"
|
||||
aria-label="There's no one here to call"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby=":r2c:"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 4h10a2 2 0 0 1 2 2v4.286l3.35-2.871a1 1 0 0 1 1.65.76v7.65a1 1 0 0 1-1.65.76L18 13.715V18a2 2 0 0 1-2 2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
aria-disabled="true"
|
||||
aria-label="There's no one here to call"
|
||||
aria-labelledby=":r2h:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m20.958 16.374.039 3.527c0 .285-.11.537-.33.756-.22.22-.472.33-.756.33a15.97 15.97 0 0 1-6.57-1.105 16.223 16.223 0 0 1-5.563-3.663 16.084 16.084 0 0 1-3.653-5.573 16.313 16.313 0 0 1-1.115-6.56c0-.285.11-.537.33-.757.22-.22.471-.329.755-.329l3.528.039a1.069 1.069 0 0 1 1.085.93l.543 3.954c.026.181.013.349-.039.504a1.088 1.088 0 0 1-.271.426l-1.64 1.64c.337.672.721 1.308 1.154 1.909.433.6 1.444 1.696 1.444 1.696s1.095 1.01 1.696 1.444c.6.433 1.237.817 1.909 1.153l1.64-1.64a1.08 1.08 0 0 1 .426-.27c.155-.052.323-.065.504-.04l3.954.543a1.069 1.069 0 0 1 .93 1.085Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
aria-labelledby=":r2m:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Threads"
|
||||
aria-labelledby=":r2r:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M4 3h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6l-2.293 2.293c-.63.63-1.707.184-1.707-.707V5a2 2 0 0 1 2-2Zm3 7h10a.97.97 0 0 0 .712-.287A.967.967 0 0 0 18 9a.967.967 0 0 0-.288-.713A.968.968 0 0 0 17 8H7a.968.968 0 0 0-.713.287A.968.968 0 0 0 6 9c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 4h6c.283 0 .52-.096.713-.287A.968.968 0 0 0 14 13a.968.968 0 0 0-.287-.713A.968.968 0 0 0 13 12H7a.967.967 0 0 0-.713.287A.968.968 0 0 0 6 13c0 .283.096.52.287.713.192.191.43.287.713.287Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
||||
>
|
||||
<div
|
||||
aria-label="0 members"
|
||||
aria-labelledby=":r30:"
|
||||
class="mx_AccessibleButton mx_FacePile"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_stacked-avatars_mcap2_111"
|
||||
/>
|
||||
0
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div
|
||||
class="mx_AutoHideScrollbar mx_AuxPanel"
|
||||
role="region"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
<main
|
||||
class="mx_RoomView_timeline mx_RoomView_timeline_rr_enabled"
|
||||
/>
|
||||
<div
|
||||
aria-label="Room status bar"
|
||||
class="mx_RoomView_statusArea"
|
||||
role="region"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomView_statusAreaBox"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomView_statusAreaBox_line"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`RoomView should not display the timeline when the room encryption is loading 2`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_RoomView"
|
||||
>
|
||||
<canvas
|
||||
aria-hidden="true"
|
||||
height="768"
|
||||
style="display: block; z-index: 999999; pointer-events: none; position: fixed; top: 0px; right: 0px;"
|
||||
width="0"
|
||||
/>
|
||||
<div
|
||||
class="mx_MainSplit"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomView_body mx_MainSplit_timeline"
|
||||
data-layout="group"
|
||||
>
|
||||
<header
|
||||
class="mx_Flex mx_RoomHeader light-panel"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x);"
|
||||
>
|
||||
<button
|
||||
aria-label="Open room settings"
|
||||
aria-live="off"
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="2"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="button"
|
||||
style="--cpd-avatar-size: 40px;"
|
||||
tabindex="-1"
|
||||
>
|
||||
!
|
||||
</button>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
class="mx_RoomHeader_infoWrapper"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_Box mx_RoomHeader_info mx_Box--flex"
|
||||
style="--mx-box-flex: 1;"
|
||||
>
|
||||
<div
|
||||
aria-level="1"
|
||||
class="_typography_yh5dq_162 _font-body-lg-semibold_yh5dq_83 mx_RoomHeader_heading"
|
||||
dir="auto"
|
||||
role="heading"
|
||||
>
|
||||
<span
|
||||
class="mx_RoomHeader_truncated mx_lineClamp"
|
||||
>
|
||||
!6:example.org
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
class="mx_Flex"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
||||
>
|
||||
<button
|
||||
aria-disabled="true"
|
||||
aria-label="There's no one here to call"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
|
||||
>
|
||||
<svg
|
||||
aria-labelledby=":r2c:"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 4h10a2 2 0 0 1 2 2v4.286l3.35-2.871a1 1 0 0 1 1.65.76v7.65a1 1 0 0 1-1.65.76L18 13.715V18a2 2 0 0 1-2 2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
aria-disabled="true"
|
||||
aria-label="There's no one here to call"
|
||||
aria-labelledby=":r2h:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
style="--cpd-icon-button-size: 100%; --cpd-color-icon-tertiary: var(--cpd-color-icon-disabled);"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m20.958 16.374.039 3.527c0 .285-.11.537-.33.756-.22.22-.472.33-.756.33a15.97 15.97 0 0 1-6.57-1.105 16.223 16.223 0 0 1-5.563-3.663 16.084 16.084 0 0 1-3.653-5.573 16.313 16.313 0 0 1-1.115-6.56c0-.285.11-.537.33-.757.22-.22.471-.329.755-.329l3.528.039a1.069 1.069 0 0 1 1.085.93l.543 3.954c.026.181.013.349-.039.504a1.088 1.088 0 0 1-.271.426l-1.64 1.64c.337.672.721 1.308 1.154 1.909.433.6 1.444 1.696 1.444 1.696s1.095 1.01 1.696 1.444c.6.433 1.237.817 1.909 1.153l1.64-1.64a1.08 1.08 0 0 1 .426-.27c.155-.052.323-.065.504-.04l3.954.543a1.069 1.069 0 0 1 .93 1.085Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
aria-labelledby=":r2m:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16v-4a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 11a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 12v4c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-8c.283 0 .52-.096.713-.287A.967.967 0 0 0 13 8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 13a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Threads"
|
||||
aria-labelledby=":r2r:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M4 3h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6l-2.293 2.293c-.63.63-1.707.184-1.707-.707V5a2 2 0 0 1 2-2Zm3 7h10a.97.97 0 0 0 .712-.287A.967.967 0 0 0 18 9a.967.967 0 0 0-.288-.713A.968.968 0 0 0 17 8H7a.968.968 0 0 0-.713.287A.968.968 0 0 0 6 9c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 4h6c.283 0 .52-.096.713-.287A.968.968 0 0 0 14 13a.968.968 0 0 0-.287-.713A.968.968 0 0 0 13 12H7a.967.967 0 0 0-.713.287A.968.968 0 0 0 6 13c0 .283.096.52.287.713.192.191.43.287.713.287Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="_typography_yh5dq_162 _font-body-sm-medium_yh5dq_50"
|
||||
>
|
||||
<div
|
||||
aria-label="0 members"
|
||||
aria-labelledby=":r30:"
|
||||
class="mx_AccessibleButton mx_FacePile"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="_stacked-avatars_mcap2_111"
|
||||
/>
|
||||
0
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div
|
||||
class="mx_AutoHideScrollbar mx_AuxPanel"
|
||||
role="region"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
<main
|
||||
class="mx_RoomView_timeline mx_RoomView_timeline_rr_enabled"
|
||||
>
|
||||
<div
|
||||
class="mx_AutoHideScrollbar mx_ScrollPanel mx_RoomView_messagePanel"
|
||||
tabindex="-1"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomView_messageListWrapper"
|
||||
>
|
||||
<ol
|
||||
aria-live="polite"
|
||||
class="mx_RoomView_MessageList"
|
||||
style="height: 400px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<div
|
||||
aria-label="Room status bar"
|
||||
class="mx_RoomView_statusArea"
|
||||
role="region"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomView_statusAreaBox"
|
||||
>
|
||||
<div
|
||||
class="mx_RoomView_statusAreaBox_line"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Message composer"
|
||||
class="mx_MessageComposer mx_MessageComposer_e2eStatus"
|
||||
role="region"
|
||||
>
|
||||
<div
|
||||
class="mx_MessageComposer_wrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_MessageComposer_row"
|
||||
>
|
||||
<div
|
||||
class="mx_MessageComposer_e2eIconWrapper"
|
||||
>
|
||||
<span
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
aria-labelledby=":r3e:"
|
||||
class="mx_E2EIcon mx_E2EIcon_verified mx_MessageComposer_e2eIcon"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SendMessageComposer"
|
||||
>
|
||||
<div
|
||||
class="mx_BasicMessageComposer"
|
||||
>
|
||||
<div
|
||||
aria-label="Formatting"
|
||||
class="mx_MessageComposerFormatBar"
|
||||
role="toolbar"
|
||||
>
|
||||
<button
|
||||
aria-label="Bold"
|
||||
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconBold"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
/>
|
||||
<button
|
||||
aria-label="Italics"
|
||||
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconItalic"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
/>
|
||||
<button
|
||||
aria-label="Strikethrough"
|
||||
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconStrikethrough"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
/>
|
||||
<button
|
||||
aria-label="Code block"
|
||||
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconCode"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
/>
|
||||
<button
|
||||
aria-label="Quote"
|
||||
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconQuote"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
/>
|
||||
<button
|
||||
aria-label="Insert link"
|
||||
class="mx_AccessibleButton mx_MessageComposerFormatBar_button mx_MessageComposerFormatBar_buttonIconInsertLink"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
aria-autocomplete="list"
|
||||
aria-disabled="false"
|
||||
aria-haspopup="listbox"
|
||||
aria-label="Send an encrypted message…"
|
||||
aria-multiline="true"
|
||||
class="mx_BasicMessageComposer_input mx_BasicMessageComposer_input_shouldShowPillAvatar mx_BasicMessageComposer_inputEmpty"
|
||||
contenteditable="true"
|
||||
data-testid="basicmessagecomposer"
|
||||
dir="auto"
|
||||
role="textbox"
|
||||
style="--placeholder: 'Send\\ an\\ encrypted\\ message…';"
|
||||
tabindex="0"
|
||||
translate="no"
|
||||
>
|
||||
<div>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_MessageComposer_actions"
|
||||
>
|
||||
<div
|
||||
aria-label="Emoji"
|
||||
class="mx_AccessibleButton mx_EmojiButton mx_MessageComposer_button mx_EmojiButton_icon"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-label="Attachment"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_upload"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-label="More options"
|
||||
class="mx_AccessibleButton mx_MessageComposer_button mx_MessageComposer_buttonMenu"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
<input
|
||||
multiple=""
|
||||
style="display: none;"
|
||||
type="file"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`RoomView should show error view if failed to look up room alias 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
@@ -1332,7 +1897,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||
aria-label="Open room settings"
|
||||
aria-live="off"
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="3"
|
||||
data-color="6"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="button"
|
||||
@@ -1359,7 +1924,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||
<span
|
||||
class="mx_RoomHeader_truncated mx_lineClamp"
|
||||
>
|
||||
!10:example.org
|
||||
!13:example.org
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1370,7 +1935,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||
>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
aria-labelledby=":r2k:"
|
||||
aria-labelledby=":r7c:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -1395,7 +1960,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||
</button>
|
||||
<button
|
||||
aria-label="Chat"
|
||||
aria-labelledby=":r2p:"
|
||||
aria-labelledby=":r7h:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -1420,7 +1985,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||
</button>
|
||||
<button
|
||||
aria-label="Threads"
|
||||
aria-labelledby=":r2u:"
|
||||
aria-labelledby=":r7m:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -1449,7 +2014,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||
>
|
||||
<div
|
||||
aria-label="0 members"
|
||||
aria-labelledby=":r33:"
|
||||
aria-labelledby=":r7r:"
|
||||
class="mx_AccessibleButton mx_FacePile"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@@ -1487,7 +2052,7 @@ exports[`RoomView video rooms should render joined video room view 1`] = `
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
aria-labelledby=":r3c:"
|
||||
aria-labelledby=":r84:"
|
||||
class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38"
|
||||
data-testid="base-card-close-button"
|
||||
role="button"
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<UserMenu> <UserMenu> when video broadcast when rendered should render as expected 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_UserMenu"
|
||||
>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="true"
|
||||
aria-label="User menu"
|
||||
class="mx_AccessibleButton mx_UserMenu_contextMenuButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_UserMenu_userAvatar"
|
||||
>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar mx_UserMenu_userAvatar_BaseAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="2"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
u
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -15,10 +15,11 @@ import RecordingPlayback, {
|
||||
PlaybackLayout,
|
||||
} from "../../../../../src/components/views/audio_messages/RecordingPlayback";
|
||||
import { Playback } from "../../../../../src/audio/Playback";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../../../src/contexts/RoomContext";
|
||||
import { TimelineRenderingType } from "../../../../../src/contexts/RoomContext";
|
||||
import { createAudioContext } from "../../../../../src/audio/compat";
|
||||
import { flushPromises } from "../../../../test-utils";
|
||||
import { IRoomState } from "../../../../../src/components/structures/RoomView";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
jest.mock("../../../../../src/WorkerManager", () => ({
|
||||
WorkerManager: jest.fn(() => ({
|
||||
@@ -56,9 +57,9 @@ describe("<RecordingPlayback />", () => {
|
||||
const defaultRoom = { roomId: "!room:server.org", timelineRenderingType: TimelineRenderingType.File } as IRoomState;
|
||||
const getComponent = (props: React.ComponentProps<typeof RecordingPlayback>, room = defaultRoom) =>
|
||||
render(
|
||||
<RoomContext.Provider value={room}>
|
||||
<ScopedRoomContextProvider {...room}>
|
||||
<RecordingPlayback {...props} />
|
||||
</RoomContext.Provider>,
|
||||
</ScopedRoomContextProvider>,
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -12,11 +12,11 @@ import { MatrixClient, PendingEventOrdering, Room, RoomMember } from "matrix-js-
|
||||
import React, { ComponentProps } from "react";
|
||||
|
||||
import MemberAvatar from "../../../../../src/components/views/avatars/MemberAvatar";
|
||||
import RoomContext from "../../../../../src/contexts/RoomContext";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { getRoomContext } from "../../../../test-utils/room";
|
||||
import { stubClient } from "../../../../test-utils/test-utils";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
describe("MemberAvatar", () => {
|
||||
const ROOM_ID = "roomId";
|
||||
@@ -27,9 +27,9 @@ describe("MemberAvatar", () => {
|
||||
|
||||
function getComponent(props: Partial<ComponentProps<typeof MemberAvatar>>) {
|
||||
return (
|
||||
<RoomContext.Provider value={getRoomContext(room, {})}>
|
||||
<ScopedRoomContextProvider {...getRoomContext(room, {})}>
|
||||
<MemberAvatar member={null} size="35px" {...props} />
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import { mocked } from "jest-mock";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../../../src/contexts/RoomContext";
|
||||
import { TimelineRenderingType } from "../../../../../src/contexts/RoomContext";
|
||||
import { IRoomState } from "../../../../../src/components/structures/RoomView";
|
||||
import { canEditContent } from "../../../../../src/utils/EventUtils";
|
||||
import { copyPlaintext, getSelectedText } from "../../../../../src/utils/strings";
|
||||
@@ -37,9 +37,8 @@ import dispatcher from "../../../../../src/dispatcher/dispatcher";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { ReadPinsEventId } from "../../../../../src/components/views/right_panel/types";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils";
|
||||
import { VoiceBroadcastInfoState } from "../../../../../src/voice-broadcast";
|
||||
import { createMessageEventContent } from "../../../../test-utils/events";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
jest.mock("../../../../../src/utils/strings", () => ({
|
||||
copyPlaintext: jest.fn(),
|
||||
@@ -233,17 +232,6 @@ describe("MessageContextMenu", () => {
|
||||
expect(document.querySelector('li[aria-label="Forward"]')).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should not allow forwarding a voice broadcast", () => {
|
||||
const broadcastStartEvent = mkVoiceBroadcastInfoStateEvent(
|
||||
roomId,
|
||||
VoiceBroadcastInfoState.Started,
|
||||
"@user:example.com",
|
||||
"ABC123",
|
||||
);
|
||||
createMenu(broadcastStartEvent);
|
||||
expect(document.querySelector('li[aria-label="Forward"]')).toBeFalsy();
|
||||
});
|
||||
|
||||
describe("forwarding beacons", () => {
|
||||
const aliceId = "@alice:server.org";
|
||||
|
||||
@@ -546,8 +534,8 @@ function createMenu(
|
||||
client.getRoom = jest.fn().mockReturnValue(room);
|
||||
|
||||
return render(
|
||||
<RoomContext.Provider value={context as IRoomState}>
|
||||
<ScopedRoomContextProvider {...(context as IRoomState)}>
|
||||
<MessageContextMenu mxEvent={mxEvent} onFinished={jest.fn()} {...props} />
|
||||
</RoomContext.Provider>,
|
||||
</ScopedRoomContextProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ describe("RoomGeneralContextMenu", () => {
|
||||
user: "@user:id",
|
||||
ts: 1000,
|
||||
});
|
||||
room.addLiveEvents([event], {});
|
||||
room.addLiveEvents([event], { addToState: true });
|
||||
|
||||
const { container } = getComponent({});
|
||||
|
||||
|
||||
@@ -6,14 +6,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { Feature, ServerSupport } from "matrix-js-sdk/src/feature";
|
||||
import { MatrixClient, MatrixEvent, RelationType } from "matrix-js-sdk/src/matrix";
|
||||
import { MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { screen, act } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import { flushPromises, mkEvent, stubClient } from "../../../../test-utils";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils";
|
||||
import { VoiceBroadcastInfoState } from "../../../../../src/voice-broadcast";
|
||||
import { createRedactEventDialog } from "../../../../../src/components/views/dialogs/ConfirmRedactDialog";
|
||||
|
||||
describe("ConfirmRedactDialog", () => {
|
||||
@@ -21,15 +18,6 @@ describe("ConfirmRedactDialog", () => {
|
||||
let client: MatrixClient;
|
||||
let mxEvent: MatrixEvent;
|
||||
|
||||
const setUpVoiceBroadcastStartedEvent = () => {
|
||||
mxEvent = mkVoiceBroadcastInfoStateEvent(
|
||||
roomId,
|
||||
VoiceBroadcastInfoState.Started,
|
||||
client.getUserId()!,
|
||||
client.deviceId!,
|
||||
);
|
||||
};
|
||||
|
||||
const confirmDeleteVoiceBroadcastStartedEvent = async () => {
|
||||
act(() => createRedactEventDialog({ mxEvent }));
|
||||
// double-flush promises required for the dialog to show up
|
||||
@@ -68,44 +56,4 @@ describe("ConfirmRedactDialog", () => {
|
||||
`cannot redact event ${mxEvent.getId()} without room ID`,
|
||||
);
|
||||
});
|
||||
|
||||
describe("when redacting a voice broadcast started event", () => {
|
||||
beforeEach(() => {
|
||||
setUpVoiceBroadcastStartedEvent();
|
||||
});
|
||||
|
||||
describe("and the server does not support relation based redactions", () => {
|
||||
beforeEach(() => {
|
||||
client.canSupport.set(Feature.RelationBasedRedactions, ServerSupport.Unsupported);
|
||||
});
|
||||
|
||||
describe("and displaying and confirm the dialog for a voice broadcast", () => {
|
||||
beforeEach(async () => {
|
||||
await confirmDeleteVoiceBroadcastStartedEvent();
|
||||
});
|
||||
|
||||
it("should call redact without `with_rel_types`", () => {
|
||||
expect(client.redactEvent).toHaveBeenCalledWith(roomId, mxEvent.getId(), undefined, {});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("and the server supports relation based redactions", () => {
|
||||
beforeEach(() => {
|
||||
client.canSupport.set(Feature.RelationBasedRedactions, ServerSupport.Unstable);
|
||||
});
|
||||
|
||||
describe("and displaying and confirm the dialog for a voice broadcast", () => {
|
||||
beforeEach(async () => {
|
||||
await confirmDeleteVoiceBroadcastStartedEvent();
|
||||
});
|
||||
|
||||
it("should call redact with `with_rel_types`", () => {
|
||||
expect(client.redactEvent).toHaveBeenCalledWith(roomId, mxEvent.getId(), undefined, {
|
||||
with_rel_types: [RelationType.Reference],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -67,7 +67,6 @@ describe("ForwardDialog", () => {
|
||||
getAccountData: jest.fn().mockReturnValue(accountDataEvent),
|
||||
getPushActionsForEvent: jest.fn(),
|
||||
mxcUrlToHttp: jest.fn().mockReturnValue(""),
|
||||
isRoomEncrypted: jest.fn().mockReturnValue(false),
|
||||
getProfileInfo: jest.fn().mockResolvedValue({
|
||||
displayname: "Alice",
|
||||
}),
|
||||
|
||||
@@ -141,16 +141,19 @@ describe("InviteDialog", () => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
room = new Room(roomId, mockClient, mockClient.getSafeUserId());
|
||||
room.addLiveEvents([
|
||||
mkMessage({
|
||||
msg: "Hello",
|
||||
relatesTo: undefined,
|
||||
event: true,
|
||||
room: roomId,
|
||||
user: mockClient.getSafeUserId(),
|
||||
ts: Date.now(),
|
||||
}),
|
||||
]);
|
||||
room.addLiveEvents(
|
||||
[
|
||||
mkMessage({
|
||||
msg: "Hello",
|
||||
relatesTo: undefined,
|
||||
event: true,
|
||||
room: roomId,
|
||||
user: mockClient.getSafeUserId(),
|
||||
ts: Date.now(),
|
||||
}),
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
room.currentState.setStateEvents([
|
||||
mkRoomCreateEvent(bobId, roomId),
|
||||
mkMembership({
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
* Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render, screen } from "jest-matrix-react";
|
||||
import { Device, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { stubClient } from "../../../../test-utils";
|
||||
import { ManualDeviceKeyVerificationDialog } from "../../../../../src/components/views/dialogs/ManualDeviceKeyVerificationDialog";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
|
||||
describe("ManualDeviceKeyVerificationDialog", () => {
|
||||
let mockClient: MatrixClient;
|
||||
|
||||
function renderDialog(userId: string, device: Device, onLegacyFinished: (confirm: boolean) => void) {
|
||||
return render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<ManualDeviceKeyVerificationDialog userId={userId} device={device} onFinished={onLegacyFinished} />
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
mockClient = stubClient();
|
||||
});
|
||||
|
||||
it("should display the device", () => {
|
||||
// When
|
||||
const deviceId = "XYZ";
|
||||
const device = new Device({
|
||||
userId: mockClient.getUserId()!,
|
||||
deviceId,
|
||||
displayName: "my device",
|
||||
algorithms: [],
|
||||
keys: new Map([[`ed25519:${deviceId}`, "ABCDEFGH"]]),
|
||||
});
|
||||
const { container } = renderDialog(mockClient.getUserId()!, device, jest.fn());
|
||||
|
||||
// Then
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should display the device of another user", () => {
|
||||
// When
|
||||
const userId = "@alice:example.com";
|
||||
const deviceId = "XYZ";
|
||||
const device = new Device({
|
||||
userId,
|
||||
deviceId,
|
||||
displayName: "my device",
|
||||
algorithms: [],
|
||||
keys: new Map([[`ed25519:${deviceId}`, "ABCDEFGH"]]),
|
||||
});
|
||||
const { container } = renderDialog(userId, device, jest.fn());
|
||||
|
||||
// Then
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should call onFinished and matrixClient.setDeviceVerified", () => {
|
||||
// When
|
||||
const deviceId = "XYZ";
|
||||
const device = new Device({
|
||||
userId: mockClient.getUserId()!,
|
||||
deviceId,
|
||||
displayName: "my device",
|
||||
algorithms: [],
|
||||
keys: new Map([[`ed25519:${deviceId}`, "ABCDEFGH"]]),
|
||||
});
|
||||
const onFinished = jest.fn();
|
||||
renderDialog(mockClient.getUserId()!, device, onFinished);
|
||||
|
||||
screen.getByRole("button", { name: "Verify session" }).click();
|
||||
|
||||
// Then
|
||||
expect(onFinished).toHaveBeenCalledWith(true);
|
||||
expect(mockClient.setDeviceVerified).toHaveBeenCalledWith(mockClient.getUserId(), deviceId, true);
|
||||
});
|
||||
|
||||
it("should call onFinished and not matrixClient.setDeviceVerified", () => {
|
||||
// When
|
||||
const deviceId = "XYZ";
|
||||
const device = new Device({
|
||||
userId: mockClient.getUserId()!,
|
||||
deviceId,
|
||||
displayName: "my device",
|
||||
algorithms: [],
|
||||
keys: new Map([[`ed25519:${deviceId}`, "ABCDEFGH"]]),
|
||||
});
|
||||
const onFinished = jest.fn();
|
||||
renderDialog(mockClient.getUserId()!, device, onFinished);
|
||||
|
||||
screen.getByRole("button", { name: "Cancel" }).click();
|
||||
|
||||
// Then
|
||||
expect(onFinished).toHaveBeenCalledWith(false);
|
||||
expect(mockClient.setDeviceVerified).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -7,111 +7,139 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { EventTimeline, MatrixEvent, Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||
import { render, RenderOptions } from "jest-matrix-react";
|
||||
import { MatrixClient, MatrixEvent, Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||
import { render, screen, act } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { waitFor } from "@testing-library/dom";
|
||||
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import { _t } from "../../../../../src/languageHandler";
|
||||
import ShareDialog from "../../../../../src/components/views/dialogs/ShareDialog";
|
||||
import { ShareDialog } from "../../../../../src/components/views/dialogs/ShareDialog";
|
||||
import { UIFeature } from "../../../../../src/settings/UIFeature";
|
||||
import { stubClient } from "../../../../test-utils";
|
||||
jest.mock("../../../../../src/utils/ShieldUtils");
|
||||
|
||||
function getWrapper(): RenderOptions {
|
||||
return {
|
||||
wrapper: ({ children }) => (
|
||||
<MatrixClientContext.Provider value={MatrixClientPeg.safeGet()}>{children}</MatrixClientContext.Provider>
|
||||
),
|
||||
};
|
||||
}
|
||||
import { stubClient, withClientContextRenderOptions } from "../../../../test-utils";
|
||||
import * as StringsModule from "../../../../../src/utils/strings";
|
||||
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks.ts";
|
||||
|
||||
describe("ShareDialog", () => {
|
||||
let client: MatrixClient;
|
||||
let room: Room;
|
||||
|
||||
const ROOM_ID = "!1:example.org";
|
||||
const copyTextFunc = jest.fn();
|
||||
|
||||
beforeEach(async () => {
|
||||
stubClient();
|
||||
room = new Room(ROOM_ID, MatrixClientPeg.get()!, "@alice:example.org");
|
||||
client = stubClient();
|
||||
room = new Room("!1:example.org", client, "@alice:example.org");
|
||||
jest.spyOn(StringsModule, "copyPlaintext").mockImplementation(copyTextFunc);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
copyTextFunc.mockClear();
|
||||
});
|
||||
|
||||
it("renders room share dialog", () => {
|
||||
const { container: withoutEvents } = render(<ShareDialog target={room} onFinished={jest.fn()} />, getWrapper());
|
||||
expect(withoutEvents).toHaveTextContent(_t("share|title_room"));
|
||||
function renderComponent(target: Room | RoomMember | URL) {
|
||||
return render(<ShareDialog target={target} onFinished={jest.fn()} />, withClientContextRenderOptions(client));
|
||||
}
|
||||
|
||||
jest.spyOn(room, "getLiveTimeline").mockReturnValue({ getEvents: () => [{} as MatrixEvent] } as EventTimeline);
|
||||
const { container: withEvents } = render(<ShareDialog target={room} onFinished={jest.fn()} />, getWrapper());
|
||||
expect(withEvents).toHaveTextContent(_t("share|permalink_most_recent"));
|
||||
const getUrl = () => new URL("https://matrix.org/");
|
||||
const getRoomMember = () => new RoomMember(room.roomId, "@alice:example.org");
|
||||
|
||||
test.each([
|
||||
{ name: "an URL", title: "Share Link", url: "https://matrix.org/", getTarget: getUrl },
|
||||
{
|
||||
name: "a room member",
|
||||
title: "Share User",
|
||||
url: "https://matrix.to/#/@alice:example.org",
|
||||
getTarget: getRoomMember,
|
||||
},
|
||||
])("should render a share dialog for $name", async ({ title, url, getTarget }) => {
|
||||
const { asFragment } = renderComponent(getTarget());
|
||||
|
||||
expect(screen.getByRole("heading", { name: title })).toBeInTheDocument();
|
||||
expect(screen.getByText(url)).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
|
||||
await userEvent.click(screen.getByRole("button", { name: "Copy link" }));
|
||||
expect(copyTextFunc).toHaveBeenCalledWith(url);
|
||||
});
|
||||
|
||||
it("renders user share dialog", () => {
|
||||
mockRoomMembers(room, 1);
|
||||
const { container } = render(
|
||||
<ShareDialog target={room.getJoinedMembers()[0]} onFinished={jest.fn()} />,
|
||||
getWrapper(),
|
||||
it("should render a share dialog for a room", async () => {
|
||||
const expectedURL = "https://matrix.to/#/!1:example.org";
|
||||
jest.spyOn(room.getLiveTimeline(), "getEvents").mockReturnValue([new MatrixEvent({ event_id: "!eventId" })]);
|
||||
|
||||
const { asFragment } = renderComponent(room);
|
||||
expect(screen.getByRole("heading", { name: "Share Room" })).toBeInTheDocument();
|
||||
expect(screen.getByText(expectedURL)).toBeInTheDocument();
|
||||
expect(screen.getByRole("checkbox", { name: "Link to most recent message" })).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
|
||||
await userEvent.click(screen.getByRole("button", { name: "Copy link" }));
|
||||
expect(copyTextFunc).toHaveBeenCalledWith(expectedURL);
|
||||
|
||||
// Click on the checkbox to link to the most recent message
|
||||
await userEvent.click(screen.getByRole("checkbox", { name: "Link to most recent message" }));
|
||||
const newExpectedURL = "https://matrix.to/#/!1:example.org/!eventId";
|
||||
expect(screen.getByText(newExpectedURL)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render a share dialog for a matrix event", async () => {
|
||||
const matrixEvent = new MatrixEvent({ event_id: "!eventId" });
|
||||
const permalinkCreator = new RoomPermalinkCreator(room);
|
||||
const expectedURL = "https://matrix.to/#/!1:example.org/!eventId";
|
||||
|
||||
const { asFragment } = render(
|
||||
<ShareDialog target={matrixEvent} permalinkCreator={permalinkCreator} onFinished={jest.fn()} />,
|
||||
withClientContextRenderOptions(client),
|
||||
);
|
||||
expect(container).toHaveTextContent(_t("share|title_user"));
|
||||
expect(screen.getByRole("heading", { name: "Share Room Message" })).toBeInTheDocument();
|
||||
expect(screen.getByText(expectedURL)).toBeInTheDocument();
|
||||
expect(screen.getByRole("checkbox", { name: "Link to selected message" })).toBeChecked();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
|
||||
await userEvent.click(screen.getByRole("button", { name: "Copy link" }));
|
||||
expect(copyTextFunc).toHaveBeenCalledWith(expectedURL);
|
||||
|
||||
// Click on the checkbox to link to the room
|
||||
await userEvent.click(screen.getByRole("checkbox", { name: "Link to selected message" }));
|
||||
expect(screen.getByText("https://matrix.to/#/!1:example.org")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders link share dialog", () => {
|
||||
mockRoomMembers(room, 1);
|
||||
const { container } = render(
|
||||
<ShareDialog target={new URL("https://matrix.org")} onFinished={jest.fn()} />,
|
||||
getWrapper(),
|
||||
);
|
||||
expect(container).toHaveTextContent(_t("share|title_link"));
|
||||
it("should change the copy button text when clicked", async () => {
|
||||
jest.useFakeTimers();
|
||||
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
|
||||
// To not be bother with rtl warnings about QR code state update
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
|
||||
renderComponent(room);
|
||||
await user.click(screen.getByRole("button", { name: "Copy link" }));
|
||||
// Move after `copyPlaintext`
|
||||
await jest.advanceTimersToNextTimerAsync();
|
||||
expect(screen.getByRole("button", { name: "Link copied" })).toBeInTheDocument();
|
||||
|
||||
// 2 sec after the button should be back to normal
|
||||
act(() => jest.advanceTimersByTime(2000));
|
||||
await waitFor(() => expect(screen.getByRole("button", { name: "Copy link" })).toBeInTheDocument());
|
||||
});
|
||||
|
||||
it("renders the QR code if configured", () => {
|
||||
it("should not render the QR code if disabled", () => {
|
||||
const originalGetValue = SettingsStore.getValue;
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((feature) => {
|
||||
if (feature === UIFeature.ShareQRCode) return true;
|
||||
if (feature === UIFeature.ShareQRCode) return false;
|
||||
return originalGetValue(feature);
|
||||
});
|
||||
const { container } = render(<ShareDialog target={room} onFinished={jest.fn()} />, getWrapper());
|
||||
const qrCodesVisible = container.getElementsByClassName("mx_ShareDialog_qrcode_container").length > 0;
|
||||
expect(qrCodesVisible).toBe(true);
|
||||
|
||||
const { asFragment } = renderComponent(room);
|
||||
expect(screen.queryByRole("img", { name: "QR code" })).toBeNull();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders the social button if configured", () => {
|
||||
it("should not render the socials if disabled", () => {
|
||||
const originalGetValue = SettingsStore.getValue;
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((feature) => {
|
||||
if (feature === UIFeature.ShareSocial) return true;
|
||||
if (feature === UIFeature.ShareSocial) return false;
|
||||
return originalGetValue(feature);
|
||||
});
|
||||
const { container } = render(<ShareDialog target={room} onFinished={jest.fn()} />, getWrapper());
|
||||
const qrCodesVisible = container.getElementsByClassName("mx_ShareDialog_social_container").length > 0;
|
||||
expect(qrCodesVisible).toBe(true);
|
||||
});
|
||||
it("renders custom title and subtitle", () => {
|
||||
const { container } = render(
|
||||
<ShareDialog
|
||||
target={room}
|
||||
customTitle="test_title_123"
|
||||
subtitle="custom_subtitle_1234"
|
||||
onFinished={jest.fn()}
|
||||
/>,
|
||||
getWrapper(),
|
||||
);
|
||||
expect(container).toHaveTextContent("test_title_123");
|
||||
expect(container).toHaveTextContent("custom_subtitle_1234");
|
||||
|
||||
const { asFragment } = renderComponent(room);
|
||||
expect(screen.queryByRole("link", { name: "Reddit" })).toBeNull();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
/**
|
||||
*
|
||||
* @param count the number of users to create
|
||||
*/
|
||||
function mockRoomMembers(room: Room, count: number) {
|
||||
const members = Array(count)
|
||||
.fill(0)
|
||||
.map((_, index) => new RoomMember(room.roomId, "@alice:example.org"));
|
||||
|
||||
room.currentState.setJoinedMemberCount(members.length);
|
||||
room.getJoinedMembers = jest.fn().mockReturnValue(members);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* 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 { Device, MatrixClient, User } from "matrix-js-sdk/src/matrix";
|
||||
import { render, screen } from "jest-matrix-react";
|
||||
|
||||
import { stubClient } from "../../../../test-utils";
|
||||
import UntrustedDeviceDialog from "../../../../../src/components/views/dialogs/UntrustedDeviceDialog.tsx";
|
||||
|
||||
describe("<UntrustedDeviceDialog />", () => {
|
||||
let client: MatrixClient;
|
||||
let user: User;
|
||||
let device: Device;
|
||||
const onFinished = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
client = stubClient();
|
||||
user = User.createUser("@alice:example.org", client);
|
||||
user.setDisplayName("Alice");
|
||||
device = new Device({ deviceId: "device_id", userId: user.userId, algorithms: [], keys: new Map() });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
onFinished.mockReset();
|
||||
});
|
||||
|
||||
function renderComponent() {
|
||||
return render(<UntrustedDeviceDialog user={user} device={device} onFinished={onFinished} />);
|
||||
}
|
||||
|
||||
it("should display the dialog for the device of another user", () => {
|
||||
const { asFragment } = renderComponent();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should display the dialog for the device of the current user", () => {
|
||||
jest.spyOn(client, "getUserId").mockReturnValue(user.userId);
|
||||
|
||||
const { asFragment } = renderComponent();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should call onFinished without parameter when Done is clicked", () => {
|
||||
renderComponent();
|
||||
screen.getByRole("button", { name: "Done" }).click();
|
||||
expect(onFinished).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it("should call onFinished with sas when Interactively verify by emoji is clicked", () => {
|
||||
renderComponent();
|
||||
screen.getByRole("button", { name: "Interactively verify by emoji" }).click();
|
||||
expect(onFinished).toHaveBeenCalledWith("sas");
|
||||
});
|
||||
});
|
||||
@@ -185,33 +185,6 @@ exports[`DevtoolsDialog renders the devtools dialog 1`] = `
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_SettingsFlag"
|
||||
>
|
||||
<label
|
||||
class="mx_SettingsFlag_label"
|
||||
for="mx_SettingsFlag_4yVCeEefiPqp"
|
||||
>
|
||||
<span
|
||||
class="mx_SettingsFlag_labelText"
|
||||
>
|
||||
Force 15s voice broadcast chunk length
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
aria-checked="false"
|
||||
aria-disabled="false"
|
||||
aria-label="Force 15s voice broadcast chunk length"
|
||||
class="mx_AccessibleButton mx_ToggleSwitch mx_ToggleSwitch_enabled"
|
||||
id="mx_SettingsFlag_4yVCeEefiPqp"
|
||||
role="switch"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="mx_ToggleSwitch_ball"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ManualDeviceKeyVerificationDialog should display the device 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-describedby="mx_Dialog_content"
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_QuestionDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Verify session
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_content"
|
||||
id="mx_Dialog_content"
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
Confirm by comparing the following with the User Settings in your other session:
|
||||
</p>
|
||||
<div
|
||||
class="mx_DeviceVerifyDialog_cryptoSection"
|
||||
>
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
Session name
|
||||
:
|
||||
</label>
|
||||
|
||||
<span>
|
||||
my device
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
Session ID
|
||||
:
|
||||
</label>
|
||||
|
||||
<span>
|
||||
<code>
|
||||
XYZ
|
||||
</code>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
Session key
|
||||
:
|
||||
</label>
|
||||
|
||||
<span>
|
||||
<code>
|
||||
<strong>
|
||||
ABCD EFGH
|
||||
</strong>
|
||||
</code>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>
|
||||
If they don't match, the security of your communication may be compromised.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
data-testid="dialog-cancel-button"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
type="button"
|
||||
>
|
||||
Verify session
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ManualDeviceKeyVerificationDialog should display the device of another user 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-describedby="mx_Dialog_content"
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_QuestionDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Verify session
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_content"
|
||||
id="mx_Dialog_content"
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
Confirm this user's session by comparing the following with their User Settings:
|
||||
</p>
|
||||
<div
|
||||
class="mx_DeviceVerifyDialog_cryptoSection"
|
||||
>
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
Session name
|
||||
:
|
||||
</label>
|
||||
|
||||
<span>
|
||||
my device
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
Session ID
|
||||
:
|
||||
</label>
|
||||
|
||||
<span>
|
||||
<code>
|
||||
XYZ
|
||||
</code>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
Session key
|
||||
:
|
||||
</label>
|
||||
|
||||
<span>
|
||||
<code>
|
||||
<strong>
|
||||
ABCD EFGH
|
||||
</strong>
|
||||
</code>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p>
|
||||
If they don't match, the security of your communication may be compromised.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
data-testid="dialog-cancel-button"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
type="button"
|
||||
>
|
||||
Verify session
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,852 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ShareDialog should not render the QR code if disabled 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-describedby="mx_Dialog_content"
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_ShareDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Share Room
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_ShareDialog_content"
|
||||
>
|
||||
<div
|
||||
class="mx_ShareDialog_top"
|
||||
>
|
||||
<span>
|
||||
https://matrix.to/#/!1:example.org
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
class="_button_i91xf_17 _has-icon_i91xf_66"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 19.071c-.978.978-2.157 1.467-3.536 1.467-1.378 0-2.557-.489-3.535-1.467-.978-.978-1.467-2.157-1.467-3.536 0-1.378.489-2.557 1.467-3.535L7.05 9.879c.2-.2.436-.3.707-.3.271 0 .507.1.707.3.2.2.301.436.301.707 0 .27-.1.506-.3.707l-2.122 2.121a2.893 2.893 0 0 0-.884 2.122c0 .824.295 1.532.884 2.12.59.59 1.296.885 2.121.885s1.533-.295 2.122-.884l2.121-2.121c.2-.2.436-.301.707-.301.271 0 .507.1.707.3.2.2.3.437.3.708 0 .27-.1.506-.3.707L12 19.07Zm-1.414-4.243c-.2.2-.436.3-.707.3a.967.967 0 0 1-.707-.3.969.969 0 0 1-.301-.707c0-.27.1-.507.3-.707l4.243-4.242c.2-.2.436-.301.707-.301.271 0 .507.1.707.3.2.2.3.437.3.708 0 .27-.1.506-.3.707l-4.242 4.242Zm6.364-.707c-.2.2-.436.3-.707.3a.968.968 0 0 1-.707-.3.969.969 0 0 1-.301-.707c0-.27.1-.507.3-.707l2.122-2.121c.59-.59.884-1.297.884-2.122s-.295-1.532-.884-2.12a2.893 2.893 0 0 0-2.121-.885c-.825 0-1.532.295-2.122.884l-2.121 2.121c-.2.2-.436.301-.707.301a.968.968 0 0 1-.707-.3.97.97 0 0 1-.3-.708c0-.27.1-.506.3-.707L12 4.93c.978-.978 2.157-1.467 3.536-1.467 1.378 0 2.557.489 3.535 1.467.978.978 1.467 2.157 1.467 3.535 0 1.38-.489 2.558-1.467 3.536l-2.121 2.121Z"
|
||||
/>
|
||||
</svg>
|
||||
Copy link
|
||||
</button>
|
||||
<div
|
||||
class="mx_ShareDialog_social"
|
||||
>
|
||||
<a
|
||||
href="https://www.facebook.com/sharer/sharer.php?u=https://matrix.to/#/!1:example.org"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="Facebook"
|
||||
>
|
||||
<img
|
||||
alt="Facebook"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://twitter.com/home?status=https://matrix.to/#/!1:example.org"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="Twitter"
|
||||
>
|
||||
<img
|
||||
alt="Twitter"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.linkedin.com/shareArticle?mini=true&url=https://matrix.to/#/!1:example.org"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="LinkedIn"
|
||||
>
|
||||
<img
|
||||
alt="LinkedIn"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.reddit.com/submit?url=https://matrix.to/#/!1:example.org"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="Reddit"
|
||||
>
|
||||
<img
|
||||
alt="Reddit"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="mailto:?body=https://matrix.to/#/!1:example.org"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="email"
|
||||
>
|
||||
<img
|
||||
alt="email"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`ShareDialog should not render the socials if disabled 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-describedby="mx_Dialog_content"
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_ShareDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Share Room
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_ShareDialog_content"
|
||||
>
|
||||
<div
|
||||
class="mx_ShareDialog_top"
|
||||
>
|
||||
<div
|
||||
class="mx_QRCode"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
https://matrix.to/#/!1:example.org
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
class="_button_i91xf_17 _has-icon_i91xf_66"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 19.071c-.978.978-2.157 1.467-3.536 1.467-1.378 0-2.557-.489-3.535-1.467-.978-.978-1.467-2.157-1.467-3.536 0-1.378.489-2.557 1.467-3.535L7.05 9.879c.2-.2.436-.3.707-.3.271 0 .507.1.707.3.2.2.301.436.301.707 0 .27-.1.506-.3.707l-2.122 2.121a2.893 2.893 0 0 0-.884 2.122c0 .824.295 1.532.884 2.12.59.59 1.296.885 2.121.885s1.533-.295 2.122-.884l2.121-2.121c.2-.2.436-.301.707-.301.271 0 .507.1.707.3.2.2.3.437.3.708 0 .27-.1.506-.3.707L12 19.07Zm-1.414-4.243c-.2.2-.436.3-.707.3a.967.967 0 0 1-.707-.3.969.969 0 0 1-.301-.707c0-.27.1-.507.3-.707l4.243-4.242c.2-.2.436-.301.707-.301.271 0 .507.1.707.3.2.2.3.437.3.708 0 .27-.1.506-.3.707l-4.242 4.242Zm6.364-.707c-.2.2-.436.3-.707.3a.968.968 0 0 1-.707-.3.969.969 0 0 1-.301-.707c0-.27.1-.507.3-.707l2.122-2.121c.59-.59.884-1.297.884-2.122s-.295-1.532-.884-2.12a2.893 2.893 0 0 0-2.121-.885c-.825 0-1.532.295-2.122.884l-2.121 2.121c-.2.2-.436.301-.707.301a.968.968 0 0 1-.707-.3.97.97 0 0 1-.3-.708c0-.27.1-.506.3-.707L12 4.93c.978-.978 2.157-1.467 3.536-1.467 1.378 0 2.557.489 3.535 1.467.978.978 1.467 2.157 1.467 3.535 0 1.38-.489 2.558-1.467 3.536l-2.121 2.121Z"
|
||||
/>
|
||||
</svg>
|
||||
Copy link
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`ShareDialog should render a share dialog for a matrix event 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-describedby="mx_Dialog_content"
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_ShareDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Share Room Message
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_ShareDialog_content"
|
||||
>
|
||||
<div
|
||||
class="mx_ShareDialog_top"
|
||||
>
|
||||
<div
|
||||
class="mx_QRCode"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
https://matrix.to/#/!1:example.org/!eventId
|
||||
</span>
|
||||
</div>
|
||||
<label>
|
||||
<div
|
||||
class="_container_1wloq_18"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="_input_1wloq_26"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1wloq_27"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212L4.55 13c-.183-.183-.27-.42-.263-.713.009-.291.105-.529.288-.712a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275L9.55 15.15l8.475-8.475c.183-.183.42-.275.713-.275.291 0 .529.092.712.275.183.183.275.42.275.713 0 .291-.092.529-.275.712l-9.2 9.2c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
Link to selected message
|
||||
</label>
|
||||
<button
|
||||
class="_button_i91xf_17 _has-icon_i91xf_66"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 19.071c-.978.978-2.157 1.467-3.536 1.467-1.378 0-2.557-.489-3.535-1.467-.978-.978-1.467-2.157-1.467-3.536 0-1.378.489-2.557 1.467-3.535L7.05 9.879c.2-.2.436-.3.707-.3.271 0 .507.1.707.3.2.2.301.436.301.707 0 .27-.1.506-.3.707l-2.122 2.121a2.893 2.893 0 0 0-.884 2.122c0 .824.295 1.532.884 2.12.59.59 1.296.885 2.121.885s1.533-.295 2.122-.884l2.121-2.121c.2-.2.436-.301.707-.301.271 0 .507.1.707.3.2.2.3.437.3.708 0 .27-.1.506-.3.707L12 19.07Zm-1.414-4.243c-.2.2-.436.3-.707.3a.967.967 0 0 1-.707-.3.969.969 0 0 1-.301-.707c0-.27.1-.507.3-.707l4.243-4.242c.2-.2.436-.301.707-.301.271 0 .507.1.707.3.2.2.3.437.3.708 0 .27-.1.506-.3.707l-4.242 4.242Zm6.364-.707c-.2.2-.436.3-.707.3a.968.968 0 0 1-.707-.3.969.969 0 0 1-.301-.707c0-.27.1-.507.3-.707l2.122-2.121c.59-.59.884-1.297.884-2.122s-.295-1.532-.884-2.12a2.893 2.893 0 0 0-2.121-.885c-.825 0-1.532.295-2.122.884l-2.121 2.121c-.2.2-.436.301-.707.301a.968.968 0 0 1-.707-.3.97.97 0 0 1-.3-.708c0-.27.1-.506.3-.707L12 4.93c.978-.978 2.157-1.467 3.536-1.467 1.378 0 2.557.489 3.535 1.467.978.978 1.467 2.157 1.467 3.535 0 1.38-.489 2.558-1.467 3.536l-2.121 2.121Z"
|
||||
/>
|
||||
</svg>
|
||||
Copy link
|
||||
</button>
|
||||
<div
|
||||
class="mx_ShareDialog_social"
|
||||
>
|
||||
<a
|
||||
href="https://www.facebook.com/sharer/sharer.php?u=https://matrix.to/#/!1:example.org/!eventId"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="Facebook"
|
||||
>
|
||||
<img
|
||||
alt="Facebook"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://twitter.com/home?status=https://matrix.to/#/!1:example.org/!eventId"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="Twitter"
|
||||
>
|
||||
<img
|
||||
alt="Twitter"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.linkedin.com/shareArticle?mini=true&url=https://matrix.to/#/!1:example.org/!eventId"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="LinkedIn"
|
||||
>
|
||||
<img
|
||||
alt="LinkedIn"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.reddit.com/submit?url=https://matrix.to/#/!1:example.org/!eventId"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="Reddit"
|
||||
>
|
||||
<img
|
||||
alt="Reddit"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="mailto:?body=https://matrix.to/#/!1:example.org/!eventId"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="email"
|
||||
>
|
||||
<img
|
||||
alt="email"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`ShareDialog should render a share dialog for a room 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-describedby="mx_Dialog_content"
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_ShareDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Share Room
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_ShareDialog_content"
|
||||
>
|
||||
<div
|
||||
class="mx_ShareDialog_top"
|
||||
>
|
||||
<div
|
||||
class="mx_QRCode"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
https://matrix.to/#/!1:example.org
|
||||
</span>
|
||||
</div>
|
||||
<label>
|
||||
<div
|
||||
class="_container_1wloq_18"
|
||||
>
|
||||
<input
|
||||
class="_input_1wloq_26"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div
|
||||
class="_ui_1wloq_27"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.55 17.575c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212L4.55 13c-.183-.183-.27-.42-.263-.713.009-.291.105-.529.288-.712a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275L9.55 15.15l8.475-8.475c.183-.183.42-.275.713-.275.291 0 .529.092.712.275.183.183.275.42.275.713 0 .291-.092.529-.275.712l-9.2 9.2c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
Link to most recent message
|
||||
</label>
|
||||
<button
|
||||
class="_button_i91xf_17 _has-icon_i91xf_66"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 19.071c-.978.978-2.157 1.467-3.536 1.467-1.378 0-2.557-.489-3.535-1.467-.978-.978-1.467-2.157-1.467-3.536 0-1.378.489-2.557 1.467-3.535L7.05 9.879c.2-.2.436-.3.707-.3.271 0 .507.1.707.3.2.2.301.436.301.707 0 .27-.1.506-.3.707l-2.122 2.121a2.893 2.893 0 0 0-.884 2.122c0 .824.295 1.532.884 2.12.59.59 1.296.885 2.121.885s1.533-.295 2.122-.884l2.121-2.121c.2-.2.436-.301.707-.301.271 0 .507.1.707.3.2.2.3.437.3.708 0 .27-.1.506-.3.707L12 19.07Zm-1.414-4.243c-.2.2-.436.3-.707.3a.967.967 0 0 1-.707-.3.969.969 0 0 1-.301-.707c0-.27.1-.507.3-.707l4.243-4.242c.2-.2.436-.301.707-.301.271 0 .507.1.707.3.2.2.3.437.3.708 0 .27-.1.506-.3.707l-4.242 4.242Zm6.364-.707c-.2.2-.436.3-.707.3a.968.968 0 0 1-.707-.3.969.969 0 0 1-.301-.707c0-.27.1-.507.3-.707l2.122-2.121c.59-.59.884-1.297.884-2.122s-.295-1.532-.884-2.12a2.893 2.893 0 0 0-2.121-.885c-.825 0-1.532.295-2.122.884l-2.121 2.121c-.2.2-.436.301-.707.301a.968.968 0 0 1-.707-.3.97.97 0 0 1-.3-.708c0-.27.1-.506.3-.707L12 4.93c.978-.978 2.157-1.467 3.536-1.467 1.378 0 2.557.489 3.535 1.467.978.978 1.467 2.157 1.467 3.535 0 1.38-.489 2.558-1.467 3.536l-2.121 2.121Z"
|
||||
/>
|
||||
</svg>
|
||||
Copy link
|
||||
</button>
|
||||
<div
|
||||
class="mx_ShareDialog_social"
|
||||
>
|
||||
<a
|
||||
href="https://www.facebook.com/sharer/sharer.php?u=https://matrix.to/#/!1:example.org"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="Facebook"
|
||||
>
|
||||
<img
|
||||
alt="Facebook"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://twitter.com/home?status=https://matrix.to/#/!1:example.org"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="Twitter"
|
||||
>
|
||||
<img
|
||||
alt="Twitter"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.linkedin.com/shareArticle?mini=true&url=https://matrix.to/#/!1:example.org"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="LinkedIn"
|
||||
>
|
||||
<img
|
||||
alt="LinkedIn"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.reddit.com/submit?url=https://matrix.to/#/!1:example.org"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="Reddit"
|
||||
>
|
||||
<img
|
||||
alt="Reddit"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="mailto:?body=https://matrix.to/#/!1:example.org"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="email"
|
||||
>
|
||||
<img
|
||||
alt="email"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`ShareDialog should render a share dialog for a room member 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-describedby="mx_Dialog_content"
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_ShareDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Share User
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_ShareDialog_content"
|
||||
>
|
||||
<div
|
||||
class="mx_ShareDialog_top"
|
||||
>
|
||||
<div
|
||||
class="mx_QRCode"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
https://matrix.to/#/@alice:example.org
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
class="_button_i91xf_17 _has-icon_i91xf_66"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 19.071c-.978.978-2.157 1.467-3.536 1.467-1.378 0-2.557-.489-3.535-1.467-.978-.978-1.467-2.157-1.467-3.536 0-1.378.489-2.557 1.467-3.535L7.05 9.879c.2-.2.436-.3.707-.3.271 0 .507.1.707.3.2.2.301.436.301.707 0 .27-.1.506-.3.707l-2.122 2.121a2.893 2.893 0 0 0-.884 2.122c0 .824.295 1.532.884 2.12.59.59 1.296.885 2.121.885s1.533-.295 2.122-.884l2.121-2.121c.2-.2.436-.301.707-.301.271 0 .507.1.707.3.2.2.3.437.3.708 0 .27-.1.506-.3.707L12 19.07Zm-1.414-4.243c-.2.2-.436.3-.707.3a.967.967 0 0 1-.707-.3.969.969 0 0 1-.301-.707c0-.27.1-.507.3-.707l4.243-4.242c.2-.2.436-.301.707-.301.271 0 .507.1.707.3.2.2.3.437.3.708 0 .27-.1.506-.3.707l-4.242 4.242Zm6.364-.707c-.2.2-.436.3-.707.3a.968.968 0 0 1-.707-.3.969.969 0 0 1-.301-.707c0-.27.1-.507.3-.707l2.122-2.121c.59-.59.884-1.297.884-2.122s-.295-1.532-.884-2.12a2.893 2.893 0 0 0-2.121-.885c-.825 0-1.532.295-2.122.884l-2.121 2.121c-.2.2-.436.301-.707.301a.968.968 0 0 1-.707-.3.97.97 0 0 1-.3-.708c0-.27.1-.506.3-.707L12 4.93c.978-.978 2.157-1.467 3.536-1.467 1.378 0 2.557.489 3.535 1.467.978.978 1.467 2.157 1.467 3.535 0 1.38-.489 2.558-1.467 3.536l-2.121 2.121Z"
|
||||
/>
|
||||
</svg>
|
||||
Copy link
|
||||
</button>
|
||||
<div
|
||||
class="mx_ShareDialog_social"
|
||||
>
|
||||
<a
|
||||
href="https://www.facebook.com/sharer/sharer.php?u=https://matrix.to/#/@alice:example.org"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="Facebook"
|
||||
>
|
||||
<img
|
||||
alt="Facebook"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://twitter.com/home?status=https://matrix.to/#/@alice:example.org"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="Twitter"
|
||||
>
|
||||
<img
|
||||
alt="Twitter"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.linkedin.com/shareArticle?mini=true&url=https://matrix.to/#/@alice:example.org"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="LinkedIn"
|
||||
>
|
||||
<img
|
||||
alt="LinkedIn"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.reddit.com/submit?url=https://matrix.to/#/@alice:example.org"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="Reddit"
|
||||
>
|
||||
<img
|
||||
alt="Reddit"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="mailto:?body=https://matrix.to/#/@alice:example.org"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="email"
|
||||
>
|
||||
<img
|
||||
alt="email"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`ShareDialog should render a share dialog for an URL 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-describedby="mx_Dialog_content"
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_ShareDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Share Link
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_ShareDialog_content"
|
||||
>
|
||||
<div
|
||||
class="mx_ShareDialog_top"
|
||||
>
|
||||
<div
|
||||
class="mx_QRCode"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
https://matrix.org/
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
class="_button_i91xf_17 _has-icon_i91xf_66"
|
||||
data-kind="primary"
|
||||
data-size="lg"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 19.071c-.978.978-2.157 1.467-3.536 1.467-1.378 0-2.557-.489-3.535-1.467-.978-.978-1.467-2.157-1.467-3.536 0-1.378.489-2.557 1.467-3.535L7.05 9.879c.2-.2.436-.3.707-.3.271 0 .507.1.707.3.2.2.301.436.301.707 0 .27-.1.506-.3.707l-2.122 2.121a2.893 2.893 0 0 0-.884 2.122c0 .824.295 1.532.884 2.12.59.59 1.296.885 2.121.885s1.533-.295 2.122-.884l2.121-2.121c.2-.2.436-.301.707-.301.271 0 .507.1.707.3.2.2.3.437.3.708 0 .27-.1.506-.3.707L12 19.07Zm-1.414-4.243c-.2.2-.436.3-.707.3a.967.967 0 0 1-.707-.3.969.969 0 0 1-.301-.707c0-.27.1-.507.3-.707l4.243-4.242c.2-.2.436-.301.707-.301.271 0 .507.1.707.3.2.2.3.437.3.708 0 .27-.1.506-.3.707l-4.242 4.242Zm6.364-.707c-.2.2-.436.3-.707.3a.968.968 0 0 1-.707-.3.969.969 0 0 1-.301-.707c0-.27.1-.507.3-.707l2.122-2.121c.59-.59.884-1.297.884-2.122s-.295-1.532-.884-2.12a2.893 2.893 0 0 0-2.121-.885c-.825 0-1.532.295-2.122.884l-2.121 2.121c-.2.2-.436.301-.707.301a.968.968 0 0 1-.707-.3.97.97 0 0 1-.3-.708c0-.27.1-.506.3-.707L12 4.93c.978-.978 2.157-1.467 3.536-1.467 1.378 0 2.557.489 3.535 1.467.978.978 1.467 2.157 1.467 3.535 0 1.38-.489 2.558-1.467 3.536l-2.121 2.121Z"
|
||||
/>
|
||||
</svg>
|
||||
Copy link
|
||||
</button>
|
||||
<div
|
||||
class="mx_ShareDialog_social"
|
||||
>
|
||||
<a
|
||||
href="https://www.facebook.com/sharer/sharer.php?u=https://matrix.org/"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="Facebook"
|
||||
>
|
||||
<img
|
||||
alt="Facebook"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://twitter.com/home?status=https://matrix.org/"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="Twitter"
|
||||
>
|
||||
<img
|
||||
alt="Twitter"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.linkedin.com/shareArticle?mini=true&url=https://matrix.org/"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="LinkedIn"
|
||||
>
|
||||
<img
|
||||
alt="LinkedIn"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.reddit.com/submit?url=https://matrix.org/"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="Reddit"
|
||||
>
|
||||
<img
|
||||
alt="Reddit"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="mailto:?body=https://matrix.org/"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
title="email"
|
||||
>
|
||||
<img
|
||||
alt="email"
|
||||
src="image-file-stub"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
@@ -0,0 +1,149 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<UntrustedDeviceDialog /> should display the dialog for the device of another user 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_UntrustedDeviceDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
<div
|
||||
class="mx_E2EIcon mx_E2EIcon_warning"
|
||||
style="width: 24px; height: 24px;"
|
||||
/>
|
||||
Not Trusted
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_content"
|
||||
id="mx_Dialog_content"
|
||||
>
|
||||
<p>
|
||||
Alice (@alice:example.org) signed in to a new session without verifying it:
|
||||
</p>
|
||||
<p>
|
||||
(device_id)
|
||||
</p>
|
||||
<p>
|
||||
Ask this user to verify their session, or manually verify it below.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Interactively verify by emoji
|
||||
</div>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Done
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<UntrustedDeviceDialog /> should display the dialog for the device of the current user 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_UntrustedDeviceDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
<div
|
||||
class="mx_E2EIcon mx_E2EIcon_warning"
|
||||
style="width: 24px; height: 24px;"
|
||||
/>
|
||||
Not Trusted
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_content"
|
||||
id="mx_Dialog_content"
|
||||
>
|
||||
<p>
|
||||
You signed in to a new session without verifying it:
|
||||
</p>
|
||||
<p>
|
||||
(device_id)
|
||||
</p>
|
||||
<p>
|
||||
Verify your other session using one of the options below.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Interactively verify by emoji
|
||||
</div>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Done
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
@@ -9,7 +9,8 @@ Please see LICENSE files in the repository root for full details.
|
||||
import React from "react";
|
||||
import { screen, fireEvent, render, waitFor, act } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { Crypto, IMegolmSessionData } from "matrix-js-sdk/src/matrix";
|
||||
import { IMegolmSessionData } from "matrix-js-sdk/src/matrix";
|
||||
import { CryptoApi } from "matrix-js-sdk/src/crypto-api";
|
||||
|
||||
import * as MegolmExportEncryption from "../../../../../../src/utils/MegolmExportEncryption";
|
||||
import ExportE2eKeysDialog from "../../../../../../src/async-components/views/dialogs/security/ExportE2eKeysDialog";
|
||||
@@ -62,7 +63,7 @@ describe("ExportE2eKeysDialog", () => {
|
||||
cli.getCrypto = () => {
|
||||
return {
|
||||
exportRoomKeysAsJson,
|
||||
} as unknown as Crypto.CryptoApi;
|
||||
} as unknown as CryptoApi;
|
||||
};
|
||||
|
||||
// Mock the result of encrypting the sessions. If we don't do this, the
|
||||
|
||||
@@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
import React from "react";
|
||||
import { fireEvent, render, waitFor } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { Crypto } from "matrix-js-sdk/src/matrix";
|
||||
import { CryptoApi } from "matrix-js-sdk/src/crypto-api";
|
||||
|
||||
import ImportE2eKeysDialog from "../../../../../../src/async-components/views/dialogs/security/ImportE2eKeysDialog";
|
||||
import * as MegolmExportEncryption from "../../../../../../src/utils/MegolmExportEncryption";
|
||||
@@ -67,7 +67,7 @@ describe("ImportE2eKeysDialog", () => {
|
||||
cli.getCrypto = () => {
|
||||
return {
|
||||
importRoomKeysAsJson,
|
||||
} as unknown as Crypto.CryptoApi;
|
||||
} as unknown as CryptoApi;
|
||||
};
|
||||
|
||||
// Mock the result of decrypting the sessions, to avoid needing to
|
||||
|
||||
@@ -86,7 +86,7 @@ describe("<Pill>", () => {
|
||||
room: room1Id,
|
||||
msg: "Room 1 Message",
|
||||
});
|
||||
room1.addLiveEvents([room1Message]);
|
||||
room1.addLiveEvents([room1Message], { addToState: true });
|
||||
|
||||
room2 = new Room(room2Id, client, user1Id);
|
||||
room2.currentState.setStateEvents([mkRoomMemberJoinEvent(user2Id, room2Id)]);
|
||||
|
||||
@@ -41,7 +41,7 @@ describe("<RoomTopic/>", () => {
|
||||
ts: 123,
|
||||
event: true,
|
||||
});
|
||||
room.addLiveEvents([topicEvent]);
|
||||
room.addLiveEvents([topicEvent], { addToState: true });
|
||||
|
||||
return room;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ import {
|
||||
import { MediaEventHelper } from "../../../../../src/utils/MediaEventHelper";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import MFileBody from "../../../../../src/components/views/messages/MFileBody.tsx";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../../../src/contexts/RoomContext.ts";
|
||||
import { TimelineRenderingType } from "../../../../../src/contexts/RoomContext.ts";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
jest.mock("matrix-encrypt-attachment", () => ({
|
||||
decryptAttachment: jest.fn(),
|
||||
@@ -72,14 +73,14 @@ describe("<MFileBody/>", () => {
|
||||
|
||||
it("should show a download button in file rendering type", async () => {
|
||||
const { container, getByRole } = render(
|
||||
<RoomContext.Provider value={{ timelineRenderingType: TimelineRenderingType.File } as any}>
|
||||
<ScopedRoomContextProvider {...({ timelineRenderingType: TimelineRenderingType.File } as any)}>
|
||||
<MFileBody
|
||||
{...props}
|
||||
mxEvent={mediaEvent}
|
||||
mediaEventHelper={new MediaEventHelper(mediaEvent)}
|
||||
showGenericPlaceholder={false}
|
||||
/>
|
||||
</RoomContext.Provider>,
|
||||
</ScopedRoomContextProvider>,
|
||||
);
|
||||
|
||||
expect(getByRole("link", { name: "Download" })).toBeInTheDocument();
|
||||
|
||||
@@ -35,6 +35,7 @@ import dispatcher from "../../../../../src/dispatcher/dispatcher";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
import PinningUtils from "../../../../../src/utils/PinningUtils";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
jest.mock("../../../../../src/dispatcher/dispatcher");
|
||||
|
||||
@@ -117,9 +118,9 @@ describe("<MessageActionBar />", () => {
|
||||
} as unknown as IRoomState;
|
||||
const getComponent = (props = {}, roomContext: Partial<IRoomState> = {}) =>
|
||||
render(
|
||||
<RoomContext.Provider value={{ ...defaultRoomContext, ...roomContext }}>
|
||||
<ScopedRoomContextProvider {...defaultRoomContext} {...roomContext}>
|
||||
<MessageActionBar {...defaultProps} {...props} />
|
||||
</RoomContext.Provider>,
|
||||
</ScopedRoomContextProvider>,
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -14,7 +14,6 @@ import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "../../../../../src/voice-broadcast";
|
||||
import { mkEvent, mkRoom, stubClient } from "../../../../test-utils";
|
||||
import MessageEvent from "../../../../../src/components/views/messages/MessageEvent";
|
||||
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
|
||||
@@ -24,10 +23,6 @@ jest.mock("../../../../../src/components/views/messages/UnknownBody", () => ({
|
||||
default: () => <div data-testid="unknown-body" />,
|
||||
}));
|
||||
|
||||
jest.mock("../../../../../src/voice-broadcast/components/VoiceBroadcastBody", () => ({
|
||||
VoiceBroadcastBody: () => <div data-testid="voice-broadcast-body" />,
|
||||
}));
|
||||
|
||||
jest.mock("../../../../../src/components/views/messages/MImageBody", () => ({
|
||||
__esModule: true,
|
||||
default: () => <div data-testid="image-body" />,
|
||||
@@ -81,27 +76,6 @@ describe("MessageEvent", () => {
|
||||
jest.spyOn(SettingsStore, "unwatchSetting").mockImplementation(jest.fn());
|
||||
});
|
||||
|
||||
describe("when a voice broadcast start event occurs", () => {
|
||||
let result: RenderResult;
|
||||
|
||||
beforeEach(() => {
|
||||
event = mkEvent({
|
||||
event: true,
|
||||
type: VoiceBroadcastInfoEventType,
|
||||
user: client.getUserId()!,
|
||||
room: room.roomId,
|
||||
content: {
|
||||
state: VoiceBroadcastInfoState.Started,
|
||||
},
|
||||
});
|
||||
result = renderMessageEvent();
|
||||
});
|
||||
|
||||
it("should render a VoiceBroadcast component", () => {
|
||||
result.getByTestId("voice-broadcast-body");
|
||||
});
|
||||
});
|
||||
|
||||
describe("when an image with a caption is sent", () => {
|
||||
let result: RenderResult;
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@ import {
|
||||
} from "../../../../../src/components/views/messages/RoomPredecessorTile";
|
||||
import { stubClient, upsertRoomStateEvents } from "../../../../test-utils/test-utils";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
import RoomContext from "../../../../../src/contexts/RoomContext";
|
||||
import { filterConsole, getRoomContext } from "../../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
jest.mock("../../../../../src/dispatcher/dispatcher");
|
||||
|
||||
@@ -99,9 +99,9 @@ describe("<RoomPredecessorTile />", () => {
|
||||
expect(createEvent).toBeTruthy();
|
||||
|
||||
return render(
|
||||
<RoomContext.Provider value={getRoomContext(room, {})}>
|
||||
<ScopedRoomContextProvider {...getRoomContext(room, {})}>
|
||||
<RoomPredecessorTile mxEvent={createEvent} />
|
||||
</RoomContext.Provider>,
|
||||
</ScopedRoomContextProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import userEvent from "@testing-library/user-event";
|
||||
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import RoomSummaryCard from "../../../../../src/components/views/right_panel/RoomSummaryCard";
|
||||
import ShareDialog from "../../../../../src/components/views/dialogs/ShareDialog";
|
||||
import { ShareDialog } from "../../../../../src/components/views/dialogs/ShareDialog";
|
||||
import ExportDialog from "../../../../../src/components/views/dialogs/ExportDialog";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import defaultDispatcher from "../../../../../src/dispatcher/dispatcher";
|
||||
@@ -30,7 +30,8 @@ import { _t } from "../../../../../src/languageHandler";
|
||||
import { tagRoom } from "../../../../../src/utils/room/tagRoom";
|
||||
import { DefaultTagID } from "../../../../../src/stores/room-list/models";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../../../src/contexts/RoomContext";
|
||||
import { TimelineRenderingType } from "../../../../../src/contexts/RoomContext";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
jest.mock("../../../../../src/utils/room/tagRoom");
|
||||
|
||||
@@ -172,14 +173,14 @@ describe("<RoomSummaryCard />", () => {
|
||||
const onSearchChange = jest.fn();
|
||||
const { rerender } = render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider value={{ timelineRenderingType: TimelineRenderingType.Search } as any}>
|
||||
<ScopedRoomContextProvider {...({ timelineRenderingType: TimelineRenderingType.Search } as any)}>
|
||||
<RoomSummaryCard
|
||||
room={room}
|
||||
permalinkCreator={new RoomPermalinkCreator(room)}
|
||||
onSearchChange={onSearchChange}
|
||||
focusRoomSearch={true}
|
||||
/>
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
@@ -188,13 +189,13 @@ describe("<RoomSummaryCard />", () => {
|
||||
|
||||
rerender(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider value={{ timelineRenderingType: TimelineRenderingType.Room } as any}>
|
||||
<ScopedRoomContextProvider {...({ timelineRenderingType: TimelineRenderingType.Room } as any)}>
|
||||
<RoomSummaryCard
|
||||
room={room}
|
||||
permalinkCreator={new RoomPermalinkCreator(room)}
|
||||
onSearchChange={onSearchChange}
|
||||
/>
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
expect(screen.getByPlaceholderText("Search messages…")).toHaveValue("");
|
||||
@@ -254,10 +255,7 @@ describe("<RoomSummaryCard />", () => {
|
||||
|
||||
fireEvent.click(getByText("People"));
|
||||
|
||||
expect(RightPanelStore.instance.pushCard).toHaveBeenCalledWith(
|
||||
{ phase: RightPanelPhases.RoomMemberList },
|
||||
true,
|
||||
);
|
||||
expect(RightPanelStore.instance.pushCard).toHaveBeenCalledWith({ phase: RightPanelPhases.MemberList }, true);
|
||||
});
|
||||
|
||||
it("opens room threads list on button click", () => {
|
||||
|
||||
@@ -49,7 +49,7 @@ import ErrorDialog from "../../../../../src/components/views/dialogs/ErrorDialog
|
||||
import { shouldShowComponent } from "../../../../../src/customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../../../src/settings/UIFeature";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
import ShareDialog from "../../../../../src/components/views/dialogs/ShareDialog";
|
||||
import { ShareDialog } from "../../../../../src/components/views/dialogs/ShareDialog";
|
||||
import BulkRedactDialog from "../../../../../src/components/views/dialogs/BulkRedactDialog";
|
||||
|
||||
jest.mock("../../../../../src/utils/direct-messages", () => ({
|
||||
@@ -188,7 +188,7 @@ describe("<UserInfo />", () => {
|
||||
const defaultProps = {
|
||||
user: defaultUser,
|
||||
// idk what is wrong with this type
|
||||
phase: RightPanelPhases.RoomMemberInfo as RightPanelPhases.RoomMemberInfo,
|
||||
phase: RightPanelPhases.MemberInfo as RightPanelPhases.MemberInfo,
|
||||
onClose: jest.fn(),
|
||||
};
|
||||
|
||||
@@ -455,7 +455,7 @@ describe("<UserInfo />", () => {
|
||||
mockCrypto.getUserVerificationStatus.mockResolvedValue(new UserVerificationStatus(false, false, false));
|
||||
|
||||
const { container } = renderComponent({
|
||||
phase: RightPanelPhases.SpaceMemberInfo,
|
||||
phase: RightPanelPhases.MemberInfo,
|
||||
verificationRequest,
|
||||
room: mockRoom,
|
||||
});
|
||||
@@ -649,7 +649,7 @@ describe("<UserInfo />", () => {
|
||||
mockClient.getDomain.mockReturnValue("example.com");
|
||||
|
||||
const { container } = renderComponent({
|
||||
phase: RightPanelPhases.RoomMemberInfo,
|
||||
phase: RightPanelPhases.MemberInfo,
|
||||
room: mockRoom,
|
||||
});
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ exports[`<UserInfo /> with crypto enabled renders <BasicUserInfo /> 1`] = `
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
aria-labelledby=":r6s:"
|
||||
aria-labelledby=":r74:"
|
||||
class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38"
|
||||
data-testid="base-card-close-button"
|
||||
role="button"
|
||||
@@ -402,7 +402,7 @@ exports[`<UserInfo /> with crypto enabled should render a deactivate button for
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
aria-labelledby=":r92:"
|
||||
aria-labelledby=":r9a:"
|
||||
class="_icon-button_bh2qc_17 _subtle-bg_bh2qc_38"
|
||||
data-testid="base-card-close-button"
|
||||
role="button"
|
||||
|
||||
@@ -27,12 +27,12 @@ import {
|
||||
import DocumentOffset from "../../../../../src/editor/offset";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import EditorStateTransfer from "../../../../../src/utils/EditorStateTransfer";
|
||||
import RoomContext from "../../../../../src/contexts/RoomContext";
|
||||
import { IRoomState } from "../../../../../src/components/structures/RoomView";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import Autocompleter, { IProviderCompletions } from "../../../../../src/autocomplete/Autocompleter";
|
||||
import NotifProvider from "../../../../../src/autocomplete/NotifProvider";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
describe("<EditMessageComposer/>", () => {
|
||||
const userId = "@alice:server.org";
|
||||
@@ -79,7 +79,7 @@ describe("<EditMessageComposer/>", () => {
|
||||
render(<EditMessageComposerWithMatrixClient editState={editState} />, {
|
||||
wrapper: ({ children }) => (
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider value={roomContext}>{children}</RoomContext.Provider>
|
||||
<ScopedRoomContextProvider {...roomContext}>{children}</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>
|
||||
),
|
||||
});
|
||||
@@ -128,7 +128,7 @@ describe("<EditMessageComposer/>", () => {
|
||||
|
||||
const expectedBody = {
|
||||
...editedEvent.getContent(),
|
||||
"body": " * original message + edit",
|
||||
"body": "* original message + edit",
|
||||
"m.new_content": {
|
||||
"body": "original message + edit",
|
||||
"msgtype": "m.text",
|
||||
@@ -160,7 +160,7 @@ describe("<EditMessageComposer/>", () => {
|
||||
const content = createEditContent(model, editedEvent);
|
||||
|
||||
expect(content).toEqual({
|
||||
"body": " * hello world",
|
||||
"body": "* hello world",
|
||||
"msgtype": "m.text",
|
||||
"m.new_content": {
|
||||
"body": "hello world",
|
||||
@@ -183,10 +183,10 @@ describe("<EditMessageComposer/>", () => {
|
||||
const content = createEditContent(model, editedEvent);
|
||||
|
||||
expect(content).toEqual({
|
||||
"body": " * hello *world*",
|
||||
"body": "* hello *world*",
|
||||
"msgtype": "m.text",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": " * hello <em>world</em>",
|
||||
"formatted_body": "* hello <em>world</em>",
|
||||
"m.new_content": {
|
||||
"body": "hello *world*",
|
||||
"msgtype": "m.text",
|
||||
@@ -210,10 +210,10 @@ describe("<EditMessageComposer/>", () => {
|
||||
const content = createEditContent(model, editedEvent);
|
||||
|
||||
expect(content).toEqual({
|
||||
"body": " * blinks __quickly__",
|
||||
"body": "* blinks __quickly__",
|
||||
"msgtype": "m.emote",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": " * blinks <strong>quickly</strong>",
|
||||
"formatted_body": "* blinks <strong>quickly</strong>",
|
||||
"m.new_content": {
|
||||
"body": "blinks __quickly__",
|
||||
"msgtype": "m.emote",
|
||||
@@ -238,7 +238,7 @@ describe("<EditMessageComposer/>", () => {
|
||||
const content = createEditContent(model, editedEvent);
|
||||
|
||||
expect(content).toEqual({
|
||||
"body": " * ✨sparkles✨",
|
||||
"body": "* ✨sparkles✨",
|
||||
"msgtype": "m.emote",
|
||||
"m.new_content": {
|
||||
"body": "✨sparkles✨",
|
||||
@@ -264,7 +264,7 @@ describe("<EditMessageComposer/>", () => {
|
||||
// TODO Edits do not properly strip the double slash used to skip
|
||||
// command processing.
|
||||
expect(content).toEqual({
|
||||
"body": " * //dev/null is my favourite place",
|
||||
"body": "* //dev/null is my favourite place",
|
||||
"msgtype": "m.text",
|
||||
"m.new_content": {
|
||||
"body": "//dev/null is my favourite place",
|
||||
|
||||
@@ -30,7 +30,7 @@ import { mkEncryptedMatrixEvent } from "matrix-js-sdk/src/testing";
|
||||
|
||||
import EventTile, { EventTileProps } from "../../../../../src/components/views/rooms/EventTile";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../../../src/contexts/RoomContext";
|
||||
import { TimelineRenderingType } from "../../../../../src/contexts/RoomContext";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import { filterConsole, flushPromises, getRoomContext, mkEvent, mkMessage, stubClient } from "../../../../test-utils";
|
||||
import { mkThread } from "../../../../test-utils/threads";
|
||||
@@ -40,6 +40,7 @@ import { Action } from "../../../../../src/dispatcher/actions";
|
||||
import { IRoomState } from "../../../../../src/components/structures/RoomView";
|
||||
import PinningUtils from "../../../../../src/utils/PinningUtils";
|
||||
import { Layout } from "../../../../../src/settings/enums/Layout";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
describe("EventTile", () => {
|
||||
const ROOM_ID = "!roomId:example.org";
|
||||
@@ -56,13 +57,13 @@ describe("EventTile", () => {
|
||||
}) {
|
||||
return (
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<RoomContext.Provider value={props.roomContext}>
|
||||
<ScopedRoomContextProvider {...props.roomContext}>
|
||||
<EventTile
|
||||
mxEvent={mxEvent}
|
||||
replacingEventId={mxEvent.replacingEventId()}
|
||||
{...(props.eventTilePropertyOverrides ?? {})}
|
||||
/>
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>
|
||||
);
|
||||
}
|
||||
@@ -70,9 +71,11 @@ describe("EventTile", () => {
|
||||
function getComponent(
|
||||
overrides: Partial<EventTileProps> = {},
|
||||
renderingType: TimelineRenderingType = TimelineRenderingType.Room,
|
||||
roomContext: Partial<IRoomState> = {},
|
||||
) {
|
||||
const context = getRoomContext(room, {
|
||||
timelineRenderingType: renderingType,
|
||||
...roomContext,
|
||||
});
|
||||
return render(<WrappedEventTile roomContext={context} eventTilePropertyOverrides={overrides} />);
|
||||
}
|
||||
@@ -301,6 +304,8 @@ describe("EventTile", () => {
|
||||
[EventShieldReason.UNKNOWN_DEVICE, "unknown or deleted device"],
|
||||
[EventShieldReason.AUTHENTICITY_NOT_GUARANTEED, "can't be guaranteed"],
|
||||
[EventShieldReason.MISMATCHED_SENDER_KEY, "Encrypted by an unverified session"],
|
||||
[EventShieldReason.SENT_IN_CLEAR, "Not encrypted"],
|
||||
[EventShieldReason.VERIFICATION_VIOLATION, "Sender's verified identity has changed"],
|
||||
])("shows the correct reason code for %i (%s)", async (reasonCode: EventShieldReason, expectedText: string) => {
|
||||
mxEvent = await mkEncryptedMatrixEvent({
|
||||
plainContent: { msgtype: "m.text", body: "msg1" },
|
||||
@@ -434,8 +439,6 @@ describe("EventTile", () => {
|
||||
});
|
||||
|
||||
it("should update the warning when the event is replaced with an unencrypted one", async () => {
|
||||
jest.spyOn(client, "isRoomEncrypted").mockReturnValue(true);
|
||||
|
||||
// we start out with an event from the trusted device
|
||||
mxEvent = await mkEncryptedMatrixEvent({
|
||||
plainContent: { msgtype: "m.text", body: "msg1" },
|
||||
@@ -449,7 +452,7 @@ describe("EventTile", () => {
|
||||
shieldReason: null,
|
||||
} as EventEncryptionInfo);
|
||||
|
||||
const roomContext = getRoomContext(room, {});
|
||||
const roomContext = getRoomContext(room, { isRoomEncrypted: true });
|
||||
const { container, rerender } = render(<WrappedEventTile roomContext={roomContext} />);
|
||||
await flushPromises();
|
||||
|
||||
@@ -578,4 +581,28 @@ describe("EventTile", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should display the not encrypted status for an unencrypted event when the room becomes encrypted", async () => {
|
||||
jest.spyOn(client.getCrypto()!, "getEncryptionInfoForEvent").mockResolvedValue({
|
||||
shieldColour: EventShieldColour.NONE,
|
||||
shieldReason: null,
|
||||
});
|
||||
|
||||
const { rerender } = getComponent();
|
||||
await flushPromises();
|
||||
// The room and the event are unencrypted, the tile should not show the not encrypted status
|
||||
expect(screen.queryByText("Not encrypted")).toBeNull();
|
||||
|
||||
// The room is now encrypted
|
||||
rerender(
|
||||
<WrappedEventTile
|
||||
roomContext={getRoomContext(room, {
|
||||
isRoomEncrypted: true,
|
||||
})}
|
||||
/>,
|
||||
);
|
||||
|
||||
// The event tile should now show the not encrypted status
|
||||
await waitFor(() => expect(screen.getByText("Not encrypted")).toBeInTheDocument());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import * as React from "react";
|
||||
import { EventType, MatrixEvent, Room, RoomMember, THREAD_RELATION_TYPE } from "matrix-js-sdk/src/matrix";
|
||||
import { EventType, MatrixEvent, RoomMember, THREAD_RELATION_TYPE } from "matrix-js-sdk/src/matrix";
|
||||
import { act, fireEvent, render, screen, waitFor } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
@@ -19,17 +19,14 @@ import {
|
||||
mkStubRoom,
|
||||
mockPlatformPeg,
|
||||
stubClient,
|
||||
waitEnoughCyclesForModal,
|
||||
} from "../../../../test-utils";
|
||||
import MessageComposer from "../../../../../src/components/views/rooms/MessageComposer";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import RoomContext from "../../../../../src/contexts/RoomContext";
|
||||
import { IRoomState } from "../../../../../src/components/structures/RoomView";
|
||||
import ResizeNotifier from "../../../../../src/utils/ResizeNotifier";
|
||||
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
|
||||
import { LocalRoom } from "../../../../../src/models/LocalRoom";
|
||||
import { Features } from "../../../../../src/settings/Settings";
|
||||
import SettingsStore from "../../../../../src/settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../../src/settings/SettingLevel";
|
||||
import dis from "../../../../../src/dispatcher/dispatcher";
|
||||
@@ -37,9 +34,7 @@ import { E2EStatus } from "../../../../../src/utils/ShieldUtils";
|
||||
import { addTextToComposerRTL } from "../../../../test-utils/composer";
|
||||
import UIStore, { UI_EVENTS } from "../../../../../src/stores/UIStore";
|
||||
import { Action } from "../../../../../src/dispatcher/actions";
|
||||
import { VoiceBroadcastInfoState, VoiceBroadcastRecording } from "../../../../../src/voice-broadcast";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils";
|
||||
import { SdkContextClass } from "../../../../../src/contexts/SDKContext";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
const openStickerPicker = async (): Promise<void> => {
|
||||
await userEvent.click(screen.getByLabelText("More options"));
|
||||
@@ -51,15 +46,6 @@ const startVoiceMessage = async (): Promise<void> => {
|
||||
await userEvent.click(screen.getByLabelText("Voice Message"));
|
||||
};
|
||||
|
||||
const setCurrentBroadcastRecording = (room: Room, state: VoiceBroadcastInfoState): void => {
|
||||
const recording = new VoiceBroadcastRecording(
|
||||
mkVoiceBroadcastInfoStateEvent(room.roomId, state, "@user:example.com", "ABC123"),
|
||||
MatrixClientPeg.safeGet(),
|
||||
state,
|
||||
);
|
||||
act(() => SdkContextClass.instance.voiceBroadcastRecordingsStore.setCurrent(recording));
|
||||
};
|
||||
|
||||
const expectVoiceMessageRecordingTriggered = (): void => {
|
||||
// Checking for the voice message dialog text, if no mic can be found.
|
||||
// By this we know at least that starting a voice message was triggered.
|
||||
@@ -78,14 +64,11 @@ describe("MessageComposer", () => {
|
||||
await clearAllModals();
|
||||
jest.useRealTimers();
|
||||
|
||||
SdkContextClass.instance.voiceBroadcastRecordingsStore.clearCurrent();
|
||||
|
||||
// restore settings
|
||||
act(() => {
|
||||
[
|
||||
"MessageComposerInput.showStickersButton",
|
||||
"MessageComposerInput.showPollsButton",
|
||||
Features.VoiceBroadcast,
|
||||
"feature_wysiwyg_composer",
|
||||
].forEach((setting: string): void => {
|
||||
SettingsStore.setValue(setting, null, SettingLevel.DEVICE, SettingsStore.getDefaultValue(setting));
|
||||
@@ -212,10 +195,6 @@ describe("MessageComposer", () => {
|
||||
setting: "MessageComposerInput.showPollsButton",
|
||||
buttonLabel: "Poll",
|
||||
},
|
||||
{
|
||||
setting: Features.VoiceBroadcast,
|
||||
buttonLabel: "Voice broadcast",
|
||||
},
|
||||
].forEach(({ setting, buttonLabel }) => {
|
||||
[true, false].forEach((value: boolean) => {
|
||||
describe(`when ${setting} = ${value}`, () => {
|
||||
@@ -437,34 +416,6 @@ describe("MessageComposer", () => {
|
||||
expectVoiceMessageRecordingTriggered();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when recording a voice broadcast and trying to start a voice message", () => {
|
||||
beforeEach(async () => {
|
||||
setCurrentBroadcastRecording(room, VoiceBroadcastInfoState.Started);
|
||||
wrapAndRender({ room });
|
||||
await startVoiceMessage();
|
||||
await waitEnoughCyclesForModal();
|
||||
});
|
||||
|
||||
it("should not start a voice message and display the info dialog", async () => {
|
||||
expect(screen.queryByLabelText("Stop recording")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("Can't start voice message")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when there is a stopped voice broadcast recording and trying to start a voice message", () => {
|
||||
beforeEach(async () => {
|
||||
setCurrentBroadcastRecording(room, VoiceBroadcastInfoState.Stopped);
|
||||
wrapAndRender({ room });
|
||||
await startVoiceMessage();
|
||||
await waitEnoughCyclesForModal();
|
||||
});
|
||||
|
||||
it("should try to start a voice message and should not display the info dialog", async () => {
|
||||
expect(screen.queryByText("Can't start voice message")).not.toBeInTheDocument();
|
||||
expectVoiceMessageRecordingTriggered();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("for a LocalRoom", () => {
|
||||
@@ -512,9 +463,9 @@ function wrapAndRender(
|
||||
|
||||
const getRawComponent = (props = {}, context = roomContext, client = mockClient) => (
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<RoomContext.Provider value={context}>
|
||||
<ScopedRoomContextProvider {...context}>
|
||||
<MessageComposer {...defaultProps} {...props} />
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>
|
||||
);
|
||||
return {
|
||||
|
||||
@@ -10,11 +10,11 @@ import React from "react";
|
||||
import { render, screen, waitFor } from "jest-matrix-react";
|
||||
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import RoomContext from "../../../../../src/contexts/RoomContext";
|
||||
import { createTestClient, getRoomContext, mkStubRoom } from "../../../../test-utils";
|
||||
import { IRoomState } from "../../../../../src/components/structures/RoomView";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import MessageComposerButtons from "../../../../../src/components/views/rooms/MessageComposerButtons";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
describe("MessageComposerButtons", () => {
|
||||
// @ts-ignore - we're deliberately not implementing the whole interface here, but
|
||||
@@ -54,7 +54,7 @@ describe("MessageComposerButtons", () => {
|
||||
|
||||
return render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider value={defaultRoomContext}>{component}</RoomContext.Provider>
|
||||
<ScopedRoomContextProvider {...defaultRoomContext}>{component}</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
}
|
||||
@@ -168,27 +168,4 @@ describe("MessageComposerButtons", () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with showVoiceBroadcastButton = true", () => {
|
||||
it("should render the »Voice broadcast« button", () => {
|
||||
wrapAndRender(
|
||||
<MessageComposerButtons
|
||||
{...mockProps}
|
||||
isMenuOpen={true}
|
||||
showLocationButton={true}
|
||||
showPollsButton={true}
|
||||
showStickersButton={true}
|
||||
showVoiceBroadcastButton={true}
|
||||
/>,
|
||||
false,
|
||||
);
|
||||
|
||||
expect(getButtonLabels()).toEqual([
|
||||
"Emoji",
|
||||
"Attachment",
|
||||
"More options",
|
||||
["Sticker", "Voice Message", "Voice broadcast", "Poll", "Location"],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,19 +13,19 @@ import { MatrixClient, Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { LocalRoom } from "../../../../../src/models/LocalRoom";
|
||||
import { filterConsole, mkRoomMemberJoinEvent, mkThirdPartyInviteEvent, stubClient } from "../../../../test-utils";
|
||||
import RoomContext from "../../../../../src/contexts/RoomContext";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import NewRoomIntro from "../../../../../src/components/views/rooms/NewRoomIntro";
|
||||
import { IRoomState } from "../../../../../src/components/structures/RoomView";
|
||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import { DirectoryMember } from "../../../../../src/utils/direct-messages";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
const renderNewRoomIntro = (client: MatrixClient, room: Room | LocalRoom) => {
|
||||
render(
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<RoomContext.Provider value={{ room, roomId: room.roomId } as unknown as IRoomState}>
|
||||
<ScopedRoomContextProvider {...({ room, roomId: room.roomId } as unknown as IRoomState)}>
|
||||
<NewRoomIntro />
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -165,7 +165,7 @@ describe("UnreadNotificationBadge", () => {
|
||||
},
|
||||
ts: 5,
|
||||
});
|
||||
room.addLiveEvents([event]);
|
||||
room.addLiveEvents([event], { addToState: true });
|
||||
|
||||
const { container } = render(getComponent(THREAD_ID));
|
||||
expect(container.querySelector(".mx_NotificationBadge_dot")).toBeTruthy();
|
||||
|
||||
@@ -224,7 +224,7 @@ describe("<PinnedMessageBanner />", () => {
|
||||
// The Right panel is opened on another card
|
||||
jest.spyOn(RightPanelStore.instance, "isOpenForRoom").mockReturnValue(true);
|
||||
jest.spyOn(RightPanelStore.instance, "currentCard", "get").mockReturnValue({
|
||||
phase: RightPanelPhases.RoomMemberList,
|
||||
phase: RightPanelPhases.MemberList,
|
||||
});
|
||||
|
||||
renderBanner();
|
||||
|
||||
@@ -158,7 +158,7 @@ describe("RoomHeader", () => {
|
||||
|
||||
fireEvent.click(facePile);
|
||||
|
||||
expect(setCardSpy).toHaveBeenCalledWith({ phase: RightPanelPhases.RoomMemberList });
|
||||
expect(setCardSpy).toHaveBeenCalledWith({ phase: RightPanelPhases.MemberList });
|
||||
});
|
||||
|
||||
it("has room info icon that opens the room info panel", async () => {
|
||||
@@ -589,7 +589,7 @@ describe("RoomHeader", () => {
|
||||
state_key: "",
|
||||
room_id: room.roomId,
|
||||
});
|
||||
room.addLiveEvents([joinRuleEvent]);
|
||||
room.addLiveEvents([joinRuleEvent], { addToState: true });
|
||||
|
||||
render(<RoomHeader room={room} />, getWrapper());
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
} from "../../../../../../src/components/views/rooms/RoomHeader/CallGuestLinkButton";
|
||||
import Modal from "../../../../../../src/Modal";
|
||||
import SdkConfig from "../../../../../../src/SdkConfig";
|
||||
import ShareDialog from "../../../../../../src/components/views/dialogs/ShareDialog";
|
||||
import { ShareDialog } from "../../../../../../src/components/views/dialogs/ShareDialog";
|
||||
import { _t } from "../../../../../../src/languageHandler";
|
||||
import SettingsStore from "../../../../../../src/settings/SettingsStore";
|
||||
|
||||
|
||||
@@ -9,14 +9,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
import React from "react";
|
||||
import { render, screen, act, RenderResult } from "jest-matrix-react";
|
||||
import { mocked, Mocked } from "jest-mock";
|
||||
import {
|
||||
MatrixClient,
|
||||
PendingEventOrdering,
|
||||
Room,
|
||||
MatrixEvent,
|
||||
RoomStateEvent,
|
||||
Thread,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { MatrixClient, PendingEventOrdering, Room, RoomStateEvent, Thread } from "matrix-js-sdk/src/matrix";
|
||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||
import { Widget } from "matrix-widget-api";
|
||||
|
||||
@@ -40,8 +33,6 @@ import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||
import PlatformPeg from "../../../../../src/PlatformPeg";
|
||||
import BasePlatform from "../../../../../src/BasePlatform";
|
||||
import { WidgetMessagingStore } from "../../../../../src/stores/widgets/WidgetMessagingStore";
|
||||
import { VoiceBroadcastInfoState } from "../../../../../src/voice-broadcast";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils";
|
||||
import { TestSdkContext } from "../../../TestSdkContext";
|
||||
import { SDKContext } from "../../../../../src/contexts/SDKContext";
|
||||
import { shouldShowComponent } from "../../../../../src/customisations/helpers/UIComponents";
|
||||
@@ -61,20 +52,6 @@ describe("RoomTile", () => {
|
||||
} as unknown as BasePlatform);
|
||||
useMockedCalls();
|
||||
|
||||
const setUpVoiceBroadcast = async (state: VoiceBroadcastInfoState): Promise<void> => {
|
||||
voiceBroadcastInfoEvent = mkVoiceBroadcastInfoStateEvent(
|
||||
room.roomId,
|
||||
state,
|
||||
client.getSafeUserId(),
|
||||
client.getDeviceId()!,
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
room.currentState.setStateEvents([voiceBroadcastInfoEvent]);
|
||||
await flushPromises();
|
||||
});
|
||||
};
|
||||
|
||||
const renderRoomTile = (): RenderResult => {
|
||||
return render(
|
||||
<SDKContext.Provider value={sdkContext}>
|
||||
@@ -89,7 +66,6 @@ describe("RoomTile", () => {
|
||||
};
|
||||
|
||||
let client: Mocked<MatrixClient>;
|
||||
let voiceBroadcastInfoEvent: MatrixEvent;
|
||||
let room: Room;
|
||||
let sdkContext: TestSdkContext;
|
||||
let showMessagePreview = false;
|
||||
@@ -303,49 +279,6 @@ describe("RoomTile", () => {
|
||||
});
|
||||
expect(screen.queryByLabelText(/participant/)).toBe(null);
|
||||
});
|
||||
|
||||
describe("and a live broadcast starts", () => {
|
||||
beforeEach(async () => {
|
||||
renderRoomTile();
|
||||
await setUpVoiceBroadcast(VoiceBroadcastInfoState.Started);
|
||||
});
|
||||
|
||||
it("should still render the call subtitle", () => {
|
||||
expect(screen.queryByText("Video")).toBeInTheDocument();
|
||||
expect(screen.queryByText("Live")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when a live voice broadcast starts", () => {
|
||||
beforeEach(async () => {
|
||||
renderRoomTile();
|
||||
await setUpVoiceBroadcast(VoiceBroadcastInfoState.Started);
|
||||
});
|
||||
|
||||
it("should render the »Live« subtitle", () => {
|
||||
expect(screen.queryByText("Live")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe("and the broadcast stops", () => {
|
||||
beforeEach(async () => {
|
||||
const stopEvent = mkVoiceBroadcastInfoStateEvent(
|
||||
room.roomId,
|
||||
VoiceBroadcastInfoState.Stopped,
|
||||
client.getSafeUserId(),
|
||||
client.getDeviceId()!,
|
||||
voiceBroadcastInfoEvent,
|
||||
);
|
||||
await act(async () => {
|
||||
room.currentState.setStateEvents([stopEvent]);
|
||||
await flushPromises();
|
||||
});
|
||||
});
|
||||
|
||||
it("should not render the »Live« subtitle", () => {
|
||||
expect(screen.queryByText("Live")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import SendMessageComposer, {
|
||||
isQuickReaction,
|
||||
} from "../../../../../src/components/views/rooms/SendMessageComposer";
|
||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||
import RoomContext, { TimelineRenderingType } from "../../../../../src/contexts/RoomContext";
|
||||
import { TimelineRenderingType } from "../../../../../src/contexts/RoomContext";
|
||||
import EditorModel from "../../../../../src/editor/model";
|
||||
import { createPartCreator } from "../../../editor/mock";
|
||||
import { createTestClient, mkEvent, mkStubRoom, stubClient } from "../../../../test-utils";
|
||||
@@ -30,6 +30,7 @@ import { IRoomState, MainSplitContentType } from "../../../../../src/components/
|
||||
import { mockPlatformPeg } from "../../../../test-utils/platform";
|
||||
import { doMaybeLocalRoomAction } from "../../../../../src/utils/local-room";
|
||||
import { addTextToComposer } from "../../../../test-utils/composer";
|
||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
jest.mock("../../../../../src/utils/local-room", () => ({
|
||||
doMaybeLocalRoomAction: jest.fn(),
|
||||
@@ -77,6 +78,7 @@ describe("<SendMessageComposer/>", () => {
|
||||
canAskToJoin: false,
|
||||
promptAskToJoin: false,
|
||||
viewRoomOpts: { buttons: [] },
|
||||
isRoomEncrypted: false,
|
||||
};
|
||||
describe("createMessageContent", () => {
|
||||
it("sends plaintext messages correctly", () => {
|
||||
@@ -364,9 +366,9 @@ describe("<SendMessageComposer/>", () => {
|
||||
};
|
||||
const getRawComponent = (props = {}, roomContext = defaultRoomContext, client = mockClient) => (
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<RoomContext.Provider value={roomContext}>
|
||||
<ScopedRoomContextProvider {...roomContext}>
|
||||
<SendMessageComposer {...defaultProps} {...props} />
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>
|
||||
);
|
||||
const getComponent = (props = {}, roomContext = defaultRoomContext, client = mockClient) => {
|
||||
|
||||
@@ -47,7 +47,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
||||
>
|
||||
<button
|
||||
aria-labelledby=":r154:"
|
||||
aria-labelledby=":r16c:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -73,7 +73,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
|
||||
<button
|
||||
aria-disabled="true"
|
||||
aria-label="There's no one here to call"
|
||||
aria-labelledby=":r159:"
|
||||
aria-labelledby=":r16h:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -98,7 +98,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
|
||||
</button>
|
||||
<button
|
||||
aria-label="Room info"
|
||||
aria-labelledby=":r15e:"
|
||||
aria-labelledby=":r16m:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
@@ -123,7 +123,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = `
|
||||
</button>
|
||||
<button
|
||||
aria-label="Threads"
|
||||
aria-labelledby=":r15j:"
|
||||
aria-labelledby=":r16r:"
|
||||
class="_icon-button_bh2qc_17"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 32px;"
|
||||
|
||||
@@ -11,7 +11,6 @@ import React from "react";
|
||||
import { fireEvent, render, screen, waitFor } from "jest-matrix-react";
|
||||
|
||||
import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext";
|
||||
import RoomContext from "../../../../../../src/contexts/RoomContext";
|
||||
import defaultDispatcher from "../../../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../../../src/dispatcher/actions";
|
||||
import { flushPromises, mkEvent } from "../../../../../test-utils";
|
||||
@@ -23,6 +22,7 @@ import { ComposerInsertPayload, ComposerType } from "../../../../../../src/dispa
|
||||
import { ActionPayload } from "../../../../../../src/dispatcher/payloads";
|
||||
import * as EmojiButton from "../../../../../../src/components/views/rooms/EmojiButton";
|
||||
import { createMocks } from "./utils";
|
||||
import { ScopedRoomContextProvider } from "../../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
describe("EditWysiwygComposer", () => {
|
||||
afterEach(() => {
|
||||
@@ -39,9 +39,9 @@ describe("EditWysiwygComposer", () => {
|
||||
) => {
|
||||
return render(
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<RoomContext.Provider value={roomContext}>
|
||||
<ScopedRoomContextProvider {...roomContext}>
|
||||
<EditWysiwygComposer disabled={disabled} editorStateTransfer={_editorStateTransfer} />
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
};
|
||||
@@ -64,9 +64,9 @@ describe("EditWysiwygComposer", () => {
|
||||
|
||||
rerender(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider value={{ ...defaultRoomContext, room: undefined }}>
|
||||
<ScopedRoomContextProvider {...defaultRoomContext} room={undefined}>
|
||||
<EditWysiwygComposer disabled={false} editorStateTransfer={editorStateTransfer} />
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
@@ -196,9 +196,9 @@ describe("EditWysiwygComposer", () => {
|
||||
// Then
|
||||
screen.getByText("Save").click();
|
||||
const expectedContent = {
|
||||
"body": ` * foo bar`,
|
||||
"body": `* foo bar`,
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": ` * foo bar`,
|
||||
"formatted_body": `* foo bar`,
|
||||
"m.new_content": {
|
||||
body: "foo bar",
|
||||
format: "org.matrix.custom.html",
|
||||
@@ -275,10 +275,10 @@ describe("EditWysiwygComposer", () => {
|
||||
);
|
||||
render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider value={defaultRoomContext}>
|
||||
<ScopedRoomContextProvider {...defaultRoomContext}>
|
||||
<EditWysiwygComposer editorStateTransfer={editorStateTransfer} />
|
||||
<Emoji menuPosition={{ chevronFace: ChevronFace.Top }} />
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
// Same behavior as in RoomView.tsx
|
||||
|
||||
@@ -11,7 +11,6 @@ import React from "react";
|
||||
import { act, fireEvent, render, screen, waitFor } from "jest-matrix-react";
|
||||
|
||||
import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext";
|
||||
import RoomContext from "../../../../../../src/contexts/RoomContext";
|
||||
import defaultDispatcher from "../../../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../../../src/dispatcher/actions";
|
||||
import { flushPromises } from "../../../../../test-utils";
|
||||
@@ -20,6 +19,7 @@ import { aboveLeftOf } from "../../../../../../src/components/structures/Context
|
||||
import { ComposerInsertPayload, ComposerType } from "../../../../../../src/dispatcher/payloads/ComposerInsertPayload";
|
||||
import { setSelection } from "../../../../../../src/components/views/rooms/wysiwyg_composer/utils/selection";
|
||||
import { createMocks } from "./utils";
|
||||
import { ScopedRoomContextProvider } from "../../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
jest.mock("../../../../../../src/components/views/rooms/EmojiButton", () => ({
|
||||
EmojiButton: ({ addEmoji }: { addEmoji: (emoji: string) => void }) => {
|
||||
@@ -66,7 +66,7 @@ describe("SendWysiwygComposer", () => {
|
||||
) => {
|
||||
return render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider value={defaultRoomContext}>
|
||||
<ScopedRoomContextProvider {...defaultRoomContext}>
|
||||
<SendWysiwygComposer
|
||||
onChange={onChange}
|
||||
onSend={onSend}
|
||||
@@ -75,7 +75,7 @@ describe("SendWysiwygComposer", () => {
|
||||
menuPosition={aboveLeftOf({ top: 0, bottom: 0, right: 0 })}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ import { PlainTextComposer } from "../../../../../../../src/components/views/roo
|
||||
import * as mockUseSettingsHook from "../../../../../../../src/hooks/useSettings";
|
||||
import * as mockKeyboard from "../../../../../../../src/Keyboard";
|
||||
import { createMocks } from "../utils";
|
||||
import RoomContext from "../../../../../../../src/contexts/RoomContext";
|
||||
import { ScopedRoomContextProvider } from "../../../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
describe("PlainTextComposer", () => {
|
||||
const customRender = (
|
||||
@@ -275,9 +275,9 @@ describe("PlainTextComposer", () => {
|
||||
const { defaultRoomContext } = createMocks();
|
||||
|
||||
render(
|
||||
<RoomContext.Provider value={defaultRoomContext}>
|
||||
<ScopedRoomContextProvider {...defaultRoomContext}>
|
||||
<PlainTextComposer onChange={jest.fn()} onSend={jest.fn()} disabled={false} initialContent="" />
|
||||
</RoomContext.Provider>,
|
||||
</ScopedRoomContextProvider>,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId("autocomplete-wrapper")).toBeInTheDocument();
|
||||
|
||||
@@ -11,12 +11,12 @@ import React, { createRef } from "react";
|
||||
import { render, screen, waitFor } from "jest-matrix-react";
|
||||
|
||||
import MatrixClientContext from "../../../../../../../src/contexts/MatrixClientContext";
|
||||
import RoomContext from "../../../../../../../src/contexts/RoomContext";
|
||||
import { WysiwygAutocomplete } from "../../../../../../../src/components/views/rooms/wysiwyg_composer/components/WysiwygAutocomplete";
|
||||
import { getRoomContext, mkStubRoom, stubClient } from "../../../../../../test-utils";
|
||||
import Autocomplete from "../../../../../../../src/components/views/rooms/Autocomplete";
|
||||
import Autocompleter, { ICompletion } from "../../../../../../../src/autocomplete/Autocompleter";
|
||||
import AutocompleteProvider from "../../../../../../../src/autocomplete/AutocompleteProvider";
|
||||
import { ScopedRoomContextProvider } from "../../../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
const mockCompletion: ICompletion[] = [
|
||||
{
|
||||
@@ -71,7 +71,7 @@ describe("WysiwygAutocomplete", () => {
|
||||
|
||||
return render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider value={mockRoomContext}>
|
||||
<ScopedRoomContextProvider {...mockRoomContext}>
|
||||
<WysiwygAutocomplete
|
||||
ref={autocompleteRef}
|
||||
suggestion={null}
|
||||
@@ -80,7 +80,7 @@ describe("WysiwygAutocomplete", () => {
|
||||
handleAtRoomMention={mockHandleAtRoomMention}
|
||||
{...props}
|
||||
/>
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -18,7 +18,6 @@ import defaultDispatcher from "../../../../../../../src/dispatcher/dispatcher";
|
||||
import * as EventUtils from "../../../../../../../src/utils/EventUtils";
|
||||
import { Action } from "../../../../../../../src/dispatcher/actions";
|
||||
import MatrixClientContext from "../../../../../../../src/contexts/MatrixClientContext";
|
||||
import RoomContext from "../../../../../../../src/contexts/RoomContext";
|
||||
import {
|
||||
ComposerContext,
|
||||
getDefaultContextValue,
|
||||
@@ -32,20 +31,21 @@ import Autocompleter, { ICompletion } from "../../../../../../../src/autocomplet
|
||||
import AutocompleteProvider from "../../../../../../../src/autocomplete/AutocompleteProvider";
|
||||
import * as Permalinks from "../../../../../../../src/utils/permalinks/Permalinks";
|
||||
import { PermalinkParts } from "../../../../../../../src/utils/permalinks/PermalinkConstructor";
|
||||
import { ScopedRoomContextProvider } from "../../../../../../../src/contexts/ScopedRoomContext.tsx";
|
||||
|
||||
describe("WysiwygComposer", () => {
|
||||
const customRender = (onChange = jest.fn(), onSend = jest.fn(), disabled = false, initialContent?: string) => {
|
||||
const { mockClient, defaultRoomContext } = createMocks();
|
||||
return render(
|
||||
<MatrixClientContext.Provider value={mockClient}>
|
||||
<RoomContext.Provider value={defaultRoomContext}>
|
||||
<ScopedRoomContextProvider {...defaultRoomContext}>
|
||||
<WysiwygComposer
|
||||
onChange={onChange}
|
||||
onSend={onSend}
|
||||
disabled={disabled}
|
||||
initialContent={initialContent}
|
||||
/>
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
};
|
||||
@@ -523,7 +523,7 @@ describe("WysiwygComposer", () => {
|
||||
) => {
|
||||
return render(
|
||||
<MatrixClientContext.Provider value={client}>
|
||||
<RoomContext.Provider value={roomContext}>
|
||||
<ScopedRoomContextProvider {...roomContext}>
|
||||
<ComposerContext.Provider
|
||||
value={getDefaultContextValue({ editorStateTransfer: _editorStateTransfer })}
|
||||
>
|
||||
@@ -537,7 +537,7 @@ describe("WysiwygComposer", () => {
|
||||
}
|
||||
/>
|
||||
</ComposerContext.Provider>
|
||||
</RoomContext.Provider>
|
||||
</ScopedRoomContextProvider>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -88,9 +88,9 @@ describe("createMessageContent", () => {
|
||||
|
||||
// Then
|
||||
expect(content).toEqual({
|
||||
"body": " * *__hello__ world*",
|
||||
"body": "* *__hello__ world*",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": ` * ${message}`,
|
||||
"formatted_body": `* ${message}`,
|
||||
"msgtype": "m.text",
|
||||
"m.new_content": {
|
||||
body: "*__hello__ world*",
|
||||
|
||||
@@ -418,8 +418,8 @@ describe("message", () => {
|
||||
// Then
|
||||
const { msgtype, format } = mockEvent.getContent();
|
||||
const expectedContent = {
|
||||
"body": ` * ${newMessage}`,
|
||||
"formatted_body": ` * ${newMessage}`,
|
||||
"body": `* ${newMessage}`,
|
||||
"formatted_body": `* ${newMessage}`,
|
||||
"m.new_content": {
|
||||
body: "Replying to this new content",
|
||||
format: "org.matrix.custom.html",
|
||||
|
||||
@@ -915,7 +915,7 @@ describe("<Notifications />", () => {
|
||||
user: "@alice:example.org",
|
||||
ts: 1,
|
||||
});
|
||||
await room.addLiveEvents([message]);
|
||||
await room.addLiveEvents([message], { addToState: true });
|
||||
|
||||
const { container } = await getComponentAndWait();
|
||||
const clearNotificationEl = getByTestId(container, "clear-notifications");
|
||||
|
||||
@@ -56,7 +56,6 @@ describe("<LoginWithQRSection />", () => {
|
||||
const defaultProps = {
|
||||
onShowQr: () => {},
|
||||
versions: makeVersions({ "org.matrix.msc4108": true }),
|
||||
wellKnown: {},
|
||||
};
|
||||
|
||||
const getComponent = (props = {}) => <LoginWithQRSection {...defaultProps} {...props} />;
|
||||
|
||||
@@ -716,7 +716,7 @@ describe("<Notifications />", () => {
|
||||
user: "@alice:example.org",
|
||||
ts: 1,
|
||||
});
|
||||
room.addLiveEvents([message]);
|
||||
room.addLiveEvents([message], { addToState: true });
|
||||
room.setUnreadNotificationCount(NotificationCountType.Total, 1);
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
@@ -17,7 +17,6 @@ import userEvent from "@testing-library/user-event";
|
||||
import RolesRoomSettingsTab from "../../../../../../../src/components/views/settings/tabs/room/RolesRoomSettingsTab";
|
||||
import { mkStubRoom, withClientContextRenderOptions, stubClient } from "../../../../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../../../../src/MatrixClientPeg";
|
||||
import { VoiceBroadcastInfoEventType } from "../../../../../../../src/voice-broadcast";
|
||||
import SettingsStore from "../../../../../../../src/settings/SettingsStore";
|
||||
import { ElementCall } from "../../../../../../../src/models/Call";
|
||||
|
||||
@@ -34,14 +33,6 @@ describe("RolesRoomSettingsTab", () => {
|
||||
return renderResult;
|
||||
};
|
||||
|
||||
const getVoiceBroadcastsSelect = async (): Promise<Element> => {
|
||||
return (await renderTab()).container.querySelector("select[label='Voice broadcasts']")!;
|
||||
};
|
||||
|
||||
const getVoiceBroadcastsSelectedOption = async (): Promise<Element> => {
|
||||
return (await renderTab()).container.querySelector("select[label='Voice broadcasts'] option:checked")!;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
cli = MatrixClientPeg.safeGet();
|
||||
@@ -76,26 +67,6 @@ describe("RolesRoomSettingsTab", () => {
|
||||
expect(container.querySelector(`[placeholder="@admin:server"]`)).toBeDisabled();
|
||||
});
|
||||
|
||||
it("should initially show »Moderator« permission for »Voice broadcasts«", async () => {
|
||||
expect((await getVoiceBroadcastsSelectedOption()).textContent).toBe("Moderator");
|
||||
});
|
||||
|
||||
describe("when setting »Default« permission for »Voice broadcasts«", () => {
|
||||
beforeEach(async () => {
|
||||
fireEvent.change(await getVoiceBroadcastsSelect(), {
|
||||
target: { value: 0 },
|
||||
});
|
||||
});
|
||||
|
||||
it("should update the power levels", () => {
|
||||
expect(cli.sendStateEvent).toHaveBeenCalledWith(roomId, EventType.RoomPowerLevels, {
|
||||
events: {
|
||||
[VoiceBroadcastInfoEventType]: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Element Call", () => {
|
||||
const setGroupCallsEnabled = (val: boolean): void => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((name: string) => {
|
||||
|
||||
@@ -75,7 +75,7 @@ describe("<SecurityRoomSettingsTab />", () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
client.sendStateEvent.mockReset().mockResolvedValue({ event_id: "test" });
|
||||
client.isRoomEncrypted.mockReturnValue(false);
|
||||
jest.spyOn(client.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(false);
|
||||
client.getClientWellKnown.mockReturnValue(undefined);
|
||||
jest.spyOn(SettingsStore, "getValue").mockRestore();
|
||||
|
||||
@@ -313,7 +313,7 @@ describe("<SecurityRoomSettingsTab />", () => {
|
||||
setRoomStateEvents(room);
|
||||
getComponent(room);
|
||||
|
||||
expect(screen.getByLabelText("Encrypted")).not.toBeChecked();
|
||||
await waitFor(() => expect(screen.getByLabelText("Encrypted")).not.toBeChecked());
|
||||
|
||||
fireEvent.click(screen.getByLabelText("Encrypted"));
|
||||
|
||||
@@ -330,7 +330,7 @@ describe("<SecurityRoomSettingsTab />", () => {
|
||||
setRoomStateEvents(room);
|
||||
getComponent(room);
|
||||
|
||||
expect(screen.getByLabelText("Encrypted")).not.toBeChecked();
|
||||
await waitFor(() => expect(screen.getByLabelText("Encrypted")).not.toBeChecked());
|
||||
|
||||
fireEvent.click(screen.getByLabelText("Encrypted"));
|
||||
|
||||
@@ -416,12 +416,12 @@ describe("<SecurityRoomSettingsTab />", () => {
|
||||
expect(screen.getByText("Once enabled, encryption cannot be disabled.")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays unencrypted rooms with toggle disabled", () => {
|
||||
it("displays unencrypted rooms with toggle disabled", async () => {
|
||||
const room = new Room(roomId, client, userId);
|
||||
setRoomStateEvents(room);
|
||||
getComponent(room);
|
||||
|
||||
expect(screen.getByLabelText("Encrypted")).not.toBeChecked();
|
||||
await waitFor(() => expect(screen.getByLabelText("Encrypted")).not.toBeChecked());
|
||||
expect(screen.getByLabelText("Encrypted").getAttribute("aria-disabled")).toEqual("true");
|
||||
expect(screen.queryByText("Once enabled, encryption cannot be disabled.")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("Your server requires encryption to be disabled.")).toBeInTheDocument();
|
||||
|
||||
@@ -11,11 +11,8 @@ import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { SdkContextClass } from "../../../src/contexts/SDKContext";
|
||||
import { OidcClientStore } from "../../../src/stores/oidc/OidcClientStore";
|
||||
import { UserProfilesStore } from "../../../src/stores/UserProfilesStore";
|
||||
import { VoiceBroadcastPreRecordingStore } from "../../../src/voice-broadcast";
|
||||
import { createTestClient } from "../../test-utils";
|
||||
|
||||
jest.mock("../../../src/voice-broadcast/stores/VoiceBroadcastPreRecordingStore");
|
||||
|
||||
describe("SdkContextClass", () => {
|
||||
let sdkContext = SdkContextClass.instance;
|
||||
let client: MatrixClient;
|
||||
@@ -33,12 +30,6 @@ describe("SdkContextClass", () => {
|
||||
expect(SdkContextClass.instance).toBe(globalInstance);
|
||||
});
|
||||
|
||||
it("voiceBroadcastPreRecordingStore should always return the same VoiceBroadcastPreRecordingStore", () => {
|
||||
const first = sdkContext.voiceBroadcastPreRecordingStore;
|
||||
expect(first).toBeInstanceOf(VoiceBroadcastPreRecordingStore);
|
||||
expect(sdkContext.voiceBroadcastPreRecordingStore).toBe(first);
|
||||
});
|
||||
|
||||
it("userProfilesStore should raise an error without a client", () => {
|
||||
expect(() => sdkContext.userProfilesStore).toThrow("Unable to create UserProfilesStore without a client");
|
||||
});
|
||||
|
||||
@@ -7,19 +7,16 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { mocked } from "jest-mock";
|
||||
import { EventType, MatrixClient, MatrixEvent, MsgType, RelationType, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { EventType, MatrixClient, MatrixEvent, MsgType, Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import {
|
||||
JSONEventFactory,
|
||||
MessageEventFactory,
|
||||
pickFactory,
|
||||
RoomCreateEventFactory,
|
||||
TextualEventFactory,
|
||||
} from "../../../src/events/EventTileFactory";
|
||||
import SettingsStore from "../../../src/settings/SettingsStore";
|
||||
import { VoiceBroadcastChunkEventType, VoiceBroadcastInfoState } from "../../../src/voice-broadcast";
|
||||
import { createTestClient, mkEvent } from "../../test-utils";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../voice-broadcast/utils/test-utils";
|
||||
|
||||
const roomId = "!room:example.com";
|
||||
|
||||
@@ -31,11 +28,7 @@ describe("pickFactory", () => {
|
||||
let createEventWithoutPredecessor: MatrixEvent;
|
||||
let dynamicPredecessorEvent: MatrixEvent;
|
||||
|
||||
let voiceBroadcastStartedEvent: MatrixEvent;
|
||||
let voiceBroadcastStoppedEvent: MatrixEvent;
|
||||
let voiceBroadcastChunkEvent: MatrixEvent;
|
||||
let utdEvent: MatrixEvent;
|
||||
let utdBroadcastChunkEvent: MatrixEvent;
|
||||
let audioMessageEvent: MatrixEvent;
|
||||
|
||||
beforeAll(() => {
|
||||
@@ -82,29 +75,6 @@ describe("pickFactory", () => {
|
||||
last_known_event_id: null,
|
||||
},
|
||||
});
|
||||
voiceBroadcastStartedEvent = mkVoiceBroadcastInfoStateEvent(
|
||||
roomId,
|
||||
VoiceBroadcastInfoState.Started,
|
||||
client.getUserId()!,
|
||||
client.deviceId!,
|
||||
);
|
||||
room.addLiveEvents([voiceBroadcastStartedEvent]);
|
||||
voiceBroadcastStoppedEvent = mkVoiceBroadcastInfoStateEvent(
|
||||
roomId,
|
||||
VoiceBroadcastInfoState.Stopped,
|
||||
client.getUserId()!,
|
||||
client.deviceId!,
|
||||
);
|
||||
voiceBroadcastChunkEvent = mkEvent({
|
||||
event: true,
|
||||
type: EventType.RoomMessage,
|
||||
user: client.getUserId()!,
|
||||
room: roomId,
|
||||
content: {
|
||||
msgtype: MsgType.Audio,
|
||||
[VoiceBroadcastChunkEventType]: {},
|
||||
},
|
||||
});
|
||||
audioMessageEvent = mkEvent({
|
||||
event: true,
|
||||
type: EventType.RoomMessage,
|
||||
@@ -123,20 +93,6 @@ describe("pickFactory", () => {
|
||||
msgtype: "m.bad.encrypted",
|
||||
},
|
||||
});
|
||||
utdBroadcastChunkEvent = mkEvent({
|
||||
event: true,
|
||||
type: EventType.RoomMessage,
|
||||
user: client.getUserId()!,
|
||||
room: roomId,
|
||||
content: {
|
||||
"msgtype": "m.bad.encrypted",
|
||||
"m.relates_to": {
|
||||
rel_type: RelationType.Reference,
|
||||
event_id: voiceBroadcastStartedEvent.getId(),
|
||||
},
|
||||
},
|
||||
});
|
||||
jest.spyOn(utdBroadcastChunkEvent, "isDecryptionFailure").mockReturnValue(true);
|
||||
});
|
||||
|
||||
it("should return JSONEventFactory for a no-op m.room.power_levels event", () => {
|
||||
@@ -151,10 +107,6 @@ describe("pickFactory", () => {
|
||||
});
|
||||
|
||||
describe("when showing hidden events", () => {
|
||||
it("should return a JSONEventFactory for a voice broadcast event", () => {
|
||||
expect(pickFactory(voiceBroadcastChunkEvent, client, true)).toBe(JSONEventFactory);
|
||||
});
|
||||
|
||||
it("should return a JSONEventFactory for a room create event without predecessor", () => {
|
||||
room.currentState.events.set(
|
||||
EventType.RoomCreate,
|
||||
@@ -164,17 +116,9 @@ describe("pickFactory", () => {
|
||||
expect(pickFactory(createEventWithoutPredecessor, client, true)).toBe(JSONEventFactory);
|
||||
});
|
||||
|
||||
it("should return a TextualEventFactory for a voice broadcast stopped event", () => {
|
||||
expect(pickFactory(voiceBroadcastStoppedEvent, client, true)).toBe(TextualEventFactory);
|
||||
});
|
||||
|
||||
it("should return a MessageEventFactory for an audio message event", () => {
|
||||
expect(pickFactory(audioMessageEvent, client, true)).toBe(MessageEventFactory);
|
||||
});
|
||||
|
||||
it("should return a MessageEventFactory for a UTD broadcast chunk event", () => {
|
||||
expect(pickFactory(utdBroadcastChunkEvent, client, true)).toBe(MessageEventFactory);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when not showing hidden events", () => {
|
||||
@@ -252,14 +196,6 @@ describe("pickFactory", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("should return undefined for a voice broadcast event", () => {
|
||||
expect(pickFactory(voiceBroadcastChunkEvent, client, false)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should return a TextualEventFactory for a voice broadcast stopped event", () => {
|
||||
expect(pickFactory(voiceBroadcastStoppedEvent, client, false)).toBe(TextualEventFactory);
|
||||
});
|
||||
|
||||
it("should return a MessageEventFactory for an audio message event", () => {
|
||||
expect(pickFactory(audioMessageEvent, client, false)).toBe(MessageEventFactory);
|
||||
});
|
||||
@@ -267,9 +203,5 @@ describe("pickFactory", () => {
|
||||
it("should return a MessageEventFactory for a UTD event", () => {
|
||||
expect(pickFactory(utdEvent, client, false)).toBe(MessageEventFactory);
|
||||
});
|
||||
|
||||
it("should return undefined for a UTD broadcast chunk event", () => {
|
||||
expect(pickFactory(utdBroadcastChunkEvent, client, false)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -332,7 +332,7 @@ describe("linkify-matrix", () => {
|
||||
|
||||
const event = new MouseEvent("mousedown");
|
||||
event.preventDefault = jest.fn();
|
||||
handlers.click(event);
|
||||
handlers!.click(event);
|
||||
expect(event.preventDefault).toHaveBeenCalled();
|
||||
expect(dispatchSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
@@ -372,7 +372,7 @@ describe("linkify-matrix", () => {
|
||||
|
||||
const event = new MouseEvent("mousedown");
|
||||
event.preventDefault = jest.fn();
|
||||
handlers.click(event);
|
||||
handlers!.click(event);
|
||||
expect(event.preventDefault).toHaveBeenCalled();
|
||||
expect(dispatchSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
|
||||
@@ -119,7 +119,7 @@ const setUpClientRoomAndStores = (): {
|
||||
skey: stateKey,
|
||||
content: content as IContent,
|
||||
});
|
||||
room.addLiveEvents([event]);
|
||||
room.addLiveEvents([event], { addToState: true });
|
||||
return { event_id: event.getId()! };
|
||||
});
|
||||
|
||||
|
||||
@@ -201,6 +201,7 @@ describe("MemberListStore", () => {
|
||||
function addEventToRoom(room: Room, ev: MatrixEvent) {
|
||||
room.getLiveTimeline().addEvent(ev, {
|
||||
toStartOfTimeline: false,
|
||||
addToState: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -24,13 +24,6 @@ import { ActiveRoomChangedPayload } from "../../../src/dispatcher/payloads/Activ
|
||||
import { SpaceStoreClass } from "../../../src/stores/spaces/SpaceStore";
|
||||
import { TestSdkContext } from "../TestSdkContext";
|
||||
import { ViewRoomPayload } from "../../../src/dispatcher/payloads/ViewRoomPayload";
|
||||
import {
|
||||
VoiceBroadcastInfoState,
|
||||
VoiceBroadcastPlayback,
|
||||
VoiceBroadcastPlaybacksStore,
|
||||
VoiceBroadcastRecording,
|
||||
} from "../../../src/voice-broadcast";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../voice-broadcast/utils/test-utils";
|
||||
import Modal from "../../../src/Modal";
|
||||
import ErrorDialog from "../../../src/components/views/dialogs/ErrorDialog";
|
||||
import { CancelAskToJoinPayload } from "../../../src/dispatcher/payloads/CancelAskToJoinPayload";
|
||||
@@ -160,7 +153,6 @@ describe("RoomViewStore", function () {
|
||||
stores._SlidingSyncManager = slidingSyncManager;
|
||||
stores._PosthogAnalytics = new MockPosthogAnalytics();
|
||||
stores._SpaceStore = new MockSpaceStore();
|
||||
stores._VoiceBroadcastPlaybacksStore = new VoiceBroadcastPlaybacksStore(stores.voiceBroadcastRecordingsStore);
|
||||
roomViewStore = new RoomViewStore(dis, stores);
|
||||
stores._RoomViewStore = roomViewStore;
|
||||
});
|
||||
@@ -343,88 +335,6 @@ describe("RoomViewStore", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("when listening to a voice broadcast", () => {
|
||||
let voiceBroadcastPlayback: VoiceBroadcastPlayback;
|
||||
|
||||
beforeEach(() => {
|
||||
voiceBroadcastPlayback = new VoiceBroadcastPlayback(
|
||||
mkVoiceBroadcastInfoStateEvent(
|
||||
roomId,
|
||||
VoiceBroadcastInfoState.Started,
|
||||
mockClient.getSafeUserId(),
|
||||
"d42",
|
||||
),
|
||||
mockClient,
|
||||
stores.voiceBroadcastRecordingsStore,
|
||||
);
|
||||
stores.voiceBroadcastPlaybacksStore.setCurrent(voiceBroadcastPlayback);
|
||||
jest.spyOn(voiceBroadcastPlayback, "pause").mockImplementation();
|
||||
});
|
||||
|
||||
it("and viewing a call it should pause the current broadcast", async () => {
|
||||
await viewCall();
|
||||
expect(voiceBroadcastPlayback.pause).toHaveBeenCalled();
|
||||
expect(roomViewStore.isViewingCall()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when recording a voice broadcast", () => {
|
||||
beforeEach(() => {
|
||||
stores.voiceBroadcastRecordingsStore.setCurrent(
|
||||
new VoiceBroadcastRecording(
|
||||
mkVoiceBroadcastInfoStateEvent(
|
||||
roomId,
|
||||
VoiceBroadcastInfoState.Started,
|
||||
mockClient.getSafeUserId(),
|
||||
"d42",
|
||||
),
|
||||
mockClient,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("and trying to view a call, it should not actually view it and show the info dialog", async () => {
|
||||
await viewCall();
|
||||
expect(Modal.createDialog).toMatchSnapshot();
|
||||
expect(roomViewStore.isViewingCall()).toBe(false);
|
||||
});
|
||||
|
||||
describe("and viewing a room with a broadcast", () => {
|
||||
beforeEach(async () => {
|
||||
const broadcastEvent = mkVoiceBroadcastInfoStateEvent(
|
||||
roomId2,
|
||||
VoiceBroadcastInfoState.Started,
|
||||
mockClient.getSafeUserId(),
|
||||
"ABC123",
|
||||
);
|
||||
room2.addLiveEvents([broadcastEvent]);
|
||||
|
||||
stores.voiceBroadcastPlaybacksStore.getByInfoEvent(broadcastEvent, mockClient);
|
||||
dis.dispatch({ action: Action.ViewRoom, room_id: roomId2 });
|
||||
await untilDispatch(Action.ActiveRoomChanged, dis);
|
||||
});
|
||||
|
||||
it("should continue recording", () => {
|
||||
expect(stores.voiceBroadcastPlaybacksStore.getCurrent()).toBeNull();
|
||||
expect(stores.voiceBroadcastRecordingsStore.getCurrent()?.getState()).toBe(
|
||||
VoiceBroadcastInfoState.Started,
|
||||
);
|
||||
});
|
||||
|
||||
describe("and stopping the recording", () => {
|
||||
beforeEach(async () => {
|
||||
await stores.voiceBroadcastRecordingsStore.getCurrent()?.stop();
|
||||
// check test precondition
|
||||
expect(stores.voiceBroadcastRecordingsStore.getCurrent()).toBeNull();
|
||||
});
|
||||
|
||||
it("should view the broadcast", () => {
|
||||
expect(stores.voiceBroadcastPlaybacksStore.getCurrent()?.infoEvent.getRoomId()).toBe(roomId2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Sliding Sync", function () {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName, roomId, value) => {
|
||||
|
||||
@@ -18,26 +18,3 @@ exports[`RoomViewStore should display the generic error message when the roomId
|
||||
"title": "Failed to join",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`RoomViewStore when recording a voice broadcast and trying to view a call, it should not actually view it and show the info dialog 1`] = `
|
||||
[MockFunction] {
|
||||
"calls": [
|
||||
[
|
||||
[Function],
|
||||
{
|
||||
"description": <p>
|
||||
You can’t start a call as you are currently recording a live broadcast. Please end your live broadcast in order to start a call.
|
||||
</p>,
|
||||
"hasCloseButton": true,
|
||||
"title": "Can’t start a call",
|
||||
},
|
||||
],
|
||||
],
|
||||
"results": [
|
||||
{
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -18,6 +18,9 @@ import { ActiveRoomChangedPayload } from "../../../../src/dispatcher/payloads/Ac
|
||||
import RightPanelStore from "../../../../src/stores/right-panel/RightPanelStore";
|
||||
import { RightPanelPhases } from "../../../../src/stores/right-panel/RightPanelStorePhases";
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
import { pendingVerificationRequestForUser } from "../../../../src/verification.ts";
|
||||
|
||||
jest.mock("../../../../src/verification");
|
||||
|
||||
describe("RightPanelStore", () => {
|
||||
// Mock out the settings store so the right panel store can't persist values between tests
|
||||
@@ -97,7 +100,7 @@ describe("RightPanelStore", () => {
|
||||
it("does nothing if given an invalid state", async () => {
|
||||
await viewRoom("!1:example.org");
|
||||
// Needs a member specified to be valid
|
||||
store.setCard({ phase: RightPanelPhases.RoomMemberInfo }, true, "!1:example.org");
|
||||
store.setCard({ phase: RightPanelPhases.MemberInfo }, true, "!1:example.org");
|
||||
expect(store.roomPhaseHistory).toEqual([]);
|
||||
});
|
||||
it("only creates a single history entry if given the same card twice", async () => {
|
||||
@@ -114,15 +117,15 @@ describe("RightPanelStore", () => {
|
||||
it("overwrites history if changing the phase", async () => {
|
||||
await viewRoom("!1:example.org");
|
||||
store.setCard({ phase: RightPanelPhases.RoomSummary }, true, "!1:example.org");
|
||||
store.setCard({ phase: RightPanelPhases.RoomMemberList }, true, "!1:example.org");
|
||||
expect(store.roomPhaseHistory).toEqual([{ phase: RightPanelPhases.RoomMemberList, state: {} }]);
|
||||
store.setCard({ phase: RightPanelPhases.MemberList }, true, "!1:example.org");
|
||||
expect(store.roomPhaseHistory).toEqual([{ phase: RightPanelPhases.MemberList, state: {} }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("setCards", () => {
|
||||
it("overwrites history", async () => {
|
||||
await viewRoom("!1:example.org");
|
||||
store.setCard({ phase: RightPanelPhases.RoomMemberList }, true, "!1:example.org");
|
||||
store.setCard({ phase: RightPanelPhases.MemberList }, true, "!1:example.org");
|
||||
store.setCards(
|
||||
[{ phase: RightPanelPhases.RoomSummary }, { phase: RightPanelPhases.PinnedMessages }],
|
||||
true,
|
||||
@@ -200,21 +203,40 @@ describe("RightPanelStore", () => {
|
||||
store.setCards(
|
||||
[
|
||||
{
|
||||
phase: RightPanelPhases.RoomMemberList,
|
||||
phase: RightPanelPhases.MemberList,
|
||||
},
|
||||
{
|
||||
phase: RightPanelPhases.RoomMemberInfo,
|
||||
phase: RightPanelPhases.MemberInfo,
|
||||
state: { member: new RoomMember("!1:example.org", "@alice:example.org") },
|
||||
},
|
||||
],
|
||||
true,
|
||||
"!1:example.org",
|
||||
);
|
||||
expect(store.currentCardForRoom("!1:example.org").phase).toEqual(RightPanelPhases.RoomMemberInfo);
|
||||
expect(store.currentCardForRoom("!1:example.org").phase).toEqual(RightPanelPhases.MemberInfo);
|
||||
|
||||
// Switch away and back
|
||||
await viewRoom("!2:example.org");
|
||||
await viewRoom("!1:example.org");
|
||||
expect(store.currentCardForRoom("!1:example.org").phase).toEqual(RightPanelPhases.RoomMemberList);
|
||||
expect(store.currentCardForRoom("!1:example.org").phase).toEqual(RightPanelPhases.MemberList);
|
||||
});
|
||||
|
||||
it("should redirect to verification if set to phase MemberInfo for a user with a pending verification", async () => {
|
||||
const member = new RoomMember("!1:example.org", "@alice:example.org");
|
||||
const verificationRequest = { mockVerificationRequest: true } as any;
|
||||
mocked(pendingVerificationRequestForUser).mockReturnValue(verificationRequest);
|
||||
await viewRoom("!1:example.org");
|
||||
store.setCard(
|
||||
{
|
||||
phase: RightPanelPhases.MemberInfo,
|
||||
state: { member },
|
||||
},
|
||||
true,
|
||||
"!1:example.org",
|
||||
);
|
||||
expect(store.currentCard).toEqual({
|
||||
phase: RightPanelPhases.EncryptionPanel,
|
||||
state: { member, verificationRequest },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -30,7 +30,7 @@ describe("onView3pidInvite()", () => {
|
||||
};
|
||||
onView3pidInvite(payload, rightPanelStore);
|
||||
|
||||
expect(rightPanelStore.showOrHidePhase).toHaveBeenCalledWith(RightPanelPhases.RoomMemberList);
|
||||
expect(rightPanelStore.showOrHidePhase).toHaveBeenCalledWith(RightPanelPhases.MemberList);
|
||||
expect(rightPanelStore.pushCard).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ describe("onView3pidInvite()", () => {
|
||||
|
||||
expect(rightPanelStore.showOrHidePhase).not.toHaveBeenCalled();
|
||||
expect(rightPanelStore.pushCard).toHaveBeenCalledWith({
|
||||
phase: RightPanelPhases.Room3pidMemberInfo,
|
||||
phase: RightPanelPhases.ThreePidMemberInfo,
|
||||
state: { memberInfoEvent: payload.event },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@ describe("MessagePreviewStore", () => {
|
||||
event: MatrixEvent,
|
||||
fireAction = true,
|
||||
): Promise<void> {
|
||||
room.addLiveEvents([event]);
|
||||
room.addLiveEvents([event], { addToState: true });
|
||||
if (fireAction) {
|
||||
// @ts-ignore private access
|
||||
await store.onAction({
|
||||
|
||||
@@ -47,11 +47,11 @@ describe("RecentAlgorithm", () => {
|
||||
|
||||
room.getMyMembership = () => KnownMembership.Join;
|
||||
|
||||
room.addLiveEvents([event1]);
|
||||
room.addLiveEvents([event1], { addToState: true });
|
||||
expect(algorithm.getLastTs(room, "@jane:matrix.org")).toBe(5);
|
||||
expect(algorithm.getLastTs(room, "@john:matrix.org")).toBe(5);
|
||||
|
||||
room.addLiveEvents([event2]);
|
||||
room.addLiveEvents([event2], { addToState: true });
|
||||
|
||||
expect(algorithm.getLastTs(room, "@jane:matrix.org")).toBe(10);
|
||||
expect(algorithm.getLastTs(room, "@john:matrix.org")).toBe(10);
|
||||
@@ -94,8 +94,8 @@ describe("RecentAlgorithm", () => {
|
||||
event: true,
|
||||
});
|
||||
|
||||
room1.addLiveEvents([evt]);
|
||||
room2.addLiveEvents([evt2]);
|
||||
room1.addLiveEvents([evt], { addToState: true });
|
||||
room2.addLiveEvents([evt2], { addToState: true });
|
||||
|
||||
expect(algorithm.sortRooms([room2, room1], DefaultTagID.Untagged)).toEqual([room1, room2]);
|
||||
});
|
||||
@@ -115,7 +115,7 @@ describe("RecentAlgorithm", () => {
|
||||
event: true,
|
||||
});
|
||||
|
||||
room1.addLiveEvents([evt]);
|
||||
room1.addLiveEvents([evt], { addToState: true });
|
||||
|
||||
expect(algorithm.sortRooms([room2, room1], DefaultTagID.Untagged)).toEqual([room2, room1]);
|
||||
|
||||
@@ -127,7 +127,7 @@ describe("RecentAlgorithm", () => {
|
||||
ts: 12,
|
||||
});
|
||||
|
||||
room1.addLiveEvents(events);
|
||||
room1.addLiveEvents(events, { addToState: true });
|
||||
});
|
||||
|
||||
it("orders rooms based on thread replies too", () => {
|
||||
@@ -145,7 +145,7 @@ describe("RecentAlgorithm", () => {
|
||||
ts: 12,
|
||||
length: 5,
|
||||
});
|
||||
room1.addLiveEvents(events1);
|
||||
room1.addLiveEvents(events1, { addToState: true });
|
||||
|
||||
const { events: events2 } = mkThread({
|
||||
room: room2,
|
||||
@@ -155,7 +155,7 @@ describe("RecentAlgorithm", () => {
|
||||
ts: 14,
|
||||
length: 10,
|
||||
});
|
||||
room2.addLiveEvents(events2);
|
||||
room2.addLiveEvents(events2, { addToState: true });
|
||||
|
||||
expect(algorithm.sortRooms([room1, room2], DefaultTagID.Untagged)).toEqual([room2, room1]);
|
||||
|
||||
@@ -169,7 +169,7 @@ describe("RecentAlgorithm", () => {
|
||||
// replies are 1ms after each other
|
||||
ts: 50,
|
||||
});
|
||||
room1.addLiveEvents([threadReply]);
|
||||
room1.addLiveEvents([threadReply], { addToState: true });
|
||||
|
||||
expect(algorithm.sortRooms([room1, room2], DefaultTagID.Untagged)).toEqual([room1, room2]);
|
||||
});
|
||||
|
||||
@@ -71,18 +71,5 @@ describe("MessageEventPreview", () => {
|
||||
});
|
||||
expect(preview.getTextFor(event)).toBe(`${userId}: test new content body`);
|
||||
});
|
||||
|
||||
it("when called with a broadcast chunk event it should return null", () => {
|
||||
const event = mkEvent({
|
||||
event: true,
|
||||
content: {
|
||||
body: "test body",
|
||||
["io.element.voice_broadcast_chunk"]: {},
|
||||
},
|
||||
user: userId,
|
||||
type: "m.room.message",
|
||||
});
|
||||
expect(preview.getTextFor(event)).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -70,7 +70,7 @@ describe("ReactionEventPreview", () => {
|
||||
room: roomId,
|
||||
});
|
||||
|
||||
room.getUnfilteredTimelineSet().addLiveEvent(message, {});
|
||||
room.getUnfilteredTimelineSet().addLiveEvent(message, { addToState: true });
|
||||
|
||||
const event = mkEvent({
|
||||
event: true,
|
||||
@@ -107,7 +107,7 @@ describe("ReactionEventPreview", () => {
|
||||
room: roomId,
|
||||
});
|
||||
|
||||
room.getUnfilteredTimelineSet().addLiveEvent(message, {});
|
||||
room.getUnfilteredTimelineSet().addLiveEvent(message, { addToState: true });
|
||||
|
||||
const event = mkEvent({
|
||||
event: true,
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
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 { Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { VoiceBroadcastPreview } from "../../../../../src/stores/room-list/previews/VoiceBroadcastPreview";
|
||||
import { VoiceBroadcastInfoState } from "../../../../../src/voice-broadcast";
|
||||
import { mkEvent, stubClient } from "../../../../test-utils";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../../../voice-broadcast/utils/test-utils";
|
||||
|
||||
describe("VoiceBroadcastPreview.getTextFor", () => {
|
||||
const roomId = "!room:example.com";
|
||||
const userId = "@user:example.com";
|
||||
const deviceId = "d42";
|
||||
let preview: VoiceBroadcastPreview;
|
||||
|
||||
beforeAll(() => {
|
||||
preview = new VoiceBroadcastPreview();
|
||||
});
|
||||
|
||||
it("when passing an event with empty content, it should return null", () => {
|
||||
const event = mkEvent({
|
||||
event: true,
|
||||
content: {},
|
||||
user: userId,
|
||||
type: "m.room.message",
|
||||
});
|
||||
expect(preview.getTextFor(event)).toBeNull();
|
||||
});
|
||||
|
||||
it("when passing a broadcast started event, it should return null", () => {
|
||||
const event = mkVoiceBroadcastInfoStateEvent(roomId, VoiceBroadcastInfoState.Started, userId, deviceId);
|
||||
expect(preview.getTextFor(event)).toBeNull();
|
||||
});
|
||||
|
||||
it("when passing a broadcast stopped event, it should return the expected text", () => {
|
||||
const event = mkVoiceBroadcastInfoStateEvent(roomId, VoiceBroadcastInfoState.Stopped, userId, deviceId);
|
||||
expect(preview.getTextFor(event)).toBe("@user:example.com ended a voice broadcast");
|
||||
});
|
||||
|
||||
it("when passing a redacted broadcast stopped event, it should return null", () => {
|
||||
const event = mkVoiceBroadcastInfoStateEvent(roomId, VoiceBroadcastInfoState.Stopped, userId, deviceId);
|
||||
event.makeRedacted(
|
||||
mkEvent({ event: true, content: {}, user: userId, type: "m.room.redaction" }),
|
||||
new Room(roomId, stubClient(), userId),
|
||||
);
|
||||
expect(preview.getTextFor(event)).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -22,9 +22,6 @@ import { waitFor } from "jest-matrix-react";
|
||||
import { stubClient, mkRoom, mkEvent } from "../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
|
||||
import { StopGapWidget } from "../../../../src/stores/widgets/StopGapWidget";
|
||||
import { ElementWidgetActions } from "../../../../src/stores/widgets/ElementWidgetActions";
|
||||
import { VoiceBroadcastInfoEventType, VoiceBroadcastRecording } from "../../../../src/voice-broadcast";
|
||||
import { SdkContextClass } from "../../../../src/contexts/SDKContext";
|
||||
import ActiveWidgetStore from "../../../../src/stores/ActiveWidgetStore";
|
||||
import SettingsStore from "../../../../src/settings/SettingsStore";
|
||||
|
||||
@@ -225,41 +222,6 @@ describe("StopGapWidget", () => {
|
||||
expect(messaging.feedEvent).toHaveBeenLastCalledWith(event.getEffectiveEvent(), "!1:example.org");
|
||||
});
|
||||
});
|
||||
|
||||
describe("when there is a voice broadcast recording", () => {
|
||||
let voiceBroadcastInfoEvent: MatrixEvent;
|
||||
let voiceBroadcastRecording: VoiceBroadcastRecording;
|
||||
|
||||
beforeEach(() => {
|
||||
voiceBroadcastInfoEvent = mkEvent({
|
||||
event: true,
|
||||
room: client.getRoom("x")?.roomId,
|
||||
user: client.getUserId()!,
|
||||
type: VoiceBroadcastInfoEventType,
|
||||
content: {},
|
||||
});
|
||||
voiceBroadcastRecording = new VoiceBroadcastRecording(voiceBroadcastInfoEvent, client);
|
||||
jest.spyOn(voiceBroadcastRecording, "pause");
|
||||
jest.spyOn(SdkContextClass.instance.voiceBroadcastRecordingsStore, "getCurrent").mockReturnValue(
|
||||
voiceBroadcastRecording,
|
||||
);
|
||||
});
|
||||
|
||||
describe(`and receiving a action:${ElementWidgetActions.JoinCall} message`, () => {
|
||||
beforeEach(async () => {
|
||||
messaging.on.mock.calls.find(([event, listener]) => {
|
||||
if (event === `action:${ElementWidgetActions.JoinCall}`) {
|
||||
listener();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should pause the current voice broadcast recording", () => {
|
||||
expect(voiceBroadcastRecording.pause).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("StopGapWidget with stickyPromise", () => {
|
||||
let client: MockedObject<MatrixClient>;
|
||||
|
||||
@@ -29,7 +29,7 @@ describe("useTopic", () => {
|
||||
event: true,
|
||||
});
|
||||
|
||||
room.addLiveEvents([topic]);
|
||||
room.addLiveEvents([topic], { addToState: true });
|
||||
|
||||
function RoomTopic() {
|
||||
const topic = useTopic(room);
|
||||
@@ -52,7 +52,7 @@ describe("useTopic", () => {
|
||||
});
|
||||
|
||||
act(() => {
|
||||
room.addLiveEvents([updatedTopic]);
|
||||
room.addLiveEvents([updatedTopic], { addToState: true });
|
||||
});
|
||||
|
||||
expect(screen.queryByText("New topic")).toBeInTheDocument();
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
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 { getEventDisplayInfo } from "../../../src/utils/EventRenderingUtils";
|
||||
import { VoiceBroadcastInfoState } from "../../../src/voice-broadcast";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../voice-broadcast/utils/test-utils";
|
||||
import { createTestClient } from "../../test-utils";
|
||||
|
||||
describe("getEventDisplayInfo", () => {
|
||||
const mkBroadcastInfoEvent = (state: VoiceBroadcastInfoState) => {
|
||||
return mkVoiceBroadcastInfoStateEvent("!room:example.com", state, "@user:example.com", "ASD123");
|
||||
};
|
||||
|
||||
it("should return the expected value for a broadcast started event", () => {
|
||||
expect(getEventDisplayInfo(createTestClient(), mkBroadcastInfoEvent(VoiceBroadcastInfoState.Started), false))
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"hasRenderer": true,
|
||||
"isBubbleMessage": false,
|
||||
"isInfoMessage": false,
|
||||
"isLeftAlignedBubbleMessage": false,
|
||||
"isSeeingThroughMessageHiddenForModeration": false,
|
||||
"noBubbleEvent": true,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it("should return the expected value for a broadcast stopped event", () => {
|
||||
expect(getEventDisplayInfo(createTestClient(), mkBroadcastInfoEvent(VoiceBroadcastInfoState.Stopped), false))
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"hasRenderer": true,
|
||||
"isBubbleMessage": false,
|
||||
"isInfoMessage": true,
|
||||
"isLeftAlignedBubbleMessage": false,
|
||||
"isSeeingThroughMessageHiddenForModeration": false,
|
||||
"noBubbleEvent": true,
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
@@ -35,8 +35,6 @@ import {
|
||||
import { getMockClientWithEventEmitter, makeBeaconInfoEvent, makePollStartEvent, stubClient } from "../../test-utils";
|
||||
import dis from "../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../src/dispatcher/actions";
|
||||
import { mkVoiceBroadcastInfoStateEvent } from "../voice-broadcast/utils/test-utils";
|
||||
import { VoiceBroadcastInfoState } from "../../../src/voice-broadcast/types";
|
||||
|
||||
jest.mock("../../../src/dispatcher/dispatcher");
|
||||
|
||||
@@ -148,20 +146,6 @@ describe("EventUtils", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const voiceBroadcastStart = mkVoiceBroadcastInfoStateEvent(
|
||||
"!room:example.com",
|
||||
VoiceBroadcastInfoState.Started,
|
||||
"@user:example.com",
|
||||
"ABC123",
|
||||
);
|
||||
|
||||
const voiceBroadcastStop = mkVoiceBroadcastInfoStateEvent(
|
||||
"!room:example.com",
|
||||
VoiceBroadcastInfoState.Stopped,
|
||||
"@user:example.com",
|
||||
"ABC123",
|
||||
);
|
||||
|
||||
describe("isContentActionable()", () => {
|
||||
type TestCase = [string, MatrixEvent];
|
||||
it.each<TestCase>([
|
||||
@@ -172,7 +156,6 @@ describe("EventUtils", () => {
|
||||
["room member event", roomMemberEvent],
|
||||
["event without msgtype", noMsgType],
|
||||
["event without content body property", noContentBody],
|
||||
["broadcast stop event", voiceBroadcastStop],
|
||||
])("returns false for %s", (_description, event) => {
|
||||
expect(isContentActionable(event)).toBe(false);
|
||||
});
|
||||
@@ -183,7 +166,6 @@ describe("EventUtils", () => {
|
||||
["event with empty content body", emptyContentBody],
|
||||
["event with a content body", niceTextMessage],
|
||||
["beacon_info event", beaconInfoEvent],
|
||||
["broadcast start event", voiceBroadcastStart],
|
||||
])("returns true for %s", (_description, event) => {
|
||||
expect(isContentActionable(event)).toBe(true);
|
||||
});
|
||||
|
||||
@@ -593,18 +593,21 @@ describe("HTMLExport", () => {
|
||||
|
||||
it("should not make /messages requests when exporting 'Current Timeline'", async () => {
|
||||
client.createMessagesRequest.mockRejectedValue(new Error("Should never be called"));
|
||||
room.addLiveEvents([
|
||||
new MatrixEvent({
|
||||
event_id: `$eventId`,
|
||||
type: EventType.RoomMessage,
|
||||
sender: client.getSafeUserId(),
|
||||
origin_server_ts: 123456789,
|
||||
content: {
|
||||
msgtype: "m.text",
|
||||
body: `testing testing`,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
room.addLiveEvents(
|
||||
[
|
||||
new MatrixEvent({
|
||||
event_id: `$eventId`,
|
||||
type: EventType.RoomMessage,
|
||||
sender: client.getSafeUserId(),
|
||||
origin_server_ts: 123456789,
|
||||
content: {
|
||||
msgtype: "m.text",
|
||||
body: `testing testing`,
|
||||
},
|
||||
}),
|
||||
],
|
||||
{ addToState: true },
|
||||
);
|
||||
|
||||
const exporter = new HTMLExporter(
|
||||
room,
|
||||
|
||||
@@ -121,7 +121,7 @@ describe("notifications", () => {
|
||||
user: USER_ID,
|
||||
msg: "Hello",
|
||||
});
|
||||
room.addLiveEvents([message]);
|
||||
room.addLiveEvents([message], { addToState: true });
|
||||
sendReadReceiptSpy = jest.spyOn(client, "sendReadReceipt").mockResolvedValue({});
|
||||
jest.spyOn(client, "getRooms").mockReturnValue([room]);
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((name) => {
|
||||
@@ -187,7 +187,7 @@ describe("notifications", () => {
|
||||
user: USER_ID,
|
||||
ts: 1,
|
||||
});
|
||||
room.addLiveEvents([message]);
|
||||
room.addLiveEvents([message], { addToState: true });
|
||||
room.setUnreadNotificationCount(NotificationCountType.Total, 1);
|
||||
|
||||
await clearAllNotifications(client);
|
||||
@@ -202,7 +202,7 @@ describe("notifications", () => {
|
||||
user: USER_ID,
|
||||
ts: 1,
|
||||
});
|
||||
room.addLiveEvents([message]);
|
||||
room.addLiveEvents([message], { addToState: true });
|
||||
room.setUnreadNotificationCount(NotificationCountType.Total, 1);
|
||||
|
||||
jest.spyOn(SettingsStore, "getValue").mockReset().mockReturnValue(false);
|
||||
|
||||
@@ -1,251 +0,0 @@
|
||||
/*
|
||||
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 { mocked } from "jest-mock";
|
||||
import { Optional } from "matrix-events-sdk";
|
||||
|
||||
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";
|
||||
|
||||
// mock VoiceRecording because it contains all the audio APIs
|
||||
jest.mock("../../../../src/audio/VoiceRecording", () => ({
|
||||
VoiceRecording: jest.fn().mockReturnValue({
|
||||
disableMaxLength: jest.fn(),
|
||||
emit: jest.fn(),
|
||||
liveData: {
|
||||
onUpdate: jest.fn(),
|
||||
},
|
||||
start: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
destroy: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock("../../../../src/settings/SettingsStore");
|
||||
|
||||
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;
|
||||
// 0... OpusHead
|
||||
const headers1 = new Uint8Array([...Array(28).fill(0), 79, 112, 117, 115, 72, 101, 97, 100]);
|
||||
// 0... OpusTags
|
||||
const headers2 = new Uint8Array([...Array(28).fill(0), 79, 112, 117, 115, 84, 97, 103, 115]);
|
||||
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 simulateFirstChunk = (): void => {
|
||||
// send headers in wrong order and multiple times to test robustness for that
|
||||
voiceRecording.onDataAvailable!(headers2);
|
||||
voiceRecording.onDataAvailable!(headers1);
|
||||
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);
|
||||
voiceRecording.onDataAvailable!(headers1);
|
||||
};
|
||||
|
||||
const expectOnFirstChunkRecorded = (): void => {
|
||||
expect(onChunkRecorded).toHaveBeenNthCalledWith(1, {
|
||||
buffer: concat(headers1, headers2, chunk1),
|
||||
length: 42,
|
||||
});
|
||||
};
|
||||
|
||||
const itShouldNotEmitAChunkRecordedEvent = (): void => {
|
||||
it("should not emit a ChunkRecorded event", (): void => {
|
||||
expect(voiceRecording.emit).not.toHaveBeenCalledWith(
|
||||
VoiceBroadcastRecorderEvent.ChunkRecorded,
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
voiceRecording = new VoiceRecording();
|
||||
// @ts-ignore
|
||||
voiceRecording.recorderSeconds = 23;
|
||||
// @ts-ignore
|
||||
voiceRecording.contentType = contentType;
|
||||
|
||||
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 header from recorder has been received", () => {
|
||||
beforeEach(() => {
|
||||
voiceRecording.onDataAvailable!(headers1);
|
||||
});
|
||||
|
||||
itShouldNotEmitAChunkRecordedEvent();
|
||||
});
|
||||
|
||||
describe("when the second header 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("and calling stop", () => {
|
||||
let stopPayload: Optional<ChunkRecordedPayload>;
|
||||
|
||||
beforeEach(async () => {
|
||||
stopPayload = await voiceBroadcastRecorder.stop();
|
||||
});
|
||||
|
||||
it("should return the remaining chunk", () => {
|
||||
expect(stopPayload).toEqual({
|
||||
buffer: concat(headers1, headers2, chunk1),
|
||||
length: 23,
|
||||
});
|
||||
});
|
||||
|
||||
describe("and calling start again and receiving some data", () => {
|
||||
beforeEach(() => {
|
||||
simulateFirstChunk();
|
||||
});
|
||||
|
||||
it("should emit the ChunkRecorded event for the first chunk", () => {
|
||||
expectOnFirstChunkRecorded();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("and calling stop() with recording.stop error)", () => {
|
||||
let stopPayload: Optional<ChunkRecordedPayload>;
|
||||
|
||||
beforeEach(async () => {
|
||||
mocked(voiceRecording.stop).mockRejectedValue("Error");
|
||||
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(() => {
|
||||
simulateFirstChunk();
|
||||
|
||||
// simulate a second chunk
|
||||
voiceRecording.onDataAvailable!(chunk2a);
|
||||
|
||||
// send headers again to test robustness for that
|
||||
voiceRecording.onDataAvailable!(headers2);
|
||||
|
||||
// add another 30 seconds for the next chunk
|
||||
// @ts-ignore
|
||||
voiceRecording.recorderSeconds = 72;
|
||||
voiceRecording.onDataAvailable!(chunk2b);
|
||||
});
|
||||
|
||||
it("should emit ChunkRecorded events", () => {
|
||||
expectOnFirstChunkRecorded();
|
||||
|
||||
expect(onChunkRecorded).toHaveBeenNthCalledWith(2, {
|
||||
buffer: concat(headers1, headers2, chunk2a, chunk2b),
|
||||
length: 72 - 42, // 72 (position at second chunk) - 42 (position of first chunk)
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
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");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user