UNPKG

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