Files
element-web/test/test-utils.js
T

373 lines
13 KiB
JavaScript
Raw Normal View History

2018-02-06 17:50:53 +00:00
import React from 'react';
import EventEmitter from "events";
2021-10-22 17:23:32 -05:00
import ShallowRenderer from 'react-test-renderer/shallow';
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { JoinRule } from 'matrix-js-sdk/src/@types/partials';
2021-10-22 17:23:32 -05:00
2021-06-29 13:11:58 +01:00
import { MatrixClientPeg as peg } from '../src/MatrixClientPeg';
2020-05-13 20:41:41 -06:00
import dis from '../src/dispatcher/dispatcher';
2021-06-29 13:11:58 +01:00
import { makeType } from "../src/utils/TypeUtils";
import { ValidatedServerConfig } from "../src/utils/AutoDiscoveryUtils";
2019-12-17 17:26:12 +00:00
import MatrixClientContext from "../src/contexts/MatrixClientContext";
2017-03-16 17:26:42 +00:00
export function getRenderer() {
// Old: ReactTestUtils.createRenderer();
return new ShallowRenderer();
}
2016-03-31 00:48:46 +01:00
2016-03-28 22:59:34 +01:00
/**
* Stub out the MatrixClient, and configure the MatrixClientPeg object to
* return it when get() is called.
*
* 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 22:59:34 +01:00
*/
2016-09-09 18:07:42 +05:30
export function stubClient() {
2017-10-11 17:56:17 +01:00
const client = createTestClient();
// 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 17:56:17 +01:00
const methods = ['get', 'unset', 'replaceUsingCreds'];
for (let i = 0; i < methods.length; i++) {
2019-12-16 11:12:48 +00:00
peg[methods[i]] = jest.spyOn(peg, methods[i]);
}
// 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() {
const eventEmitter = new EventEmitter();
return {
2019-12-16 11:12:48 +00:00
getHomeserverUrl: jest.fn(),
getIdentityServerUrl: jest.fn(),
getDomain: jest.fn().mockReturnValue("matrix.rog"),
getUserId: jest.fn().mockReturnValue("@userId:matrix.rog"),
getUser: jest.fn().mockReturnValue({ on: jest.fn() }),
credentials: { userId: "@userId:matrix.rog" },
2019-12-16 11:12:48 +00:00
getPushActionsForEvent: jest.fn(),
2020-02-13 17:25:54 -05:00
getRoom: jest.fn().mockImplementation(mkStubRoom),
2019-12-16 11:12:48 +00:00
getRooms: jest.fn().mockReturnValue([]),
getVisibleRooms: jest.fn().mockReturnValue([]),
getGroups: jest.fn().mockReturnValue([]),
loginFlows: jest.fn(),
on: eventEmitter.on.bind(eventEmitter),
emit: eventEmitter.emit.bind(eventEmitter),
removeListener: eventEmitter.removeListener.bind(eventEmitter),
2019-12-16 11:12:48 +00:00
isRoomEncrypted: jest.fn().mockReturnValue(false),
peekInRoom: jest.fn().mockResolvedValue(mkStubRoom()),
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 14:39:39 +01: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 18:07:42 +05:30
getAccountData: (type) => {
return mkEvent({
type,
event: true,
content: {},
});
},
mxcUrlToHttp: (mxc) => `http://this.is.a.url/${mxc.substring(6)}`,
2019-12-16 11:12:48 +00:00
setAccountData: jest.fn(),
setRoomAccountData: jest.fn(),
2019-12-16 11:12:48 +00:00
sendTyping: jest.fn().mockResolvedValue({}),
sendMessage: () => jest.fn().mockResolvedValue({}),
sendStateEvent: jest.fn().mockResolvedValue(),
2016-10-10 17:51:26 +01:00
getSyncState: () => "SYNCING",
generateClientSecret: () => "t35tcl1Ent5ECr3T",
2017-05-02 10:14:54 +01:00
isGuest: () => false,
2020-10-07 00:09:48 +01:00
isCryptoEnabled: () => false,
getRoomHierarchy: jest.fn().mockReturnValue({
2021-04-23 14:45:22 +01:00
rooms: [],
}),
2021-04-21 16:45:21 -06:00
// Used by various internal bits we aren't concerned with (yet)
sessionStore: {
2021-04-21 16:45:21 -06:00
store: {
getItem: jest.fn(),
setItem: jest.fn(),
2021-04-21 16:45:21 -06:00
},
},
2021-08-03 15:43:56 +02:00
pushRules: {},
2021-05-18 13:46:47 +01:00
decryptEventIfNeeded: () => Promise.resolve(),
isUserIgnored: jest.fn().mockReturnValue(false),
2021-07-06 10:34:50 +01:00
getCapabilities: jest.fn().mockResolvedValue({}),
supportsExperimentalThreads: () => false,
getRoomUpgradeHistory: jest.fn().mockReturnValue([]),
getOpenIdToken: jest.fn().mockResolvedValue(),
registerWithIdentityServer: jest.fn().mockResolvedValue({}),
getIdentityAccount: jest.fn().mockResolvedValue({}),
getTerms: jest.fn().mockResolvedValueOnce(),
doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(),
2022-01-06 10:47:03 +01:00
getPushRules: jest.fn().mockResolvedValue(),
getPushers: jest.fn().mockResolvedValue({ pushers: [] }),
getThreePids: jest.fn().mockResolvedValue({ threepids: [] }),
setPusher: jest.fn().mockResolvedValue(),
setPushRuleEnabled: jest.fn().mockResolvedValue(),
setPushRuleActions: jest.fn().mockResolvedValue(),
};
2016-03-28 22:59:34 +01:00
}
2016-03-31 00:48:46 +01: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 14:45:13 +01: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 00:48:46 +01:00
* @param {Object} opts.content The event.content
* @param {boolean} opts.event True to make a MatrixEvent.
2021-08-10 12:25:11 +05:30
* @param {unsigned=} opts.unsigned
2016-03-31 00:48:46 +01:00
* @return {Object} a JSON object representing this event.
*/
2016-09-09 18:07:42 +05:30
export function mkEvent(opts) {
2016-03-31 00:48:46 +01:00
if (!opts.type || !opts.content) {
throw new Error("Missing .type or .content =>" + JSON.stringify(opts));
}
2017-10-11 17:56:17 +01:00
const event = {
2016-03-31 00:48:46 +01: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 00:48:46 +01:00
event_id: "$" + Math.random() + "-" + Math.random(),
origin_server_ts: opts.ts,
};
if (opts.skey) {
event.state_key = opts.skey;
} 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",
].indexOf(opts.type) !== -1) {
2016-03-31 00:48:46 +01:00
event.state_key = "";
}
return opts.event ? new MatrixEvent(event) : event;
2017-10-11 17:56:17 +01:00
}
2016-03-31 00:48:46 +01:00
/**
* Create an m.presence event.
* @param {Object} opts Values for the presence.
* @return {Object|MatrixEvent} The event
*/
2016-09-09 18:07:42 +05:30
export function mkPresence(opts) {
2016-03-31 00:48:46 +01:00
if (!opts.user) {
throw new Error("Missing user");
}
2017-10-11 17:56:17 +01:00
const event = {
2016-03-31 00:48:46 +01: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 17:56:17 +01:00
presence: opts.presence || "offline",
},
2016-03-31 00:48:46 +01:00
};
return opts.event ? new MatrixEvent(event) : event;
2017-10-11 17:56:17 +01:00
}
2016-03-31 00:48:46 +01: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.
2021-08-10 12:25:11 +05:30
* @param {number=} opts.ts Optional. Timestamp for the event
2016-03-31 00:48:46 +01: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.
2021-08-10 12:25:11 +05:30
* @param {string=} opts.skey The other user ID for the event if applicable
2016-03-31 00:48:46 +01:00
* e.g. for invites/bans.
* @param {string} opts.name The content.displayname for the event.
2021-08-10 12:25:11 +05:30
* @param {string=} opts.url The content.avatar_url for the event.
2016-03-31 00:48:46 +01:00
* @param {boolean} opts.event True to make a MatrixEvent.
* @return {Object|MatrixEvent} The event
*/
2016-09-09 18:07:42 +05:30
export function mkMembership(opts) {
2016-03-31 00:48:46 +01: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 17:56:17 +01:00
membership: opts.mship,
2016-03-31 00:48:46 +01:00
};
2017-01-18 11:53:17 +01:00
if (opts.prevMship) {
opts.prev_content = { membership: opts.prevMship };
}
2016-03-31 00:48:46 +01:00
if (opts.name) { opts.content.displayname = opts.name; }
if (opts.url) { opts.content.avatar_url = opts.url; }
2017-10-11 17:56:17 +01: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 17:56:17 +01:00
}
2016-03-31 00:48:46 +01: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 14:36:21 +05:30
* @param {number} opts.ts The timestamp for the event.
2016-03-31 00:48:46 +01:00
* @param {boolean} opts.event True to make a MatrixEvent.
2021-08-03 14:36:21 +05:30
* @param {string=} opts.msg Optional. The content.body for the event.
2016-03-31 00:48:46 +01:00
* @return {Object|MatrixEvent} The event
*/
2016-09-09 18:07:42 +05:30
export function mkMessage(opts) {
2016-03-31 00:48:46 +01: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 17:56:17 +01:00
body: opts.msg,
2016-03-31 00:48:46 +01:00
};
2016-09-09 18:07:42 +05:30
return mkEvent(opts);
}
2016-06-17 12:20:26 +01:00
export function mkStubRoom(roomId = null, name, client) {
2017-10-11 17:56:17 +01:00
const stubTimeline = { getEvents: () => [] };
2016-06-17 12:20:26 +01:00
return {
2016-09-09 18:07:42 +05:30
roomId,
2019-12-16 11:12:48 +00:00
getReceiptsForEvent: jest.fn().mockReturnValue([]),
getMember: jest.fn().mockReturnValue({
userId: '@member:domain.bla',
name: 'Member',
2020-01-06 13:28:29 +00:00
rawDisplayName: 'Member',
roomId: roomId,
getAvatarUrl: () => 'mxc://avatar.url/image.png',
2021-03-11 09:42:55 -07:00
getMxcAvatarUrl: () => 'mxc://avatar.url/image.png',
}),
2019-12-16 11:12:48 +00:00
getMembersWithMembership: jest.fn().mockReturnValue([]),
getJoinedMembers: jest.fn().mockReturnValue([]),
getJoinedMemberCount: jest.fn().mockReturnValue(1),
2021-05-19 12:34:27 +01:00
getMembers: jest.fn().mockReturnValue([]),
2016-10-10 17:51:26 +01:00
getPendingEvents: () => [],
getLiveTimeline: () => stubTimeline,
getUnfilteredTimelineSet: () => null,
2021-05-10 00:54:00 -04:00
findEventById: () => null,
2016-10-10 17:51:26 +01:00
getAccountData: () => null,
hasMembershipState: () => null,
getVersion: () => '1',
2018-08-17 15:15:53 +01:00
shouldUpgradeToVersion: () => null,
2021-04-23 12:19:08 +01:00
getMyMembership: jest.fn().mockReturnValue("join"),
2019-12-16 11:12:48 +00:00
maySendMessage: jest.fn().mockReturnValue(true),
2016-06-17 12:20:26 +01:00
currentState: {
2019-12-16 11:12:48 +00:00
getStateEvents: jest.fn(),
2021-05-19 15:01:05 +05:30
getMember: jest.fn(),
2019-12-16 11:12:48 +00:00
mayClientSendStateEvent: jest.fn().mockReturnValue(true),
maySendStateEvent: jest.fn().mockReturnValue(true),
maySendEvent: jest.fn().mockReturnValue(true),
2016-06-17 12:20:26 +01:00
members: [],
getJoinRule: jest.fn().mockReturnValue(JoinRule.Invite),
on: jest.fn(),
2016-06-17 12:20:26 +01:00
},
2021-04-23 12:19:08 +01:00
tags: {},
2019-12-16 11:12:48 +00:00
setBlacklistUnverifiedDevices: jest.fn(),
on: jest.fn(),
2021-05-16 08:39:22 -04:00
off: jest.fn(),
2019-12-16 11:12:48 +00:00
removeListener: jest.fn(),
2020-11-05 16:27:41 +00:00
getDMInviter: jest.fn(),
2021-05-08 19:51:51 -04:00
name,
2021-02-03 15:18:19 +00:00
getAvatarUrl: () => 'mxc://avatar.url/room.png',
2021-03-11 09:42:55 -07:00
getMxcAvatarUrl: () => 'mxc://avatar.url/room.png',
2021-04-22 14:45:13 +01:00
isSpaceRoom: jest.fn(() => false),
2021-04-23 12:19:08 +01:00
getUnreadNotificationCount: jest.fn(() => 0),
getEventReadUpTo: jest.fn(() => null),
2021-06-01 20:36:28 -04:00
getCanonicalAlias: jest.fn(),
getAltAliases: jest.fn().mockReturnValue([]),
2021-04-23 12:19:08 +01:00
timeline: [],
2021-07-06 10:44:09 +01:00
getJoinRule: jest.fn().mockReturnValue("invite"),
client,
2016-06-17 12:20:26 +01:00
};
2016-09-09 18:07:42 +05:30
}
2017-05-24 16:56:13 +01:00
2019-05-02 23:46:43 -06:00
export function mkServerConfig(hsUrl, isUrl) {
return makeType(ValidatedServerConfig, {
hsUrl,
hsName: "TEST_ENVIRONMENT",
hsNameIsDifferent: false, // yes, we lie
isUrl,
});
}
2017-05-24 16:56:13 +01: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 17:50:53 +00:00
export function wrapInMatrixClientContext(WrappedComponent) {
class Wrapper extends React.Component {
2019-12-17 17:26:12 +00:00
constructor(props) {
super(props);
2018-02-06 17:50:53 +00:00
this._matrixClient = peg.get();
}
render() {
2019-12-17 17:26:12 +00:00
return <MatrixClientContext.Provider value={this._matrixClient}>
<WrappedComponent ref={this.props.wrappedRef} {...this.props} />
</MatrixClientContext.Provider>;
2018-02-06 17:50:53 +00:00
}
}
return Wrapper;
}
2018-05-02 11:19:01 +01: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 11:12:48 +00:00
* @param {number} updates Number of updates to wait for. (Defaults to 1.)
2018-05-02 11:19:01 +01:00
* @returns {Promise} promise that resolves when componentDidUpdate is called on
* given component instance.
*/
export function waitForUpdate(inst, updates = 1) {
2018-05-02 11:19:01 +01:00
return new Promise((resolve, reject) => {
const cdu = inst.componentDidUpdate;
console.log(`Waiting for ${updates} update(s)`);
2018-05-02 11:19:01 +01:00
inst.componentDidUpdate = (prevProps, prevState, snapshot) => {
updates--;
console.log(`Got update, ${updates} remaining`);
2018-05-02 11:19:01 +01:00
if (updates == 0) {
inst.componentDidUpdate = cdu;
resolve();
}
2018-05-02 11:19:01 +01:00
if (cdu) cdu(prevProps, prevState, snapshot);
2018-05-02 11:19:01 +01:00
};
});
}