2018-02-06 18:50:53 +01:00
|
|
|
import React from 'react';
|
2021-06-29 14:11:58 +02:00
|
|
|
import { MatrixClientPeg as peg } from '../src/MatrixClientPeg';
|
2020-05-14 04:41:41 +02:00
|
|
|
import dis from '../src/dispatcher/dispatcher';
|
2021-06-29 14:11:58 +02:00
|
|
|
import { makeType } from "../src/utils/TypeUtils";
|
|
|
|
import { ValidatedServerConfig } from "../src/utils/AutoDiscoveryUtils";
|
2019-09-17 22:33:32 +02:00
|
|
|
import ShallowRenderer from 'react-test-renderer/shallow';
|
2019-12-17 18:26:12 +01:00
|
|
|
import MatrixClientContext from "../src/contexts/MatrixClientContext";
|
2021-06-29 14:11:58 +02:00
|
|
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
2017-03-16 18:26:42 +01:00
|
|
|
|
2019-09-17 22:33:32 +02:00
|
|
|
export function getRenderer() {
|
|
|
|
// Old: ReactTestUtils.createRenderer();
|
|
|
|
return new ShallowRenderer();
|
|
|
|
}
|
2016-03-31 01:48:46 +02:00
|
|
|
|
2016-03-28 23:59:34 +02:00
|
|
|
/**
|
|
|
|
* Stub out the MatrixClient, and configure the MatrixClientPeg object to
|
|
|
|
* return it when get() is called.
|
2016-04-07 17:47:17 +02:00
|
|
|
*
|
2016-11-14 19:20:15 +01: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.
|
2016-03-28 23:59:34 +02:00
|
|
|
*/
|
2016-09-09 14:37:42 +02:00
|
|
|
export function stubClient() {
|
2017-10-11 18:56:17 +02:00
|
|
|
const client = createTestClient();
|
2016-11-14 19:20:15 +01:00
|
|
|
|
|
|
|
// stub out the methods in MatrixClientPeg
|
|
|
|
//
|
|
|
|
// 'sandbox.restore()' doesn't work correctly on inherited methods,
|
|
|
|
// so we do this for each method
|
2017-10-11 18:56:17 +02:00
|
|
|
const methods = ['get', 'unset', 'replaceUsingCreds'];
|
|
|
|
for (let i = 0; i < methods.length; i++) {
|
2019-12-16 12:12:48 +01:00
|
|
|
peg[methods[i]] = jest.spyOn(peg, methods[i]);
|
2016-11-14 19:20:15 +01:00
|
|
|
}
|
|
|
|
// MatrixClientPeg.get() is called a /lot/, so implement it with our own
|
|
|
|
// fast stub function rather than a sinon stub
|
|
|
|
peg.get = function() { return client; };
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a stubbed-out MatrixClient
|
|
|
|
*
|
|
|
|
* @returns {object} MatrixClient stub
|
|
|
|
*/
|
|
|
|
export function createTestClient() {
|
|
|
|
return {
|
2019-12-16 12:12:48 +01:00
|
|
|
getHomeserverUrl: jest.fn(),
|
|
|
|
getIdentityServerUrl: jest.fn(),
|
|
|
|
getDomain: jest.fn().mockReturnValue("matrix.rog"),
|
|
|
|
getUserId: jest.fn().mockReturnValue("@userId:matrix.rog"),
|
2016-04-08 15:50:04 +02:00
|
|
|
|
2019-12-16 12:12:48 +01:00
|
|
|
getPushActionsForEvent: jest.fn(),
|
2020-02-13 23:25:54 +01:00
|
|
|
getRoom: jest.fn().mockImplementation(mkStubRoom),
|
2019-12-16 12:12:48 +01:00
|
|
|
getRooms: jest.fn().mockReturnValue([]),
|
|
|
|
getVisibleRooms: jest.fn().mockReturnValue([]),
|
|
|
|
getGroups: jest.fn().mockReturnValue([]),
|
|
|
|
loginFlows: jest.fn(),
|
|
|
|
on: jest.fn(),
|
|
|
|
removeListener: jest.fn(),
|
|
|
|
isRoomEncrypted: jest.fn().mockReturnValue(false),
|
|
|
|
peekInRoom: jest.fn().mockResolvedValue(mkStubRoom()),
|
2016-04-08 15:50:04 +02:00
|
|
|
|
2019-12-16 12:12:48 +01: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 15:39:39 +02:00
|
|
|
getThirdpartyProtocols: jest.fn().mockResolvedValue({}),
|
|
|
|
getClientWellKnown: jest.fn().mockReturnValue(null),
|
|
|
|
supportsVoip: jest.fn().mockReturnValue(true),
|
|
|
|
getTurnServersExpiry: jest.fn().mockReturnValue(2^32),
|
|
|
|
getThirdpartyUser: jest.fn().mockResolvedValue([]),
|
2016-09-09 14:37:42 +02:00
|
|
|
getAccountData: (type) => {
|
|
|
|
return mkEvent({
|
|
|
|
type,
|
|
|
|
event: true,
|
|
|
|
content: {},
|
|
|
|
});
|
|
|
|
},
|
2018-04-27 15:28:24 +02:00
|
|
|
mxcUrlToHttp: (mxc) => 'http://this.is.a.url/',
|
2019-12-16 12:12:48 +01:00
|
|
|
setAccountData: jest.fn(),
|
|
|
|
sendTyping: jest.fn().mockResolvedValue({}),
|
|
|
|
sendMessage: () => jest.fn().mockResolvedValue({}),
|
2016-10-10 18:51:26 +02:00
|
|
|
getSyncState: () => "SYNCING",
|
2017-02-24 12:41:23 +01:00
|
|
|
generateClientSecret: () => "t35tcl1Ent5ECr3T",
|
2017-05-02 11:14:54 +02:00
|
|
|
isGuest: () => false,
|
2020-10-07 01:09:48 +02:00
|
|
|
isCryptoEnabled: () => false,
|
2021-04-23 15:45:22 +02:00
|
|
|
getSpaceSummary: jest.fn().mockReturnValue({
|
|
|
|
rooms: [],
|
2021-07-29 13:16:59 +02:00
|
|
|
}),
|
2021-07-29 18:35:15 +02:00
|
|
|
getRoomHierarchy: jest.fn().mockReturnValue({
|
2021-07-29 13:16:59 +02:00
|
|
|
rooms: [],
|
2021-04-23 15:45:22 +02:00
|
|
|
}),
|
2021-04-22 00:45:21 +02:00
|
|
|
|
|
|
|
// Used by various internal bits we aren't concerned with (yet)
|
2021-06-02 05:36:28 +02:00
|
|
|
sessionStore: {
|
2021-04-22 00:45:21 +02:00
|
|
|
store: {
|
|
|
|
getItem: jest.fn(),
|
|
|
|
},
|
|
|
|
},
|
2021-05-18 14:46:47 +02:00
|
|
|
decryptEventIfNeeded: () => Promise.resolve(),
|
2021-07-15 19:17:07 +02:00
|
|
|
isUserIgnored: jest.fn().mockReturnValue(false),
|
2021-07-06 11:34:50 +02:00
|
|
|
getCapabilities: jest.fn().mockResolvedValue({}),
|
2016-04-08 15:50:04 +02:00
|
|
|
};
|
2016-03-28 23:59:34 +02:00
|
|
|
}
|
|
|
|
|
2016-03-31 01:48:46 +02: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 15:45:13 +02:00
|
|
|
* @param {string=} opts.skey Optional. The state key (auto inserts empty string)
|
|
|
|
* @param {number=} opts.ts Optional. Timestamp for the event
|
2016-03-31 01:48:46 +02:00
|
|
|
* @param {Object} opts.content The event.content
|
|
|
|
* @param {boolean} opts.event True to make a MatrixEvent.
|
|
|
|
* @return {Object} a JSON object representing this event.
|
|
|
|
*/
|
2016-09-09 14:37:42 +02:00
|
|
|
export function mkEvent(opts) {
|
2016-03-31 01:48:46 +02:00
|
|
|
if (!opts.type || !opts.content) {
|
|
|
|
throw new Error("Missing .type or .content =>" + JSON.stringify(opts));
|
|
|
|
}
|
2017-10-11 18:56:17 +02:00
|
|
|
const event = {
|
2016-03-31 01:48:46 +02:00
|
|
|
type: opts.type,
|
|
|
|
room_id: opts.room,
|
|
|
|
sender: opts.user,
|
|
|
|
content: opts.content,
|
2017-01-18 11:53:17 +01:00
|
|
|
prev_content: opts.prev_content,
|
2016-03-31 01:48:46 +02:00
|
|
|
event_id: "$" + Math.random() + "-" + Math.random(),
|
|
|
|
origin_server_ts: opts.ts,
|
|
|
|
};
|
|
|
|
if (opts.skey) {
|
|
|
|
event.state_key = opts.skey;
|
2017-10-11 18:56:17 +02:00
|
|
|
} else if (["m.room.name", "m.room.topic", "m.room.create", "m.room.join_rules",
|
2020-02-13 23:25:54 +01:00
|
|
|
"m.room.power_levels", "m.room.topic", "m.room.history_visibility", "m.room.encryption",
|
2016-03-31 01:48:46 +02:00
|
|
|
"com.example.state"].indexOf(opts.type) !== -1) {
|
|
|
|
event.state_key = "";
|
|
|
|
}
|
|
|
|
return opts.event ? new MatrixEvent(event) : event;
|
2017-10-11 18:56:17 +02:00
|
|
|
}
|
2016-03-31 01:48:46 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an m.presence event.
|
|
|
|
* @param {Object} opts Values for the presence.
|
|
|
|
* @return {Object|MatrixEvent} The event
|
|
|
|
*/
|
2016-09-09 14:37:42 +02:00
|
|
|
export function mkPresence(opts) {
|
2016-03-31 01:48:46 +02:00
|
|
|
if (!opts.user) {
|
|
|
|
throw new Error("Missing user");
|
|
|
|
}
|
2017-10-11 18:56:17 +02:00
|
|
|
const event = {
|
2016-03-31 01:48:46 +02:00
|
|
|
event_id: "$" + Math.random() + "-" + Math.random(),
|
|
|
|
type: "m.presence",
|
|
|
|
sender: opts.user,
|
|
|
|
content: {
|
|
|
|
avatar_url: opts.url,
|
|
|
|
displayname: opts.name,
|
|
|
|
last_active_ago: opts.ago,
|
2017-10-11 18:56:17 +02:00
|
|
|
presence: opts.presence || "offline",
|
|
|
|
},
|
2016-03-31 01:48:46 +02:00
|
|
|
};
|
|
|
|
return opts.event ? new MatrixEvent(event) : event;
|
2017-10-11 18:56:17 +02:00
|
|
|
}
|
2016-03-31 01:48:46 +02: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 11:53:17 +01:00
|
|
|
* @param {string} opts.prevMship The prev_content.membership for the event.
|
2016-03-31 01:48:46 +02:00
|
|
|
* @param {string} opts.user The user ID for the event.
|
2017-01-18 11:53:17 +01:00
|
|
|
* @param {RoomMember} opts.target The target of the event.
|
2016-03-31 01:48:46 +02:00
|
|
|
* @param {string} opts.skey The other user ID for the event if applicable
|
|
|
|
* e.g. for invites/bans.
|
|
|
|
* @param {string} opts.name The content.displayname for the event.
|
|
|
|
* @param {string} opts.url The content.avatar_url for the event.
|
|
|
|
* @param {boolean} opts.event True to make a MatrixEvent.
|
|
|
|
* @return {Object|MatrixEvent} The event
|
|
|
|
*/
|
2016-09-09 14:37:42 +02:00
|
|
|
export function mkMembership(opts) {
|
2016-03-31 01:48:46 +02:00
|
|
|
opts.type = "m.room.member";
|
|
|
|
if (!opts.skey) {
|
|
|
|
opts.skey = opts.user;
|
|
|
|
}
|
|
|
|
if (!opts.mship) {
|
|
|
|
throw new Error("Missing .mship => " + JSON.stringify(opts));
|
|
|
|
}
|
|
|
|
opts.content = {
|
2017-10-11 18:56:17 +02:00
|
|
|
membership: opts.mship,
|
2016-03-31 01:48:46 +02:00
|
|
|
};
|
2017-01-18 11:53:17 +01:00
|
|
|
if (opts.prevMship) {
|
|
|
|
opts.prev_content = { membership: opts.prevMship };
|
|
|
|
}
|
2016-03-31 01:48:46 +02:00
|
|
|
if (opts.name) { opts.content.displayname = opts.name; }
|
|
|
|
if (opts.url) { opts.content.avatar_url = opts.url; }
|
2017-10-11 18:56:17 +02:00
|
|
|
const e = mkEvent(opts);
|
2017-01-18 11:53:17 +01:00
|
|
|
if (opts.target) {
|
|
|
|
e.target = opts.target;
|
|
|
|
}
|
|
|
|
return e;
|
2017-10-11 18:56:17 +02:00
|
|
|
}
|
2016-03-31 01:48:46 +02: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.
|
|
|
|
* @param {string} opts.msg Optional. The content.body for the event.
|
|
|
|
* @param {boolean} opts.event True to make a MatrixEvent.
|
|
|
|
* @return {Object|MatrixEvent} The event
|
|
|
|
*/
|
2016-09-09 14:37:42 +02:00
|
|
|
export function mkMessage(opts) {
|
2016-03-31 01:48:46 +02:00
|
|
|
opts.type = "m.room.message";
|
|
|
|
if (!opts.msg) {
|
|
|
|
opts.msg = "Random->" + Math.random();
|
|
|
|
}
|
|
|
|
if (!opts.room || !opts.user) {
|
|
|
|
throw new Error("Missing .room or .user from", opts);
|
|
|
|
}
|
|
|
|
opts.content = {
|
|
|
|
msgtype: "m.text",
|
2017-10-11 18:56:17 +02:00
|
|
|
body: opts.msg,
|
2016-03-31 01:48:46 +02:00
|
|
|
};
|
2016-09-09 14:37:42 +02:00
|
|
|
return mkEvent(opts);
|
|
|
|
}
|
2016-06-17 13:20:26 +02:00
|
|
|
|
2021-07-06 12:35:56 +02:00
|
|
|
export function mkStubRoom(roomId = null, name, client) {
|
2017-10-11 18:56:17 +02:00
|
|
|
const stubTimeline = { getEvents: () => [] };
|
2016-06-17 13:20:26 +02:00
|
|
|
return {
|
2016-09-09 14:37:42 +02:00
|
|
|
roomId,
|
2019-12-16 12:12:48 +01:00
|
|
|
getReceiptsForEvent: jest.fn().mockReturnValue([]),
|
|
|
|
getMember: jest.fn().mockReturnValue({
|
2017-07-24 15:42:20 +02:00
|
|
|
userId: '@member:domain.bla',
|
|
|
|
name: 'Member',
|
2020-01-06 14:28:29 +01:00
|
|
|
rawDisplayName: 'Member',
|
2017-07-24 15:42:20 +02:00
|
|
|
roomId: roomId,
|
|
|
|
getAvatarUrl: () => 'mxc://avatar.url/image.png',
|
2021-03-11 17:42:55 +01:00
|
|
|
getMxcAvatarUrl: () => 'mxc://avatar.url/image.png',
|
2017-07-24 15:42:20 +02:00
|
|
|
}),
|
2019-12-16 12:12:48 +01:00
|
|
|
getMembersWithMembership: jest.fn().mockReturnValue([]),
|
|
|
|
getJoinedMembers: jest.fn().mockReturnValue([]),
|
2021-07-06 12:35:56 +02:00
|
|
|
getJoinedMemberCount: jest.fn().mockReturnValue(1),
|
2021-05-19 13:34:27 +02:00
|
|
|
getMembers: jest.fn().mockReturnValue([]),
|
2016-10-10 18:51:26 +02:00
|
|
|
getPendingEvents: () => [],
|
|
|
|
getLiveTimeline: () => stubTimeline,
|
|
|
|
getUnfilteredTimelineSet: () => null,
|
2021-05-10 06:54:00 +02:00
|
|
|
findEventById: () => null,
|
2016-10-10 18:51:26 +02:00
|
|
|
getAccountData: () => null,
|
|
|
|
hasMembershipState: () => null,
|
2018-08-15 13:22:26 +02:00
|
|
|
getVersion: () => '1',
|
2018-08-17 16:15:53 +02:00
|
|
|
shouldUpgradeToVersion: () => null,
|
2021-04-23 13:19:08 +02:00
|
|
|
getMyMembership: jest.fn().mockReturnValue("join"),
|
2019-12-16 12:12:48 +01:00
|
|
|
maySendMessage: jest.fn().mockReturnValue(true),
|
2016-06-17 13:20:26 +02:00
|
|
|
currentState: {
|
2019-12-16 12:12:48 +01:00
|
|
|
getStateEvents: jest.fn(),
|
2021-05-19 11:31:05 +02:00
|
|
|
getMember: jest.fn(),
|
2019-12-16 12:12:48 +01:00
|
|
|
mayClientSendStateEvent: jest.fn().mockReturnValue(true),
|
|
|
|
maySendStateEvent: jest.fn().mockReturnValue(true),
|
|
|
|
maySendEvent: jest.fn().mockReturnValue(true),
|
2016-06-17 13:20:26 +02:00
|
|
|
members: [],
|
|
|
|
},
|
2021-04-23 13:19:08 +02:00
|
|
|
tags: {},
|
2019-12-16 12:12:48 +01:00
|
|
|
setBlacklistUnverifiedDevices: jest.fn(),
|
|
|
|
on: jest.fn(),
|
2021-05-16 14:39:22 +02:00
|
|
|
off: jest.fn(),
|
2019-12-16 12:12:48 +01:00
|
|
|
removeListener: jest.fn(),
|
2020-11-05 17:27:41 +01:00
|
|
|
getDMInviter: jest.fn(),
|
2021-05-09 01:51:51 +02:00
|
|
|
name,
|
2021-02-03 16:18:19 +01:00
|
|
|
getAvatarUrl: () => 'mxc://avatar.url/room.png',
|
2021-03-11 17:42:55 +01:00
|
|
|
getMxcAvatarUrl: () => 'mxc://avatar.url/room.png',
|
2021-04-22 15:45:13 +02:00
|
|
|
isSpaceRoom: jest.fn(() => false),
|
2021-04-23 13:19:08 +02:00
|
|
|
getUnreadNotificationCount: jest.fn(() => 0),
|
|
|
|
getEventReadUpTo: jest.fn(() => null),
|
2021-06-02 02:36:28 +02:00
|
|
|
getCanonicalAlias: jest.fn(),
|
|
|
|
getAltAliases: jest.fn().mockReturnValue([]),
|
2021-04-23 13:19:08 +02:00
|
|
|
timeline: [],
|
2021-07-06 11:44:09 +02:00
|
|
|
getJoinRule: jest.fn().mockReturnValue("invite"),
|
2021-07-06 12:35:56 +02:00
|
|
|
client,
|
2016-06-17 13:20:26 +02:00
|
|
|
};
|
2016-09-09 14:37:42 +02:00
|
|
|
}
|
2017-05-24 17:56:13 +02:00
|
|
|
|
2019-05-03 07:46:43 +02:00
|
|
|
export function mkServerConfig(hsUrl, isUrl) {
|
|
|
|
return makeType(ValidatedServerConfig, {
|
|
|
|
hsUrl,
|
|
|
|
hsName: "TEST_ENVIRONMENT",
|
|
|
|
hsNameIsDifferent: false, // yes, we lie
|
|
|
|
isUrl,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-05-24 17:56:13 +02:00
|
|
|
export function getDispatchForStore(store) {
|
|
|
|
// Mock the dispatcher by gut-wrenching. Stores can only __emitChange whilst a
|
|
|
|
// dispatcher `_isDispatching` is true.
|
|
|
|
return (payload) => {
|
|
|
|
dis._isDispatching = true;
|
|
|
|
dis._callbacks[store._dispatchToken](payload);
|
|
|
|
dis._isDispatching = false;
|
|
|
|
};
|
|
|
|
}
|
2018-02-06 18:50:53 +01:00
|
|
|
|
|
|
|
export function wrapInMatrixClientContext(WrappedComponent) {
|
|
|
|
class Wrapper extends React.Component {
|
2019-12-17 18:26:12 +01:00
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
2018-02-06 18:50:53 +01:00
|
|
|
|
|
|
|
this._matrixClient = peg.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2019-12-17 18:26:12 +01:00
|
|
|
return <MatrixClientContext.Provider value={this._matrixClient}>
|
|
|
|
<WrappedComponent ref={this.props.wrappedRef} {...this.props} />
|
|
|
|
</MatrixClientContext.Provider>;
|
2018-02-06 18:50:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return Wrapper;
|
|
|
|
}
|
2018-05-02 12:19:01 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Call fn before calling componentDidUpdate on a react component instance, inst.
|
|
|
|
* @param {React.Component} inst an instance of a React component.
|
2019-12-16 12:12:48 +01:00
|
|
|
* @param {number} updates Number of updates to wait for. (Defaults to 1.)
|
2018-05-02 12:19:01 +02:00
|
|
|
* @returns {Promise} promise that resolves when componentDidUpdate is called on
|
|
|
|
* given component instance.
|
|
|
|
*/
|
2018-12-04 03:38:29 +01:00
|
|
|
export function waitForUpdate(inst, updates = 1) {
|
2018-05-02 12:19:01 +02:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const cdu = inst.componentDidUpdate;
|
|
|
|
|
2018-12-04 03:38:29 +01:00
|
|
|
console.log(`Waiting for ${updates} update(s)`);
|
|
|
|
|
2018-05-02 12:19:01 +02:00
|
|
|
inst.componentDidUpdate = (prevProps, prevState, snapshot) => {
|
2018-12-04 03:38:29 +01:00
|
|
|
updates--;
|
|
|
|
console.log(`Got update, ${updates} remaining`);
|
2018-05-02 12:19:01 +02:00
|
|
|
|
2018-12-04 03:38:29 +01:00
|
|
|
if (updates == 0) {
|
|
|
|
inst.componentDidUpdate = cdu;
|
|
|
|
resolve();
|
|
|
|
}
|
2018-05-02 12:19:01 +02:00
|
|
|
|
2018-12-04 03:38:29 +01:00
|
|
|
if (cdu) cdu(prevProps, prevState, snapshot);
|
2018-05-02 12:19:01 +02:00
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|