2022-05-06 09:09:28 +00:00
|
|
|
/*
|
2023-01-31 09:58:17 +00:00
|
|
|
Copyright 2022 - 2023 The Matrix.org Foundation C.I.C.
|
2022-05-06 09:09:28 +00:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2021-12-17 09:26:32 +00:00
|
|
|
import EventEmitter from "events";
|
2022-12-12 11:24:14 +00:00
|
|
|
import { mocked, MockedObject } from "jest-mock";
|
2022-02-23 11:21:11 +00:00
|
|
|
import {
|
2023-08-07 08:24:58 +00:00
|
|
|
MatrixEvent,
|
2022-02-23 11:21:11 +00:00
|
|
|
Room,
|
|
|
|
User,
|
|
|
|
IContent,
|
|
|
|
IEvent,
|
|
|
|
RoomMember,
|
|
|
|
MatrixClient,
|
|
|
|
EventTimeline,
|
|
|
|
RoomState,
|
|
|
|
EventType,
|
2022-02-23 16:12:48 +00:00
|
|
|
IEventRelation,
|
2022-03-04 15:53:22 +00:00
|
|
|
IUnsigned,
|
2022-09-27 12:35:54 +00:00
|
|
|
IPusher,
|
2022-10-24 08:58:36 +00:00
|
|
|
RoomType,
|
2022-11-10 08:38:48 +00:00
|
|
|
KNOWN_SAFE_ROOM_VERSION,
|
2023-01-31 09:58:17 +00:00
|
|
|
ConditionKind,
|
|
|
|
IPushRules,
|
2023-06-01 07:53:48 +00:00
|
|
|
RelationType,
|
2023-08-08 10:12:12 +00:00
|
|
|
JoinRule,
|
2023-08-23 09:04:25 +00:00
|
|
|
IEventDecryptionResult,
|
2023-10-11 21:44:46 +00:00
|
|
|
OidcClientConfig,
|
2022-12-12 11:24:14 +00:00
|
|
|
} from "matrix-js-sdk/src/matrix";
|
2022-07-20 07:26:25 +00:00
|
|
|
import { normalize } from "matrix-js-sdk/src/utils";
|
2022-08-30 19:13:39 +00:00
|
|
|
import { ReEmitter } from "matrix-js-sdk/src/ReEmitter";
|
2022-09-30 16:28:53 +00:00
|
|
|
import { MediaHandler } from "matrix-js-sdk/src/webrtc/mediaHandler";
|
2022-10-13 17:22:25 +00:00
|
|
|
import { Feature, ServerSupport } from "matrix-js-sdk/src/feature";
|
2022-11-25 13:54:06 +00:00
|
|
|
import { CryptoBackend } from "matrix-js-sdk/src/common-crypto/CryptoBackend";
|
2023-01-27 11:00:06 +00:00
|
|
|
import { MapperOpts } from "matrix-js-sdk/src/event-mapper";
|
2023-10-30 15:14:27 +00:00
|
|
|
// eslint-disable-next-line no-restricted-imports
|
|
|
|
import { MatrixRTCSessionManager } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSessionManager";
|
|
|
|
// eslint-disable-next-line no-restricted-imports
|
|
|
|
import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession";
|
2021-10-22 22:23:32 +00:00
|
|
|
|
2023-08-10 08:01:14 +00:00
|
|
|
import type { GroupCall } from "matrix-js-sdk/src/matrix";
|
2022-12-12 11:24:14 +00:00
|
|
|
import { MatrixClientPeg as peg } from "../../src/MatrixClientPeg";
|
2023-10-11 21:44:46 +00:00
|
|
|
import { ValidatedServerConfig } from "../../src/utils/ValidatedServerConfig";
|
2022-02-23 11:21:11 +00:00
|
|
|
import { EnhancedMap } from "../../src/utils/maps";
|
2022-03-09 13:23:58 +00:00
|
|
|
import { AsyncStoreWithClient } from "../../src/stores/AsyncStoreWithClient";
|
2022-03-03 22:09:06 +00:00
|
|
|
import MatrixClientBackedSettingsHandler from "../../src/settings/handlers/MatrixClientBackedSettingsHandler";
|
2017-03-16 17:26:42 +00:00
|
|
|
|
2016-03-28 21:59:34 +00:00
|
|
|
/**
|
|
|
|
* Stub out the MatrixClient, and configure the MatrixClientPeg object to
|
|
|
|
* return it when get() is called.
|
2016-04-07 15:47:17 +00:00
|
|
|
*
|
2016-11-14 18:20:15 +00:00
|
|
|
* TODO: once the components are updated to get their MatrixClients from
|
|
|
|
* the react context, we can get rid of this and just inject a test client
|
|
|
|
* via the context instead.
|
2023-06-28 12:39:34 +00:00
|
|
|
*
|
2023-09-22 11:03:05 +00:00
|
|
|
* See also {@link getMockClientWithEventEmitter} which does something similar but different.
|
2016-03-28 21:59:34 +00:00
|
|
|
*/
|
2022-09-21 16:46:28 +00:00
|
|
|
export function stubClient(): MatrixClient {
|
2017-10-11 16:56:17 +00:00
|
|
|
const client = createTestClient();
|
2016-11-14 18:20:15 +00:00
|
|
|
|
|
|
|
// stub out the methods in MatrixClientPeg
|
|
|
|
//
|
|
|
|
// 'sandbox.restore()' doesn't work correctly on inherited methods,
|
|
|
|
// so we do this for each method
|
2022-12-12 11:24:14 +00:00
|
|
|
jest.spyOn(peg, "get");
|
2023-06-05 17:12:23 +00:00
|
|
|
jest.spyOn(peg, "safeGet");
|
2022-12-12 11:24:14 +00:00
|
|
|
jest.spyOn(peg, "unset");
|
|
|
|
jest.spyOn(peg, "replaceUsingCreds");
|
2023-06-05 17:12:23 +00:00
|
|
|
// MatrixClientPeg.safeGet() is called a /lot/, so implement it with our own
|
2016-11-14 18:20:15 +00:00
|
|
|
// fast stub function rather than a sinon stub
|
2023-06-05 17:12:23 +00:00
|
|
|
peg.get = () => client;
|
|
|
|
peg.safeGet = () => client;
|
2022-03-03 22:09:06 +00:00
|
|
|
MatrixClientBackedSettingsHandler.matrixClient = client;
|
2022-09-21 16:46:28 +00:00
|
|
|
return client;
|
2016-11-14 18:20:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a stubbed-out MatrixClient
|
|
|
|
*
|
|
|
|
* @returns {object} MatrixClient stub
|
|
|
|
*/
|
2022-03-09 13:23:58 +00:00
|
|
|
export function createTestClient(): MatrixClient {
|
2021-12-17 09:26:32 +00:00
|
|
|
const eventEmitter = new EventEmitter();
|
2023-10-30 15:14:27 +00:00
|
|
|
|
2022-07-13 05:56:36 +00:00
|
|
|
let txnId = 1;
|
2021-12-17 09:26:32 +00:00
|
|
|
|
2022-08-10 13:26:42 +00:00
|
|
|
const client = {
|
2019-12-16 11:12:48 +00:00
|
|
|
getHomeserverUrl: jest.fn(),
|
|
|
|
getIdentityServerUrl: jest.fn(),
|
2022-07-13 05:56:36 +00:00
|
|
|
getDomain: jest.fn().mockReturnValue("matrix.org"),
|
|
|
|
getUserId: jest.fn().mockReturnValue("@userId:matrix.org"),
|
2022-12-16 11:01:16 +00:00
|
|
|
getSafeUserId: jest.fn().mockReturnValue("@userId:matrix.org"),
|
2022-11-22 06:58:37 +00:00
|
|
|
getUserIdLocalpart: jest.fn().mockResolvedValue("userId"),
|
2023-02-28 08:58:23 +00:00
|
|
|
getUser: jest.fn().mockReturnValue({ on: jest.fn(), off: jest.fn() }),
|
2023-02-23 09:25:33 +00:00
|
|
|
getDevice: jest.fn(),
|
2022-03-28 13:12:09 +00:00
|
|
|
getDeviceId: jest.fn().mockReturnValue("ABCDEFGHI"),
|
2023-02-23 09:25:33 +00:00
|
|
|
getStoredCrossSigningForUser: jest.fn(),
|
|
|
|
getStoredDevice: jest.fn(),
|
|
|
|
requestVerification: jest.fn(),
|
2022-11-08 09:02:07 +00:00
|
|
|
deviceId: "ABCDEFGHI",
|
2022-05-16 20:54:08 +00:00
|
|
|
getDevices: jest.fn().mockResolvedValue({ devices: [{ device_id: "ABCDEFGHI" }] }),
|
2022-12-02 08:35:31 +00:00
|
|
|
getSessionId: jest.fn().mockReturnValue("iaszphgvfku"),
|
2022-07-13 05:56:36 +00:00
|
|
|
credentials: { userId: "@userId:matrix.org" },
|
2023-02-23 07:46:49 +00:00
|
|
|
bootstrapCrossSigning: jest.fn(),
|
|
|
|
hasSecretStorageKey: jest.fn(),
|
2016-04-08 13:50:04 +00:00
|
|
|
|
2024-01-10 10:34:03 +00:00
|
|
|
secretStorage: {
|
|
|
|
get: jest.fn(),
|
|
|
|
},
|
|
|
|
|
2022-06-14 20:29:24 +00:00
|
|
|
store: {
|
|
|
|
getPendingEvents: jest.fn().mockResolvedValue([]),
|
|
|
|
setPendingEvents: jest.fn().mockResolvedValue(undefined),
|
2022-07-13 05:56:36 +00:00
|
|
|
storeRoom: jest.fn(),
|
|
|
|
removeRoom: jest.fn(),
|
2022-06-14 20:29:24 +00:00
|
|
|
},
|
|
|
|
|
2022-08-10 12:57:56 +00:00
|
|
|
crypto: {
|
|
|
|
deviceList: {
|
|
|
|
downloadKeys: jest.fn(),
|
|
|
|
},
|
|
|
|
},
|
2023-10-04 13:01:39 +00:00
|
|
|
getCrypto: jest.fn().mockReturnValue({
|
|
|
|
getUserDeviceInfo: jest.fn(),
|
|
|
|
getUserVerificationStatus: jest.fn(),
|
|
|
|
getDeviceVerificationStatus: jest.fn(),
|
2024-01-10 10:34:03 +00:00
|
|
|
resetKeyBackup: jest.fn(),
|
2023-10-04 13:01:39 +00:00
|
|
|
}),
|
2022-08-10 12:57:56 +00:00
|
|
|
|
2019-12-16 11:12:48 +00:00
|
|
|
getPushActionsForEvent: jest.fn(),
|
2022-12-12 11:24:14 +00:00
|
|
|
getRoom: jest.fn().mockImplementation((roomId) => mkStubRoom(roomId, "My room", client)),
|
2019-12-16 11:12:48 +00:00
|
|
|
getRooms: jest.fn().mockReturnValue([]),
|
|
|
|
getVisibleRooms: jest.fn().mockReturnValue([]),
|
|
|
|
loginFlows: jest.fn(),
|
2021-12-17 09:26:32 +00:00
|
|
|
on: eventEmitter.on.bind(eventEmitter),
|
2022-02-17 16:30:36 +00:00
|
|
|
off: eventEmitter.off.bind(eventEmitter),
|
2021-12-17 09:26:32 +00:00
|
|
|
removeListener: eventEmitter.removeListener.bind(eventEmitter),
|
2022-02-17 16:30:36 +00:00
|
|
|
emit: eventEmitter.emit.bind(eventEmitter),
|
2019-12-16 11:12:48 +00:00
|
|
|
isRoomEncrypted: jest.fn().mockReturnValue(false),
|
2022-03-09 13:23:58 +00:00
|
|
|
peekInRoom: jest.fn().mockResolvedValue(mkStubRoom(undefined, undefined, undefined)),
|
2022-06-10 12:59:07 +00:00
|
|
|
stopPeeking: jest.fn(),
|
2016-04-08 13:50:04 +00:00
|
|
|
|
2019-12-16 11:12:48 +00:00
|
|
|
paginateEventTimeline: jest.fn().mockResolvedValue(undefined),
|
|
|
|
sendReadReceipt: jest.fn().mockResolvedValue(undefined),
|
|
|
|
getRoomIdForAlias: jest.fn().mockResolvedValue(undefined),
|
|
|
|
getRoomDirectoryVisibility: jest.fn().mockResolvedValue(undefined),
|
|
|
|
getProfileInfo: jest.fn().mockResolvedValue({}),
|
2021-04-23 13:39:39 +00:00
|
|
|
getThirdpartyProtocols: jest.fn().mockResolvedValue({}),
|
|
|
|
getClientWellKnown: jest.fn().mockReturnValue(null),
|
|
|
|
supportsVoip: jest.fn().mockReturnValue(true),
|
2022-08-10 13:26:42 +00:00
|
|
|
getTurnServers: jest.fn().mockReturnValue([]),
|
2022-03-09 13:23:58 +00:00
|
|
|
getTurnServersExpiry: jest.fn().mockReturnValue(2 ^ 32),
|
2021-04-23 13:39:39 +00:00
|
|
|
getThirdpartyUser: jest.fn().mockResolvedValue([]),
|
2022-12-22 12:09:57 +00:00
|
|
|
getAccountData: jest.fn().mockImplementation((type) => {
|
2016-09-09 12:37:42 +00:00
|
|
|
return mkEvent({
|
2023-01-31 09:58:17 +00:00
|
|
|
user: "@user:example.com",
|
2022-03-09 13:23:58 +00:00
|
|
|
room: undefined,
|
2016-09-09 12:37:42 +00:00
|
|
|
type,
|
|
|
|
event: true,
|
|
|
|
content: {},
|
|
|
|
});
|
2022-12-22 12:09:57 +00:00
|
|
|
}),
|
2023-01-27 11:00:06 +00:00
|
|
|
mxcUrlToHttp: (mxc: string) => `http://this.is.a.url/${mxc.substring(6)}`,
|
2023-06-16 12:25:24 +00:00
|
|
|
scheduleAllGroupSessionsForBackup: jest.fn().mockResolvedValue(undefined),
|
2019-12-16 11:12:48 +00:00
|
|
|
setAccountData: jest.fn(),
|
2021-08-04 08:46:39 +00:00
|
|
|
setRoomAccountData: jest.fn(),
|
2022-06-07 20:20:32 +00:00
|
|
|
setRoomTopic: jest.fn(),
|
2022-08-05 15:33:57 +00:00
|
|
|
setRoomReadMarkers: jest.fn().mockResolvedValue({}),
|
2019-12-16 11:12:48 +00:00
|
|
|
sendTyping: jest.fn().mockResolvedValue({}),
|
2022-07-13 05:56:36 +00:00
|
|
|
sendMessage: jest.fn().mockResolvedValue({}),
|
2022-03-09 13:23:58 +00:00
|
|
|
sendStateEvent: jest.fn().mockResolvedValue(undefined),
|
2022-10-26 16:59:47 +00:00
|
|
|
getSyncState: jest.fn().mockReturnValue("SYNCING"),
|
2017-02-24 11:41:23 +00:00
|
|
|
generateClientSecret: () => "t35tcl1Ent5ECr3T",
|
2022-03-04 09:39:16 +00:00
|
|
|
isGuest: jest.fn().mockReturnValue(false),
|
2021-07-29 16:35:15 +00:00
|
|
|
getRoomHierarchy: jest.fn().mockReturnValue({
|
2021-04-23 13:45:22 +00:00
|
|
|
rooms: [],
|
|
|
|
}),
|
2022-04-07 11:34:07 +00:00
|
|
|
createRoom: jest.fn().mockResolvedValue({ room_id: "!1:example.org" }),
|
|
|
|
setPowerLevel: jest.fn().mockResolvedValue(undefined),
|
2021-08-03 13:43:56 +00:00
|
|
|
pushRules: {},
|
2021-05-18 12:46:47 +00:00
|
|
|
decryptEventIfNeeded: () => Promise.resolve(),
|
2021-07-15 17:17:07 +00:00
|
|
|
isUserIgnored: jest.fn().mockReturnValue(false),
|
2021-07-06 09:34:50 +00:00
|
|
|
getCapabilities: jest.fn().mockResolvedValue({}),
|
2023-05-15 16:06:02 +00:00
|
|
|
supportsThreads: jest.fn().mockReturnValue(false),
|
2023-03-22 20:22:19 +00:00
|
|
|
supportsIntentionalMentions: () => false,
|
2021-10-08 09:30:46 +00:00
|
|
|
getRoomUpgradeHistory: jest.fn().mockReturnValue([]),
|
2022-03-09 13:23:58 +00:00
|
|
|
getOpenIdToken: jest.fn().mockResolvedValue(undefined),
|
2021-12-09 10:48:58 +00:00
|
|
|
registerWithIdentityServer: jest.fn().mockResolvedValue({}),
|
|
|
|
getIdentityAccount: jest.fn().mockResolvedValue({}),
|
2022-10-12 17:59:07 +00:00
|
|
|
getTerms: jest.fn().mockResolvedValue({ policies: [] }),
|
2022-03-09 13:23:58 +00:00
|
|
|
doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(undefined),
|
2022-10-07 13:10:18 +00:00
|
|
|
isVersionSupported: jest.fn().mockResolvedValue(undefined),
|
2022-03-09 13:23:58 +00:00
|
|
|
getPushRules: jest.fn().mockResolvedValue(undefined),
|
2022-01-06 09:47:03 +00:00
|
|
|
getPushers: jest.fn().mockResolvedValue({ pushers: [] }),
|
|
|
|
getThreePids: jest.fn().mockResolvedValue({ threepids: [] }),
|
2023-02-16 13:17:43 +00:00
|
|
|
bulkLookupThreePids: jest.fn().mockResolvedValue({ threepids: [] }),
|
2022-03-09 13:23:58 +00:00
|
|
|
setPusher: jest.fn().mockResolvedValue(undefined),
|
|
|
|
setPushRuleEnabled: jest.fn().mockResolvedValue(undefined),
|
|
|
|
setPushRuleActions: jest.fn().mockResolvedValue(undefined),
|
2022-11-10 10:53:49 +00:00
|
|
|
relations: jest.fn().mockResolvedValue({
|
|
|
|
events: [],
|
|
|
|
}),
|
2022-03-04 15:53:22 +00:00
|
|
|
isCryptoEnabled: jest.fn().mockReturnValue(false),
|
2022-06-10 12:59:07 +00:00
|
|
|
hasLazyLoadMembersEnabled: jest.fn().mockReturnValue(false),
|
|
|
|
isInitialSyncComplete: jest.fn().mockReturnValue(true),
|
2022-04-07 11:34:07 +00:00
|
|
|
downloadKeys: jest.fn(),
|
2022-12-22 10:37:07 +00:00
|
|
|
fetchRoomEvent: jest.fn().mockRejectedValue({}),
|
2022-07-13 05:56:36 +00:00
|
|
|
makeTxnId: jest.fn().mockImplementation(() => `t${txnId++}`),
|
2022-08-10 12:57:56 +00:00
|
|
|
sendToDevice: jest.fn().mockResolvedValue(undefined),
|
|
|
|
queueToDevice: jest.fn().mockResolvedValue(undefined),
|
|
|
|
encryptAndSendToDevices: jest.fn().mockResolvedValue(undefined),
|
2022-10-21 17:26:33 +00:00
|
|
|
cancelPendingEvent: jest.fn(),
|
2022-09-30 16:28:53 +00:00
|
|
|
|
|
|
|
getMediaHandler: jest.fn().mockReturnValue({
|
|
|
|
setVideoInput: jest.fn(),
|
|
|
|
setAudioInput: jest.fn(),
|
2022-11-09 20:14:55 +00:00
|
|
|
setAudioSettings: jest.fn(),
|
2022-11-28 21:37:32 +00:00
|
|
|
stopAllStreams: jest.fn(),
|
2022-09-30 16:28:53 +00:00
|
|
|
} as unknown as MediaHandler),
|
2022-10-12 17:59:07 +00:00
|
|
|
uploadContent: jest.fn(),
|
2023-01-27 11:00:06 +00:00
|
|
|
getEventMapper: (_options?: MapperOpts) => (event: Partial<IEvent>) => new MatrixEvent(event),
|
2022-12-12 11:24:14 +00:00
|
|
|
leaveRoomChain: jest.fn((roomId) => ({ [roomId]: null })),
|
2022-11-22 06:58:37 +00:00
|
|
|
requestPasswordEmailToken: jest.fn().mockRejectedValue({}),
|
|
|
|
setPassword: jest.fn().mockRejectedValue({}),
|
2022-11-28 21:37:32 +00:00
|
|
|
groupCallEventHandler: { groupCalls: new Map<string, GroupCall>() },
|
2022-12-23 15:44:01 +00:00
|
|
|
redactEvent: jest.fn(),
|
2023-01-30 14:31:32 +00:00
|
|
|
|
|
|
|
createMessagesRequest: jest.fn().mockResolvedValue({
|
|
|
|
chunk: [],
|
|
|
|
}),
|
2023-02-20 14:46:07 +00:00
|
|
|
sendEvent: jest.fn().mockImplementation((roomId, type, content) => {
|
|
|
|
return new MatrixEvent({
|
|
|
|
type,
|
|
|
|
sender: "@me:localhost",
|
|
|
|
content,
|
|
|
|
event_id: "$9999999999999999999999999999999999999999999",
|
|
|
|
room_id: roomId,
|
|
|
|
});
|
|
|
|
}),
|
2023-03-21 11:38:41 +00:00
|
|
|
|
|
|
|
searchUserDirectory: jest.fn().mockResolvedValue({ limited: false, results: [] }),
|
2023-04-26 10:23:32 +00:00
|
|
|
setDeviceVerified: jest.fn(),
|
2023-06-01 12:35:47 +00:00
|
|
|
joinRoom: jest.fn(),
|
2023-06-15 11:57:58 +00:00
|
|
|
getSyncStateData: jest.fn(),
|
2023-06-19 22:20:14 +00:00
|
|
|
getDehydratedDevice: jest.fn(),
|
2023-07-27 09:21:20 +00:00
|
|
|
exportRoomKeys: jest.fn(),
|
2023-08-07 06:27:09 +00:00
|
|
|
knockRoom: jest.fn(),
|
|
|
|
leave: jest.fn(),
|
2023-08-14 08:25:13 +00:00
|
|
|
getVersions: jest.fn().mockResolvedValue({ versions: ["v1.1"] }),
|
|
|
|
requestAdd3pidMsisdnToken: jest.fn(),
|
|
|
|
submitMsisdnTokenOtherUrl: jest.fn(),
|
|
|
|
addThreePidOnly: jest.fn(),
|
|
|
|
requestMsisdnToken: jest.fn(),
|
|
|
|
submitMsisdnToken: jest.fn(),
|
2023-09-21 19:03:22 +00:00
|
|
|
getMediaConfig: jest.fn(),
|
|
|
|
baseUrl: "https://matrix-client.matrix.org",
|
2023-10-30 15:14:27 +00:00
|
|
|
matrixRTC: createStubMatrixRTC(),
|
2022-03-09 13:23:58 +00:00
|
|
|
} as unknown as MatrixClient;
|
2022-08-10 13:26:42 +00:00
|
|
|
|
2022-08-30 19:13:39 +00:00
|
|
|
client.reEmitter = new ReEmitter(client);
|
|
|
|
|
2022-10-13 17:22:25 +00:00
|
|
|
client.canSupport = new Map();
|
2022-12-12 11:24:14 +00:00
|
|
|
Object.keys(Feature).forEach((feature) => {
|
2022-10-13 17:22:25 +00:00
|
|
|
client.canSupport.set(feature as Feature, ServerSupport.Stable);
|
|
|
|
});
|
|
|
|
|
2022-08-10 13:26:42 +00:00
|
|
|
Object.defineProperty(client, "pollingTurnServers", {
|
|
|
|
configurable: true,
|
|
|
|
get: () => true,
|
|
|
|
});
|
|
|
|
return client;
|
2016-03-28 21:59:34 +00:00
|
|
|
}
|
|
|
|
|
2023-10-30 15:14:27 +00:00
|
|
|
export function createStubMatrixRTC(): MatrixRTCSessionManager {
|
|
|
|
const eventEmitterMatrixRTCSessionManager = new EventEmitter();
|
|
|
|
const mockGetRoomSession = jest.fn();
|
|
|
|
mockGetRoomSession.mockImplementation((roomId) => {
|
|
|
|
const session = new EventEmitter() as MatrixRTCSession;
|
|
|
|
session.memberships = [];
|
|
|
|
session.getOldestMembership = () => undefined;
|
|
|
|
return session;
|
|
|
|
});
|
|
|
|
return {
|
|
|
|
start: jest.fn(),
|
|
|
|
stop: jest.fn(),
|
|
|
|
getActiveRoomSession: jest.fn(),
|
|
|
|
getRoomSession: mockGetRoomSession,
|
|
|
|
on: eventEmitterMatrixRTCSessionManager.on.bind(eventEmitterMatrixRTCSessionManager),
|
|
|
|
off: eventEmitterMatrixRTCSessionManager.off.bind(eventEmitterMatrixRTCSessionManager),
|
|
|
|
removeListener: eventEmitterMatrixRTCSessionManager.removeListener.bind(eventEmitterMatrixRTCSessionManager),
|
|
|
|
emit: eventEmitterMatrixRTCSessionManager.emit.bind(eventEmitterMatrixRTCSessionManager),
|
|
|
|
} as unknown as MatrixRTCSessionManager;
|
|
|
|
}
|
2022-02-23 11:21:11 +00:00
|
|
|
type MakeEventPassThruProps = {
|
|
|
|
user: User["userId"];
|
2022-03-09 13:23:58 +00:00
|
|
|
relatesTo?: IEventRelation;
|
2022-02-23 11:21:11 +00:00
|
|
|
event?: boolean;
|
|
|
|
ts?: number;
|
|
|
|
skey?: string;
|
|
|
|
};
|
|
|
|
type MakeEventProps = MakeEventPassThruProps & {
|
2023-03-21 09:23:20 +00:00
|
|
|
/** If provided will be used as event Id. Else an Id is generated. */
|
|
|
|
id?: string;
|
2022-02-23 11:21:11 +00:00
|
|
|
type: string;
|
2022-10-28 11:48:15 +00:00
|
|
|
redacts?: string;
|
2022-02-23 11:21:11 +00:00
|
|
|
content: IContent;
|
2022-08-10 12:57:56 +00:00
|
|
|
room?: Room["roomId"]; // to-device messages are roomless
|
2022-02-23 11:21:11 +00:00
|
|
|
// eslint-disable-next-line camelcase
|
|
|
|
prev_content?: IContent;
|
2022-03-04 15:53:22 +00:00
|
|
|
unsigned?: IUnsigned;
|
2022-02-23 11:21:11 +00:00
|
|
|
};
|
|
|
|
|
2023-10-25 10:08:10 +00:00
|
|
|
export const mkRoomCreateEvent = (userId: string, roomId: string, content?: IContent): MatrixEvent => {
|
2022-11-10 08:38:48 +00:00
|
|
|
return mkEvent({
|
|
|
|
event: true,
|
|
|
|
type: EventType.RoomCreate,
|
|
|
|
content: {
|
|
|
|
creator: userId,
|
|
|
|
room_version: KNOWN_SAFE_ROOM_VERSION,
|
2023-10-25 10:08:10 +00:00
|
|
|
...content,
|
2022-11-10 08:38:48 +00:00
|
|
|
},
|
|
|
|
skey: "",
|
|
|
|
user: userId,
|
|
|
|
room: roomId,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2016-03-30 23:48:46 +00:00
|
|
|
/**
|
|
|
|
* Create an Event.
|
|
|
|
* @param {Object} opts Values for the event.
|
|
|
|
* @param {string} opts.type The event.type
|
|
|
|
* @param {string} opts.room The event.room_id
|
|
|
|
* @param {string} opts.user The event.user_id
|
2021-04-22 13:45:13 +00:00
|
|
|
* @param {string=} opts.skey Optional. The state key (auto inserts empty string)
|
|
|
|
* @param {number=} opts.ts Optional. Timestamp for the event
|
2016-03-30 23:48:46 +00:00
|
|
|
* @param {Object} opts.content The event.content
|
|
|
|
* @param {boolean} opts.event True to make a MatrixEvent.
|
2021-08-10 06:55:11 +00:00
|
|
|
* @param {unsigned=} opts.unsigned
|
2016-03-30 23:48:46 +00:00
|
|
|
* @return {Object} a JSON object representing this event.
|
|
|
|
*/
|
2022-02-23 11:21:11 +00:00
|
|
|
export function mkEvent(opts: MakeEventProps): MatrixEvent {
|
2016-03-30 23:48:46 +00:00
|
|
|
if (!opts.type || !opts.content) {
|
|
|
|
throw new Error("Missing .type or .content =>" + JSON.stringify(opts));
|
|
|
|
}
|
2022-02-23 11:21:11 +00:00
|
|
|
const event: Partial<IEvent> = {
|
2016-03-30 23:48:46 +00:00
|
|
|
type: opts.type,
|
|
|
|
room_id: opts.room,
|
|
|
|
sender: opts.user,
|
|
|
|
content: opts.content,
|
2017-01-18 10:53:17 +00:00
|
|
|
prev_content: opts.prev_content,
|
2023-03-21 09:23:20 +00:00
|
|
|
event_id: opts.id ?? "$" + Math.random() + "-" + Math.random(),
|
2022-03-03 17:20:13 +00:00
|
|
|
origin_server_ts: opts.ts ?? 0,
|
2022-03-03 12:54:44 +00:00
|
|
|
unsigned: opts.unsigned,
|
2022-10-28 11:48:15 +00:00
|
|
|
redacts: opts.redacts,
|
2016-03-30 23:48:46 +00:00
|
|
|
};
|
2022-03-04 15:53:22 +00:00
|
|
|
if (opts.skey !== undefined) {
|
2016-03-30 23:48:46 +00:00
|
|
|
event.state_key = opts.skey;
|
2022-12-12 11:24:14 +00:00
|
|
|
} else if (
|
|
|
|
[
|
|
|
|
"m.room.name",
|
|
|
|
"m.room.topic",
|
|
|
|
"m.room.create",
|
|
|
|
"m.room.join_rules",
|
|
|
|
"m.room.power_levels",
|
|
|
|
"m.room.topic",
|
|
|
|
"m.room.history_visibility",
|
|
|
|
"m.room.encryption",
|
|
|
|
"m.room.member",
|
|
|
|
"com.example.state",
|
|
|
|
"m.room.guest_access",
|
|
|
|
"m.room.tombstone",
|
|
|
|
].indexOf(opts.type) !== -1
|
|
|
|
) {
|
2016-03-30 23:48:46 +00:00
|
|
|
event.state_key = "";
|
|
|
|
}
|
2022-03-26 22:06:25 +00:00
|
|
|
|
2022-12-12 11:24:14 +00:00
|
|
|
const mxEvent = opts.event ? new MatrixEvent(event) : (event as unknown as MatrixEvent);
|
2022-03-26 22:06:25 +00:00
|
|
|
if (!mxEvent.sender && opts.user && opts.room) {
|
|
|
|
mxEvent.sender = {
|
|
|
|
userId: opts.user,
|
|
|
|
membership: "join",
|
|
|
|
name: opts.user,
|
|
|
|
rawDisplayName: opts.user,
|
|
|
|
roomId: opts.room,
|
|
|
|
getAvatarUrl: () => {},
|
|
|
|
getMxcAvatarUrl: () => {},
|
|
|
|
} as unknown as RoomMember;
|
|
|
|
}
|
|
|
|
return mxEvent;
|
2017-10-11 16:56:17 +00:00
|
|
|
}
|
2016-03-30 23:48:46 +00:00
|
|
|
|
2022-11-25 13:54:06 +00:00
|
|
|
/**
|
|
|
|
* Create an m.room.encrypted event
|
|
|
|
*
|
|
|
|
* @param opts - Values for the event
|
|
|
|
* @param opts.room - The ID of the room for the event
|
|
|
|
* @param opts.user - The sender of the event
|
|
|
|
* @param opts.plainType - The type the event will have, once it has been decrypted
|
|
|
|
* @param opts.plainContent - The content the event will have, once it has been decrypted
|
|
|
|
*/
|
|
|
|
export async function mkEncryptedEvent(opts: {
|
|
|
|
room: Room["roomId"];
|
|
|
|
user: User["userId"];
|
|
|
|
plainType: string;
|
|
|
|
plainContent: IContent;
|
|
|
|
}): Promise<MatrixEvent> {
|
|
|
|
// we construct an event which has been decrypted by stubbing out CryptoBackend.decryptEvent and then
|
|
|
|
// calling MatrixEvent.attemptDecryption.
|
|
|
|
|
|
|
|
const mxEvent = mkEvent({
|
|
|
|
type: "m.room.encrypted",
|
|
|
|
room: opts.room,
|
|
|
|
user: opts.user,
|
|
|
|
event: true,
|
|
|
|
content: {},
|
|
|
|
});
|
|
|
|
|
|
|
|
const decryptionResult: IEventDecryptionResult = {
|
|
|
|
claimedEd25519Key: "",
|
|
|
|
clearEvent: {
|
|
|
|
type: opts.plainType,
|
|
|
|
content: opts.plainContent,
|
|
|
|
},
|
|
|
|
forwardingCurve25519KeyChain: [],
|
|
|
|
senderCurve25519Key: "",
|
|
|
|
untrusted: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
const mockCrypto = {
|
|
|
|
decryptEvent: async (_ev): Promise<IEventDecryptionResult> => decryptionResult,
|
|
|
|
} as CryptoBackend;
|
|
|
|
|
|
|
|
await mxEvent.attemptDecryption(mockCrypto);
|
|
|
|
return mxEvent;
|
|
|
|
}
|
|
|
|
|
2016-03-30 23:48:46 +00:00
|
|
|
/**
|
|
|
|
* Create an m.room.member event.
|
|
|
|
* @param {Object} opts Values for the membership.
|
|
|
|
* @param {string} opts.room The room ID for the event.
|
|
|
|
* @param {string} opts.mship The content.membership for the event.
|
2017-01-18 10:53:17 +00:00
|
|
|
* @param {string} opts.prevMship The prev_content.membership for the event.
|
2021-08-10 06:55:11 +00:00
|
|
|
* @param {number=} opts.ts Optional. Timestamp for the event
|
2016-03-30 23:48:46 +00:00
|
|
|
* @param {string} opts.user The user ID for the event.
|
2017-01-18 10:53:17 +00:00
|
|
|
* @param {RoomMember} opts.target The target of the event.
|
2021-08-10 06:55:11 +00:00
|
|
|
* @param {string=} opts.skey The other user ID for the event if applicable
|
2016-03-30 23:48:46 +00:00
|
|
|
* e.g. for invites/bans.
|
|
|
|
* @param {string} opts.name The content.displayname for the event.
|
2021-08-10 06:55:11 +00:00
|
|
|
* @param {string=} opts.url The content.avatar_url for the event.
|
2016-03-30 23:48:46 +00:00
|
|
|
* @param {boolean} opts.event True to make a MatrixEvent.
|
|
|
|
* @return {Object|MatrixEvent} The event
|
|
|
|
*/
|
2022-12-12 11:24:14 +00:00
|
|
|
export function mkMembership(
|
|
|
|
opts: MakeEventPassThruProps & {
|
|
|
|
room: Room["roomId"];
|
|
|
|
mship: string;
|
|
|
|
prevMship?: string;
|
|
|
|
name?: string;
|
|
|
|
url?: string;
|
|
|
|
skey?: string;
|
|
|
|
target?: RoomMember;
|
|
|
|
},
|
|
|
|
): MatrixEvent {
|
2022-02-23 11:21:11 +00:00
|
|
|
const event: MakeEventProps = {
|
|
|
|
...opts,
|
|
|
|
type: "m.room.member",
|
|
|
|
content: {
|
|
|
|
membership: opts.mship,
|
|
|
|
},
|
|
|
|
};
|
2016-03-30 23:48:46 +00:00
|
|
|
if (!opts.skey) {
|
2022-02-23 11:21:11 +00:00
|
|
|
event.skey = opts.user;
|
2016-03-30 23:48:46 +00:00
|
|
|
}
|
|
|
|
if (!opts.mship) {
|
|
|
|
throw new Error("Missing .mship => " + JSON.stringify(opts));
|
|
|
|
}
|
2022-02-23 11:21:11 +00:00
|
|
|
|
2017-01-18 10:53:17 +00:00
|
|
|
if (opts.prevMship) {
|
2022-02-23 11:21:11 +00:00
|
|
|
event.prev_content = { membership: opts.prevMship };
|
2017-01-18 10:53:17 +00:00
|
|
|
}
|
2022-12-12 11:24:14 +00:00
|
|
|
if (opts.name) {
|
|
|
|
event.content.displayname = opts.name;
|
|
|
|
}
|
|
|
|
if (opts.url) {
|
|
|
|
event.content.avatar_url = opts.url;
|
|
|
|
}
|
2022-02-23 11:21:11 +00:00
|
|
|
const e = mkEvent(event);
|
2017-01-18 10:53:17 +00:00
|
|
|
if (opts.target) {
|
|
|
|
e.target = opts.target;
|
|
|
|
}
|
|
|
|
return e;
|
2017-10-11 16:56:17 +00:00
|
|
|
}
|
2016-03-30 23:48:46 +00:00
|
|
|
|
2023-09-19 11:24:35 +00:00
|
|
|
export function mkRoomMember(
|
|
|
|
roomId: string,
|
|
|
|
userId: string,
|
|
|
|
membership = "join",
|
|
|
|
isKicked = false,
|
|
|
|
prevMemberContent: Partial<IContent> = {},
|
|
|
|
): RoomMember {
|
2022-08-30 19:13:39 +00:00
|
|
|
return {
|
|
|
|
userId,
|
|
|
|
membership,
|
|
|
|
name: userId,
|
|
|
|
rawDisplayName: userId,
|
|
|
|
roomId,
|
2023-09-19 11:24:35 +00:00
|
|
|
events: {
|
|
|
|
member: {
|
|
|
|
getSender: () => undefined,
|
|
|
|
getPrevContent: () => prevMemberContent,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
isKicked: () => isKicked,
|
2022-08-30 19:13:39 +00:00
|
|
|
getAvatarUrl: () => {},
|
|
|
|
getMxcAvatarUrl: () => {},
|
2022-09-16 15:12:27 +00:00
|
|
|
getDMInviter: () => {},
|
2023-02-28 08:58:23 +00:00
|
|
|
off: () => {},
|
2022-08-30 19:13:39 +00:00
|
|
|
} as unknown as RoomMember;
|
|
|
|
}
|
|
|
|
|
2022-02-23 16:12:48 +00:00
|
|
|
export type MessageEventProps = MakeEventPassThruProps & {
|
|
|
|
room: Room["roomId"];
|
|
|
|
relatesTo?: IEventRelation;
|
|
|
|
msg?: string;
|
|
|
|
};
|
|
|
|
|
2023-06-01 07:53:48 +00:00
|
|
|
/**
|
|
|
|
* Creates a "🙃" reaction for the given event.
|
|
|
|
* Uses the same room and user as for the event.
|
|
|
|
*
|
|
|
|
* @returns The reaction event
|
|
|
|
*/
|
|
|
|
export const mkReaction = (event: MatrixEvent, opts: Partial<MakeEventProps> = {}): MatrixEvent => {
|
|
|
|
return mkEvent({
|
|
|
|
event: true,
|
|
|
|
room: event.getRoomId(),
|
|
|
|
type: EventType.Reaction,
|
|
|
|
user: event.getSender()!,
|
|
|
|
content: {
|
|
|
|
"m.relates_to": {
|
|
|
|
rel_type: RelationType.Annotation,
|
|
|
|
event_id: event.getId(),
|
|
|
|
key: "🙃",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
...opts,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2016-03-30 23:48:46 +00:00
|
|
|
/**
|
|
|
|
* Create an m.room.message event.
|
|
|
|
* @param {Object} opts Values for the message
|
|
|
|
* @param {string} opts.room The room ID for the event.
|
|
|
|
* @param {string} opts.user The user ID for the event.
|
2021-08-03 09:06:21 +00:00
|
|
|
* @param {number} opts.ts The timestamp for the event.
|
2016-03-30 23:48:46 +00:00
|
|
|
* @param {boolean} opts.event True to make a MatrixEvent.
|
2021-08-03 09:06:21 +00:00
|
|
|
* @param {string=} opts.msg Optional. The content.body for the event.
|
2023-03-16 14:01:09 +00:00
|
|
|
* @param {string=} opts.format Optional. The content.format for the event.
|
|
|
|
* @param {string=} opts.formattedMsg Optional. The content.formatted_body for the event.
|
2016-03-30 23:48:46 +00:00
|
|
|
* @return {Object|MatrixEvent} The event
|
|
|
|
*/
|
2022-12-12 11:24:14 +00:00
|
|
|
export function mkMessage({
|
|
|
|
msg,
|
2023-03-16 14:01:09 +00:00
|
|
|
format,
|
|
|
|
formattedMsg,
|
2022-12-12 11:24:14 +00:00
|
|
|
relatesTo,
|
|
|
|
...opts
|
2023-03-21 09:23:20 +00:00
|
|
|
}: MakeEventPassThruProps &
|
|
|
|
Pick<MakeEventProps, "id"> & {
|
|
|
|
room: Room["roomId"];
|
|
|
|
msg?: string;
|
|
|
|
format?: string;
|
|
|
|
formattedMsg?: string;
|
|
|
|
}): MatrixEvent {
|
2016-03-30 23:48:46 +00:00
|
|
|
if (!opts.room || !opts.user) {
|
2022-02-23 11:21:11 +00:00
|
|
|
throw new Error("Missing .room or .user from options");
|
2016-03-30 23:48:46 +00:00
|
|
|
}
|
2022-02-23 16:12:48 +00:00
|
|
|
const message = msg ?? "Random->" + Math.random();
|
2022-02-23 11:21:11 +00:00
|
|
|
const event: MakeEventProps = {
|
2023-08-21 19:38:59 +00:00
|
|
|
ts: 0,
|
2022-02-23 11:21:11 +00:00
|
|
|
...opts,
|
|
|
|
type: "m.room.message",
|
|
|
|
content: {
|
|
|
|
msgtype: "m.text",
|
|
|
|
body: message,
|
2023-03-16 14:01:09 +00:00
|
|
|
...(format && formattedMsg ? { format, formatted_body: formattedMsg } : {}),
|
2022-12-12 11:24:14 +00:00
|
|
|
["m.relates_to"]: relatesTo,
|
2022-02-23 11:21:11 +00:00
|
|
|
},
|
2016-03-30 23:48:46 +00:00
|
|
|
};
|
2022-02-23 11:21:11 +00:00
|
|
|
|
|
|
|
return mkEvent(event);
|
2016-09-09 12:37:42 +00:00
|
|
|
}
|
2016-06-17 11:20:26 +00:00
|
|
|
|
2023-01-31 09:58:17 +00:00
|
|
|
export function mkStubRoom(
|
|
|
|
roomId: string | null | undefined = null,
|
|
|
|
name: string | undefined,
|
|
|
|
client: MatrixClient | undefined,
|
|
|
|
): Room {
|
|
|
|
const stubTimeline = { getEvents: (): MatrixEvent[] => [] } as unknown as EventTimeline;
|
2016-06-17 11:20:26 +00:00
|
|
|
return {
|
2023-01-27 11:00:06 +00:00
|
|
|
canInvite: jest.fn(),
|
|
|
|
client,
|
2023-03-03 13:31:51 +00:00
|
|
|
findThreadForEvent: jest.fn(),
|
2023-01-27 11:00:06 +00:00
|
|
|
createThreadsTimelineSets: jest.fn().mockReturnValue(new Promise(() => {})),
|
2016-06-17 11:20:26 +00:00
|
|
|
currentState: {
|
2022-12-12 11:24:14 +00:00
|
|
|
getStateEvents: jest.fn((_type, key) => (key === undefined ? [] : null)),
|
2021-05-19 09:31:05 +00:00
|
|
|
getMember: jest.fn(),
|
2019-12-16 11:12:48 +00:00
|
|
|
mayClientSendStateEvent: jest.fn().mockReturnValue(true),
|
|
|
|
maySendStateEvent: jest.fn().mockReturnValue(true),
|
2022-03-04 23:15:03 +00:00
|
|
|
maySendRedactionForEvent: jest.fn().mockReturnValue(true),
|
2019-12-16 11:12:48 +00:00
|
|
|
maySendEvent: jest.fn().mockReturnValue(true),
|
2022-02-23 11:21:11 +00:00
|
|
|
members: {},
|
2022-01-06 10:09:26 +00:00
|
|
|
getJoinRule: jest.fn().mockReturnValue(JoinRule.Invite),
|
|
|
|
on: jest.fn(),
|
2022-07-29 09:32:03 +00:00
|
|
|
off: jest.fn(),
|
2022-02-23 11:21:11 +00:00
|
|
|
} as unknown as RoomState,
|
2023-05-12 16:27:41 +00:00
|
|
|
eventShouldLiveIn: jest.fn().mockReturnValue({ shouldLiveInRoom: true, shouldLiveInThread: false }),
|
2023-01-27 11:00:06 +00:00
|
|
|
fetchRoomThreads: jest.fn().mockReturnValue(Promise.resolve()),
|
2023-03-22 12:27:24 +00:00
|
|
|
findEventById: jest.fn().mockReturnValue(undefined),
|
2023-01-27 11:00:06 +00:00
|
|
|
findPredecessor: jest.fn().mockReturnValue({ roomId: "", eventId: null }),
|
|
|
|
getAccountData: (_: EventType | string) => undefined as MatrixEvent | undefined,
|
|
|
|
getAltAliases: jest.fn().mockReturnValue([]),
|
2022-12-12 11:24:14 +00:00
|
|
|
getAvatarUrl: () => "mxc://avatar.url/room.png",
|
2023-01-27 11:00:06 +00:00
|
|
|
getCanonicalAlias: jest.fn(),
|
|
|
|
getDMInviter: jest.fn(),
|
|
|
|
getEventReadUpTo: jest.fn(() => null),
|
|
|
|
getInvitedAndJoinedMemberCount: jest.fn().mockReturnValue(1),
|
|
|
|
getJoinRule: jest.fn().mockReturnValue("invite"),
|
|
|
|
getJoinedMemberCount: jest.fn().mockReturnValue(1),
|
|
|
|
getJoinedMembers: jest.fn().mockReturnValue([]),
|
|
|
|
getLiveTimeline: jest.fn().mockReturnValue(stubTimeline),
|
|
|
|
getMember: jest.fn().mockReturnValue({
|
|
|
|
userId: "@member:domain.bla",
|
|
|
|
name: "Member",
|
|
|
|
rawDisplayName: "Member",
|
|
|
|
roomId: roomId,
|
|
|
|
getAvatarUrl: () => "mxc://avatar.url/image.png",
|
|
|
|
getMxcAvatarUrl: () => "mxc://avatar.url/image.png",
|
2023-09-19 11:24:35 +00:00
|
|
|
events: {},
|
|
|
|
isKicked: () => false,
|
2023-01-27 11:00:06 +00:00
|
|
|
}),
|
|
|
|
getMembers: jest.fn().mockReturnValue([]),
|
|
|
|
getMembersWithMembership: jest.fn().mockReturnValue([]),
|
2022-12-12 11:24:14 +00:00
|
|
|
getMxcAvatarUrl: () => "mxc://avatar.url/room.png",
|
2023-01-27 11:00:06 +00:00
|
|
|
getMyMembership: jest.fn().mockReturnValue("join"),
|
|
|
|
getPendingEvents: () => [] as MatrixEvent[],
|
|
|
|
getReceiptsForEvent: jest.fn().mockReturnValue([]),
|
|
|
|
getRecommendedVersion: jest.fn().mockReturnValue(Promise.resolve("")),
|
|
|
|
getThreads: jest.fn().mockReturnValue([]),
|
2022-10-24 08:58:36 +00:00
|
|
|
getType: jest.fn().mockReturnValue(undefined),
|
2023-01-27 11:00:06 +00:00
|
|
|
getUnfilteredTimelineSet: jest.fn(),
|
2021-04-23 11:19:08 +00:00
|
|
|
getUnreadNotificationCount: jest.fn(() => 0),
|
2023-01-27 11:00:06 +00:00
|
|
|
getVersion: jest.fn().mockReturnValue("1"),
|
|
|
|
hasMembershipState: () => false,
|
|
|
|
isElementVideoRoom: jest.fn().mockReturnValue(false),
|
|
|
|
isSpaceRoom: jest.fn().mockReturnValue(false),
|
2022-03-01 08:33:29 +00:00
|
|
|
loadMembersIfNeeded: jest.fn(),
|
2023-01-27 11:00:06 +00:00
|
|
|
maySendMessage: jest.fn().mockReturnValue(true),
|
2022-04-21 11:41:38 +00:00
|
|
|
myUserId: client?.getUserId(),
|
2023-01-27 11:00:06 +00:00
|
|
|
name,
|
|
|
|
normalizedName: normalize(name || ""),
|
|
|
|
off: jest.fn(),
|
|
|
|
on: jest.fn(),
|
|
|
|
removeListener: jest.fn(),
|
|
|
|
roomId,
|
|
|
|
setBlacklistUnverifiedDevices: jest.fn(),
|
|
|
|
setUnreadNotificationCount: jest.fn(),
|
|
|
|
shouldUpgradeToVersion: (() => null) as () => string | null,
|
|
|
|
tags: {},
|
|
|
|
timeline: [],
|
2022-02-23 11:21:11 +00:00
|
|
|
} as unknown as Room;
|
2016-09-09 12:37:42 +00:00
|
|
|
}
|
2017-05-24 15:56:13 +00:00
|
|
|
|
2023-06-22 10:15:44 +00:00
|
|
|
export function mkServerConfig(
|
|
|
|
hsUrl: string,
|
|
|
|
isUrl: string,
|
2023-10-11 21:44:46 +00:00
|
|
|
delegatedAuthentication?: OidcClientConfig,
|
2023-06-22 10:15:44 +00:00
|
|
|
): ValidatedServerConfig {
|
2023-04-03 08:26:55 +00:00
|
|
|
return {
|
2019-05-03 05:46:43 +00:00
|
|
|
hsUrl,
|
|
|
|
hsName: "TEST_ENVIRONMENT",
|
|
|
|
hsNameIsDifferent: false, // yes, we lie
|
|
|
|
isUrl,
|
2023-06-22 10:15:44 +00:00
|
|
|
delegatedAuthentication,
|
2023-04-03 08:26:55 +00:00
|
|
|
} as ValidatedServerConfig;
|
2019-05-03 05:46:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-23 11:21:11 +00:00
|
|
|
// These methods make some use of some private methods on the AsyncStoreWithClient to simplify getting into a consistent
|
|
|
|
// ready state without needing to wire up a dispatcher and pretend to be a js-sdk client.
|
2018-02-06 17:50:53 +00:00
|
|
|
|
2023-01-31 09:58:17 +00:00
|
|
|
export const setupAsyncStoreWithClient = async <T extends Object = any>(
|
|
|
|
store: AsyncStoreWithClient<T>,
|
|
|
|
client: MatrixClient,
|
|
|
|
) => {
|
|
|
|
// @ts-ignore protected access
|
2022-02-23 11:21:11 +00:00
|
|
|
store.readyStore.useUnitTestClient(client);
|
2023-01-31 09:58:17 +00:00
|
|
|
// @ts-ignore protected access
|
2022-02-23 11:21:11 +00:00
|
|
|
await store.onReady();
|
|
|
|
};
|
|
|
|
|
2023-01-31 09:58:17 +00:00
|
|
|
export const resetAsyncStoreWithClient = async <T extends Object = any>(store: AsyncStoreWithClient<T>) => {
|
|
|
|
// @ts-ignore protected access
|
2022-02-23 11:21:11 +00:00
|
|
|
await store.onNotReady();
|
|
|
|
};
|
|
|
|
|
2022-03-09 13:23:58 +00:00
|
|
|
export const mockStateEventImplementation = (events: MatrixEvent[]) => {
|
2022-02-23 11:21:11 +00:00
|
|
|
const stateMap = new EnhancedMap<string, Map<string, MatrixEvent>>();
|
2022-12-12 11:24:14 +00:00
|
|
|
events.forEach((event) => {
|
2023-01-31 09:58:17 +00:00
|
|
|
stateMap.getOrCreate(event.getType(), new Map()).set(event.getStateKey()!, event);
|
2022-02-23 11:21:11 +00:00
|
|
|
});
|
2018-02-06 17:50:53 +00:00
|
|
|
|
2022-03-09 13:23:58 +00:00
|
|
|
// recreate the overloading in RoomState
|
|
|
|
function getStateEvents(eventType: EventType | string): MatrixEvent[];
|
|
|
|
function getStateEvents(eventType: EventType | string, stateKey: string): MatrixEvent;
|
|
|
|
function getStateEvents(eventType: EventType | string, stateKey?: string) {
|
2022-02-23 11:21:11 +00:00
|
|
|
if (stateKey || stateKey === "") {
|
|
|
|
return stateMap.get(eventType)?.get(stateKey) || null;
|
2018-02-06 17:50:53 +00:00
|
|
|
}
|
2022-02-23 11:21:11 +00:00
|
|
|
return Array.from(stateMap.get(eventType)?.values() || []);
|
2022-03-09 13:23:58 +00:00
|
|
|
}
|
|
|
|
return getStateEvents;
|
2022-02-23 11:21:11 +00:00
|
|
|
};
|
|
|
|
|
2022-03-11 16:03:33 +00:00
|
|
|
export const mkRoom = (
|
|
|
|
client: MatrixClient,
|
|
|
|
roomId: string,
|
|
|
|
rooms?: ReturnType<typeof mkStubRoom>[],
|
|
|
|
): MockedObject<Room> => {
|
|
|
|
const room = mocked(mkStubRoom(roomId, roomId, client));
|
2022-02-23 11:21:11 +00:00
|
|
|
mocked(room.currentState).getStateEvents.mockImplementation(mockStateEventImplementation([]));
|
|
|
|
rooms?.push(room);
|
|
|
|
return room;
|
|
|
|
};
|
2018-05-02 10:19:01 +00:00
|
|
|
|
|
|
|
/**
|
2022-02-23 11:21:11 +00:00
|
|
|
* Upserts given events into room.currentState
|
|
|
|
* @param room
|
|
|
|
* @param events
|
2018-05-02 10:19:01 +00:00
|
|
|
*/
|
2022-02-23 11:21:11 +00:00
|
|
|
export const upsertRoomStateEvents = (room: Room, events: MatrixEvent[]): void => {
|
|
|
|
const eventsMap = events.reduce((acc, event) => {
|
|
|
|
const eventType = event.getType();
|
|
|
|
if (!acc.has(eventType)) {
|
|
|
|
acc.set(eventType, new Map());
|
|
|
|
}
|
2023-01-31 09:58:17 +00:00
|
|
|
acc.get(eventType)?.set(event.getStateKey()!, event);
|
2022-02-23 11:21:11 +00:00
|
|
|
return acc;
|
|
|
|
}, room.currentState.events || new Map<string, Map<string, MatrixEvent>>());
|
2018-05-02 10:19:01 +00:00
|
|
|
|
2022-02-23 11:21:11 +00:00
|
|
|
room.currentState.events = eventsMap;
|
|
|
|
};
|
2018-05-02 10:19:01 +00:00
|
|
|
|
2022-02-23 11:21:11 +00:00
|
|
|
export const mkSpace = (
|
|
|
|
client: MatrixClient,
|
|
|
|
spaceId: string,
|
|
|
|
rooms?: ReturnType<typeof mkStubRoom>[],
|
|
|
|
children: string[] = [],
|
2022-03-11 16:03:33 +00:00
|
|
|
): MockedObject<Room> => {
|
|
|
|
const space = mocked(mkRoom(client, spaceId, rooms));
|
|
|
|
space.isSpaceRoom.mockReturnValue(true);
|
2022-10-24 08:58:36 +00:00
|
|
|
space.getType.mockReturnValue(RoomType.Space);
|
2022-12-12 11:24:14 +00:00
|
|
|
mocked(space.currentState).getStateEvents.mockImplementation(
|
|
|
|
mockStateEventImplementation(
|
|
|
|
children.map((roomId) =>
|
|
|
|
mkEvent({
|
|
|
|
event: true,
|
|
|
|
type: EventType.SpaceChild,
|
|
|
|
room: spaceId,
|
|
|
|
user: "@user:server",
|
|
|
|
skey: roomId,
|
|
|
|
content: { via: [] },
|
|
|
|
ts: Date.now(),
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
2022-02-23 11:21:11 +00:00
|
|
|
return space;
|
|
|
|
};
|
2022-09-27 12:35:54 +00:00
|
|
|
|
2023-03-08 12:06:50 +00:00
|
|
|
export const mkRoomMemberJoinEvent = (user: string, room: string, content?: IContent): MatrixEvent => {
|
2022-11-10 08:38:48 +00:00
|
|
|
return mkEvent({
|
|
|
|
event: true,
|
|
|
|
type: EventType.RoomMember,
|
|
|
|
content: {
|
|
|
|
membership: "join",
|
2023-03-08 12:06:50 +00:00
|
|
|
...content,
|
2022-11-10 08:38:48 +00:00
|
|
|
},
|
|
|
|
skey: user,
|
|
|
|
user,
|
|
|
|
room,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2023-03-08 12:06:50 +00:00
|
|
|
export const mkRoomCanonicalAliasEvent = (userId: string, roomId: string, alias: string): MatrixEvent => {
|
|
|
|
return mkEvent({
|
|
|
|
event: true,
|
|
|
|
type: EventType.RoomCanonicalAlias,
|
|
|
|
content: {
|
|
|
|
alias,
|
|
|
|
},
|
|
|
|
skey: "",
|
|
|
|
user: userId,
|
|
|
|
room: roomId,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2023-03-02 08:44:12 +00:00
|
|
|
export const mkThirdPartyInviteEvent = (user: string, displayName: string, room: string): MatrixEvent => {
|
|
|
|
return mkEvent({
|
|
|
|
event: true,
|
|
|
|
type: EventType.RoomThirdPartyInvite,
|
|
|
|
content: {
|
|
|
|
display_name: displayName,
|
|
|
|
},
|
|
|
|
skey: "test" + Math.random(),
|
|
|
|
user,
|
|
|
|
room,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2022-09-27 12:35:54 +00:00
|
|
|
export const mkPusher = (extra: Partial<IPusher> = {}): IPusher => ({
|
|
|
|
app_display_name: "app",
|
|
|
|
app_id: "123",
|
|
|
|
data: {},
|
|
|
|
device_display_name: "name",
|
|
|
|
kind: "http",
|
|
|
|
lang: "en",
|
|
|
|
pushkey: "pushpush",
|
|
|
|
...extra,
|
|
|
|
});
|
2023-01-31 09:58:17 +00:00
|
|
|
|
|
|
|
/** Add a mute rule for a room. */
|
|
|
|
export function muteRoom(room: Room): void {
|
|
|
|
const client = room.client!;
|
|
|
|
client.pushRules = client.pushRules ?? ({ global: [] } as IPushRules);
|
|
|
|
client.pushRules.global = client.pushRules.global ?? {};
|
|
|
|
client.pushRules.global.override = [
|
|
|
|
{
|
|
|
|
default: true,
|
|
|
|
enabled: true,
|
|
|
|
rule_id: "rule_id",
|
|
|
|
conditions: [
|
|
|
|
{
|
|
|
|
kind: ConditionKind.EventMatch,
|
|
|
|
key: "room_id",
|
|
|
|
pattern: room.roomId,
|
|
|
|
},
|
|
|
|
],
|
2023-06-28 14:07:02 +00:00
|
|
|
actions: [],
|
2023-01-31 09:58:17 +00:00
|
|
|
},
|
|
|
|
];
|
|
|
|
}
|