UNPKG

7.52 kBJavaScriptView Raw
1"use strict";
2import expect from 'expect';
3import Promise from 'bluebird';
4
5// load olm before the sdk if possible
6import './olm-loader';
7
8import logger from '../src/logger';
9import sdk from '..';
10const MatrixEvent = sdk.MatrixEvent;
11
12/**
13 * Return a promise that is resolved when the client next emits a
14 * SYNCING event.
15 * @param {Object} client The client
16 * @param {Number=} count Number of syncs to wait for (default 1)
17 * @return {Promise} Resolves once the client has emitted a SYNCING event
18 */
19module.exports.syncPromise = function(client, count) {
20 if (count === undefined) {
21 count = 1;
22 }
23 if (count <= 0) {
24 return Promise.resolve();
25 }
26
27 const p = new Promise((resolve, reject) => {
28 const cb = (state) => {
29 logger.log(`${Date.now()} syncPromise(${count}): ${state}`);
30 if (state == 'SYNCING') {
31 resolve();
32 } else {
33 client.once('sync', cb);
34 }
35 };
36 client.once('sync', cb);
37 });
38
39 return p.then(() => {
40 return module.exports.syncPromise(client, count-1);
41 });
42};
43
44/**
45 * Perform common actions before each test case, e.g. printing the test case
46 * name to stdout.
47 * @param {Mocha.Context} context The test context
48 */
49module.exports.beforeEach = function(context) {
50 const desc = context.currentTest.fullTitle();
51
52 logger.log(desc);
53 logger.log(new Array(1 + desc.length).join("="));
54};
55
56/**
57 * Create a spy for an object and automatically spy its methods.
58 * @param {*} constr The class constructor (used with 'new')
59 * @param {string} name The name of the class
60 * @return {Object} An instantiated object with spied methods/properties.
61 */
62module.exports.mock = function(constr, name) {
63 // Based on
64 // http://eclipsesource.com/blogs/2014/03/27/mocks-in-jasmine-tests/
65 const HelperConstr = new Function(); // jshint ignore:line
66 HelperConstr.prototype = constr.prototype;
67 const result = new HelperConstr();
68 result.toString = function() {
69 return "mock" + (name ? " of " + name : "");
70 };
71 for (const key in constr.prototype) { // eslint-disable-line guard-for-in
72 try {
73 if (constr.prototype[key] instanceof Function) {
74 result[key] = expect.createSpy();
75 }
76 } catch (ex) {
77 // Direct access to some non-function fields of DOM prototypes may
78 // cause exceptions.
79 // Overwriting will not work either in that case.
80 }
81 }
82 return result;
83};
84
85/**
86 * Create an Event.
87 * @param {Object} opts Values for the event.
88 * @param {string} opts.type The event.type
89 * @param {string} opts.room The event.room_id
90 * @param {string} opts.sender The event.sender
91 * @param {string} opts.skey Optional. The state key (auto inserts empty string)
92 * @param {Object} opts.content The event.content
93 * @param {boolean} opts.event True to make a MatrixEvent.
94 * @return {Object} a JSON object representing this event.
95 */
96module.exports.mkEvent = function(opts) {
97 if (!opts.type || !opts.content) {
98 throw new Error("Missing .type or .content =>" + JSON.stringify(opts));
99 }
100 const event = {
101 type: opts.type,
102 room_id: opts.room,
103 sender: opts.sender || opts.user, // opts.user for backwards-compat
104 content: opts.content,
105 event_id: "$" + Math.random() + "-" + Math.random(),
106 };
107 if (opts.skey !== undefined) {
108 event.state_key = opts.skey;
109 } else if (["m.room.name", "m.room.topic", "m.room.create", "m.room.join_rules",
110 "m.room.power_levels", "m.room.topic",
111 "com.example.state"].indexOf(opts.type) !== -1) {
112 event.state_key = "";
113 }
114 return opts.event ? new MatrixEvent(event) : event;
115};
116
117/**
118 * Create an m.presence event.
119 * @param {Object} opts Values for the presence.
120 * @return {Object|MatrixEvent} The event
121 */
122module.exports.mkPresence = function(opts) {
123 if (!opts.user) {
124 throw new Error("Missing user");
125 }
126 const event = {
127 event_id: "$" + Math.random() + "-" + Math.random(),
128 type: "m.presence",
129 sender: opts.sender || opts.user, // opts.user for backwards-compat
130 content: {
131 avatar_url: opts.url,
132 displayname: opts.name,
133 last_active_ago: opts.ago,
134 presence: opts.presence || "offline",
135 },
136 };
137 return opts.event ? new MatrixEvent(event) : event;
138};
139
140/**
141 * Create an m.room.member event.
142 * @param {Object} opts Values for the membership.
143 * @param {string} opts.room The room ID for the event.
144 * @param {string} opts.mship The content.membership for the event.
145 * @param {string} opts.sender The sender user ID for the event.
146 * @param {string} opts.skey The target user ID for the event if applicable
147 * e.g. for invites/bans.
148 * @param {string} opts.name The content.displayname for the event.
149 * @param {string} opts.url The content.avatar_url for the event.
150 * @param {boolean} opts.event True to make a MatrixEvent.
151 * @return {Object|MatrixEvent} The event
152 */
153module.exports.mkMembership = function(opts) {
154 opts.type = "m.room.member";
155 if (!opts.skey) {
156 opts.skey = opts.sender || opts.user;
157 }
158 if (!opts.mship) {
159 throw new Error("Missing .mship => " + JSON.stringify(opts));
160 }
161 opts.content = {
162 membership: opts.mship,
163 };
164 if (opts.name) {
165 opts.content.displayname = opts.name;
166 }
167 if (opts.url) {
168 opts.content.avatar_url = opts.url;
169 }
170 return module.exports.mkEvent(opts);
171};
172
173/**
174 * Create an m.room.message event.
175 * @param {Object} opts Values for the message
176 * @param {string} opts.room The room ID for the event.
177 * @param {string} opts.user The user ID for the event.
178 * @param {string} opts.msg Optional. The content.body for the event.
179 * @param {boolean} opts.event True to make a MatrixEvent.
180 * @return {Object|MatrixEvent} The event
181 */
182module.exports.mkMessage = function(opts) {
183 opts.type = "m.room.message";
184 if (!opts.msg) {
185 opts.msg = "Random->" + Math.random();
186 }
187 if (!opts.room || !opts.user) {
188 throw new Error("Missing .room or .user from %s", opts);
189 }
190 opts.content = {
191 msgtype: "m.text",
192 body: opts.msg,
193 };
194 return module.exports.mkEvent(opts);
195};
196
197
198/**
199 * A mock implementation of webstorage
200 *
201 * @constructor
202 */
203module.exports.MockStorageApi = function() {
204 this.data = {};
205};
206module.exports.MockStorageApi.prototype = {
207 get length() {
208 return Object.keys(this.data).length;
209 },
210 key: function(i) {
211 return Object.keys(this.data)[i];
212 },
213 setItem: function(k, v) {
214 this.data[k] = v;
215 },
216 getItem: function(k) {
217 return this.data[k] || null;
218 },
219 removeItem: function(k) {
220 delete this.data[k];
221 },
222};
223
224
225/**
226 * If an event is being decrypted, wait for it to finish being decrypted.
227 *
228 * @param {MatrixEvent} event
229 * @returns {Promise} promise which resolves (to `event`) when the event has been decrypted
230 */
231module.exports.awaitDecryption = function(event) {
232 if (!event.isBeingDecrypted()) {
233 return Promise.resolve(event);
234 }
235
236 logger.log(`${Date.now()} event ${event.getId()} is being decrypted; waiting`);
237
238 return new Promise((resolve, reject) => {
239 event.once('Event.decrypted', (ev) => {
240 logger.log(`${Date.now()} event ${event.getId()} now decrypted`);
241 resolve(ev);
242 });
243 });
244};