@webex/plugin-messages 2.59.3-next.1 → 2.59.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +6 -6
- package/README.md +50 -50
- package/babel.config.js +3 -3
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/messages.js +246 -246
- package/dist/messages.js.map +1 -1
- package/jest.config.js +3 -3
- package/package.json +20 -21
- package/process +1 -1
- package/src/index.js +14 -14
- package/src/messages.js +461 -461
- package/test/integration/spec/messages.js +752 -752
|
@@ -1,752 +1,752 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import '@webex/internal-plugin-device';
|
|
6
|
-
import '@webex/plugin-logger';
|
|
7
|
-
import '@webex/plugin-rooms';
|
|
8
|
-
import '@webex/plugin-people';
|
|
9
|
-
import '@webex/plugin-messages';
|
|
10
|
-
import WebexCore, { WebexHttpError } from '@webex/webex-core';
|
|
11
|
-
import { SDK_EVENT } from '@webex/common';
|
|
12
|
-
import { assert } from '@webex/test-helper-chai';
|
|
13
|
-
import sinon from 'sinon';
|
|
14
|
-
import testUsers from '@webex/test-helper-test-users';
|
|
15
|
-
import fh from '@webex/test-helper-file';
|
|
16
|
-
import { browserOnly, flaky, nodeOnly } from '@webex/test-helper-mocha';
|
|
17
|
-
|
|
18
|
-
const debug = require('debug')('messages');
|
|
19
|
-
|
|
20
|
-
const KNOWN_HOSTED_IMAGE_URL = 'https://download.ciscospark.com/test/photo.png';
|
|
21
|
-
|
|
22
|
-
describe('plugin-messages', function () {
|
|
23
|
-
this.timeout(60000);
|
|
24
|
-
|
|
25
|
-
let webex;
|
|
26
|
-
let webexEU;
|
|
27
|
-
let actor;
|
|
28
|
-
let actorEU;
|
|
29
|
-
|
|
30
|
-
before(() =>
|
|
31
|
-
Promise.all([
|
|
32
|
-
testUsers.create({ count: 1 }),
|
|
33
|
-
testUsers.create({ count: 1, config: { orgId: process.env.EU_PRIMARY_ORG_ID } }),
|
|
34
|
-
]).then(([user, usersEU]) => {
|
|
35
|
-
[actor] = user;
|
|
36
|
-
[actorEU] = usersEU;
|
|
37
|
-
|
|
38
|
-
webex = new WebexCore({ credentials: actor.token });
|
|
39
|
-
webexEU = new WebexCore({ credentials: actorEU.token });
|
|
40
|
-
|
|
41
|
-
webex.people.get('me').then((person) => {
|
|
42
|
-
actor = person;
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
webexEU.people.get('me').then((person) => {
|
|
46
|
-
actorEU = person;
|
|
47
|
-
});
|
|
48
|
-
})
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
describe('#messages', () => {
|
|
52
|
-
let room;
|
|
53
|
-
let roomEU;
|
|
54
|
-
|
|
55
|
-
before(() =>
|
|
56
|
-
Promise.all([
|
|
57
|
-
webex.rooms.create({ title: 'Webex Test Room' }),
|
|
58
|
-
webexEU.rooms.create({ title: 'Webex Test Room for EU' }),
|
|
59
|
-
]).then(([r, rEU]) => {
|
|
60
|
-
room = r;
|
|
61
|
-
roomEU = rEU;
|
|
62
|
-
const text = 'First Message';
|
|
63
|
-
|
|
64
|
-
webex.messages
|
|
65
|
-
.create({
|
|
66
|
-
roomId: room.id,
|
|
67
|
-
text,
|
|
68
|
-
})
|
|
69
|
-
.then((message) => {
|
|
70
|
-
validateMessage(message, text);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
webexEU.messages
|
|
74
|
-
.create({
|
|
75
|
-
roomId: roomEU.id,
|
|
76
|
-
text,
|
|
77
|
-
})
|
|
78
|
-
.then((message) => {
|
|
79
|
-
validateMessage(message, text);
|
|
80
|
-
});
|
|
81
|
-
})
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
// eslint-disable-next-line consistent-return
|
|
85
|
-
after(() => Promise.all([webex.rooms.remove(room), webexEU.rooms.remove(roomEU)]));
|
|
86
|
-
|
|
87
|
-
afterEach(() => webex.messages.stopListening());
|
|
88
|
-
|
|
89
|
-
describe('#create()', () => {
|
|
90
|
-
it('posts a message in a room and validates the messages:created event', () => {
|
|
91
|
-
let message;
|
|
92
|
-
|
|
93
|
-
// "Block" this test with a promise that will
|
|
94
|
-
// resolve after the messages:created arrives.
|
|
95
|
-
const created = new Promise((resolve) => {
|
|
96
|
-
webex.messages.on('created', (event) => {
|
|
97
|
-
debug('message created event called');
|
|
98
|
-
resolve(event);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
const text = 'A test message';
|
|
103
|
-
|
|
104
|
-
return webex.messages.listen().then(() =>
|
|
105
|
-
webex.messages
|
|
106
|
-
.create({
|
|
107
|
-
roomId: room.id,
|
|
108
|
-
text,
|
|
109
|
-
})
|
|
110
|
-
.then(async (m) => {
|
|
111
|
-
message = m;
|
|
112
|
-
validateMessage(message, text);
|
|
113
|
-
const event = await created;
|
|
114
|
-
|
|
115
|
-
validateMessageEvent(event, message, actor);
|
|
116
|
-
})
|
|
117
|
-
);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('posts a message by an EU user in a room and validates the messages:created event', () => {
|
|
121
|
-
let message;
|
|
122
|
-
|
|
123
|
-
// "Block" this test with a promise that will
|
|
124
|
-
// resolve after the messages:created arrives.
|
|
125
|
-
const created = new Promise((resolve) => {
|
|
126
|
-
webexEU.messages.on('created', (event) => {
|
|
127
|
-
debug('message created event called');
|
|
128
|
-
resolve(event);
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
const text = 'A test message';
|
|
133
|
-
|
|
134
|
-
return webexEU.messages.listen().then(() =>
|
|
135
|
-
webexEU.messages
|
|
136
|
-
.create({
|
|
137
|
-
roomId: roomEU.id,
|
|
138
|
-
text,
|
|
139
|
-
})
|
|
140
|
-
.then(async (m) => {
|
|
141
|
-
message = m;
|
|
142
|
-
validateMessage(message, text);
|
|
143
|
-
const event = await created;
|
|
144
|
-
|
|
145
|
-
validateMessageEvent(event, message, actorEU);
|
|
146
|
-
})
|
|
147
|
-
);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("posts a file to a room by specifying the file's url and validates the event", () => {
|
|
151
|
-
const created = new Promise((resolve) => {
|
|
152
|
-
webex.messages.on('created', (event) => {
|
|
153
|
-
debug('message created event called');
|
|
154
|
-
resolve(event);
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
return webex.messages.listen().then(() =>
|
|
159
|
-
webex.messages
|
|
160
|
-
.create({
|
|
161
|
-
roomId: room.id,
|
|
162
|
-
files: [KNOWN_HOSTED_IMAGE_URL],
|
|
163
|
-
})
|
|
164
|
-
.then(async (message) => {
|
|
165
|
-
validateMessage(message);
|
|
166
|
-
const event = await created;
|
|
167
|
-
|
|
168
|
-
validateMessageEvent(event, message, actor);
|
|
169
|
-
})
|
|
170
|
-
);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
let blob, buffer;
|
|
174
|
-
const text = 'A File';
|
|
175
|
-
|
|
176
|
-
browserOnly(before)(() =>
|
|
177
|
-
fh.fetch('sample-image-small-one.png').then((file) => {
|
|
178
|
-
blob = file;
|
|
179
|
-
|
|
180
|
-
return new Promise((resolve) => {
|
|
181
|
-
/* global FileReader */
|
|
182
|
-
const fileReader = new FileReader();
|
|
183
|
-
|
|
184
|
-
fileReader.onload = function () {
|
|
185
|
-
buffer = this.result;
|
|
186
|
-
resolve();
|
|
187
|
-
};
|
|
188
|
-
fileReader.readAsArrayBuffer(blob);
|
|
189
|
-
});
|
|
190
|
-
})
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
nodeOnly(before)(() =>
|
|
194
|
-
fh.fetchWithoutMagic('sample-image-small-one.png').then((file) => {
|
|
195
|
-
buffer = file;
|
|
196
|
-
})
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
browserOnly(it)(
|
|
200
|
-
'posts a file to a room by directly supplying its blob and validates the event',
|
|
201
|
-
() => {
|
|
202
|
-
const created = new Promise((resolve) => {
|
|
203
|
-
webex.messages.on('created', (event) => {
|
|
204
|
-
debug('message created event called');
|
|
205
|
-
resolve(event);
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
return webex.messages.listen().then(() =>
|
|
210
|
-
webex.messages
|
|
211
|
-
.create({
|
|
212
|
-
roomId: room.id,
|
|
213
|
-
files: [blob],
|
|
214
|
-
text,
|
|
215
|
-
})
|
|
216
|
-
.then(async (message) => {
|
|
217
|
-
validateMessage(message);
|
|
218
|
-
const event = await created;
|
|
219
|
-
|
|
220
|
-
validateMessageEvent(event, message, actor);
|
|
221
|
-
})
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
// Disabling it gating pipelines because it failes a lot and we get
|
|
227
|
-
// mostly adequate coverage via blob upload
|
|
228
|
-
flaky(it, process.env.SKIP_FLAKY_TESTS)(
|
|
229
|
-
'posts a file to a room by directly supplying its buffer and validates the event',
|
|
230
|
-
() =>
|
|
231
|
-
webex.messages
|
|
232
|
-
.create({
|
|
233
|
-
roomId: room.id,
|
|
234
|
-
files: [buffer],
|
|
235
|
-
})
|
|
236
|
-
.then((message) => {
|
|
237
|
-
validateMessage(message, '', 1);
|
|
238
|
-
})
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
it("posts a file with a message to a room by specifying the file's url and validates the event", () => {
|
|
242
|
-
const created = new Promise((resolve) => {
|
|
243
|
-
webex.messages.on('created', (event) => {
|
|
244
|
-
debug('message created event called');
|
|
245
|
-
resolve(event);
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
return webex.messages.listen().then(() =>
|
|
250
|
-
webex.messages
|
|
251
|
-
.create({
|
|
252
|
-
roomId: room.id,
|
|
253
|
-
files: [KNOWN_HOSTED_IMAGE_URL],
|
|
254
|
-
text,
|
|
255
|
-
})
|
|
256
|
-
.then(async (message) => {
|
|
257
|
-
validateMessage(message);
|
|
258
|
-
let event = await created;
|
|
259
|
-
|
|
260
|
-
// When using this method to attach a file to
|
|
261
|
-
// a message, sometimes, the first event does not
|
|
262
|
-
// include all the data included in the message.
|
|
263
|
-
// kms then triggers a second event that includes
|
|
264
|
-
// all of the data in the message object.
|
|
265
|
-
if (event.data.id !== message.id) {
|
|
266
|
-
const createdCombined = new Promise((resolve) => {
|
|
267
|
-
webex.messages.on('created', (e) => {
|
|
268
|
-
debug('message created event called');
|
|
269
|
-
resolve(e);
|
|
270
|
-
});
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
event = await createdCombined;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
validateMessageEvent(event, message, actor);
|
|
277
|
-
})
|
|
278
|
-
);
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it('posts a message to a card to a room validates the event', () => {
|
|
282
|
-
const created = new Promise((resolve) => {
|
|
283
|
-
webex.messages.on('created', (event) => {
|
|
284
|
-
debug('message created event called');
|
|
285
|
-
resolve(event);
|
|
286
|
-
});
|
|
287
|
-
});
|
|
288
|
-
const attachment = {
|
|
289
|
-
contentType: 'application/vnd.microsoft.card.adaptive',
|
|
290
|
-
content: {
|
|
291
|
-
type: 'AdaptiveCard',
|
|
292
|
-
version: '1.0',
|
|
293
|
-
body: [
|
|
294
|
-
{
|
|
295
|
-
type: 'TextBlock',
|
|
296
|
-
text: 'Here is an image',
|
|
297
|
-
},
|
|
298
|
-
{
|
|
299
|
-
type: 'Image',
|
|
300
|
-
url: KNOWN_HOSTED_IMAGE_URL,
|
|
301
|
-
size: 'small',
|
|
302
|
-
},
|
|
303
|
-
],
|
|
304
|
-
},
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
return webex.messages.listen().then(() =>
|
|
308
|
-
webex.messages
|
|
309
|
-
.create({
|
|
310
|
-
roomId: room.id,
|
|
311
|
-
text,
|
|
312
|
-
attachments: [attachment],
|
|
313
|
-
})
|
|
314
|
-
.then(async (message) => {
|
|
315
|
-
// // Assert that the message shape is valid and contains attachment data.
|
|
316
|
-
validateMessage(message, text, 0, attachment);
|
|
317
|
-
let event = await created;
|
|
318
|
-
|
|
319
|
-
// When using this method to attach a file to
|
|
320
|
-
// a message, sometimes, the first event does not
|
|
321
|
-
// include all the data included in the message.
|
|
322
|
-
// kms then triggers a second event that includes
|
|
323
|
-
// all of the data in the message object.
|
|
324
|
-
if (event.data.id !== message.id) {
|
|
325
|
-
const createdCombined = new Promise((resolve) => {
|
|
326
|
-
webex.messages.on('created', (e) => {
|
|
327
|
-
debug('message created event called');
|
|
328
|
-
resolve(e);
|
|
329
|
-
});
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
event = await createdCombined;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
validateMessageEvent(event, message, actor);
|
|
336
|
-
})
|
|
337
|
-
);
|
|
338
|
-
});
|
|
339
|
-
});
|
|
340
|
-
describe('#update()', () => {
|
|
341
|
-
it('update a message in a room and validates the messages:update event, params message and altMessage objects', () => {
|
|
342
|
-
let message;
|
|
343
|
-
const text = 'This message will be updated';
|
|
344
|
-
|
|
345
|
-
beforeEach(() =>
|
|
346
|
-
webex.messages
|
|
347
|
-
.create({
|
|
348
|
-
roomId: room.id,
|
|
349
|
-
text,
|
|
350
|
-
})
|
|
351
|
-
.then((m) => {
|
|
352
|
-
message = m;
|
|
353
|
-
validateMessage(m, text);
|
|
354
|
-
})
|
|
355
|
-
);
|
|
356
|
-
|
|
357
|
-
// "Block" this test with a promise that will
|
|
358
|
-
// resolve after the messages:created arrives.
|
|
359
|
-
const updated = new Promise((resolve) => {
|
|
360
|
-
webex.messages.on('updated', (event) => {
|
|
361
|
-
debug('message updated event called');
|
|
362
|
-
resolve(event);
|
|
363
|
-
});
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
text = 'This is updated message';
|
|
367
|
-
|
|
368
|
-
return webex.messages.listen().then(() =>
|
|
369
|
-
webex.messages
|
|
370
|
-
.update({
|
|
371
|
-
message: message,
|
|
372
|
-
altMessage: { text: text },
|
|
373
|
-
})
|
|
374
|
-
.then(async (m) => {
|
|
375
|
-
message = m;
|
|
376
|
-
validateMessage(message, text);
|
|
377
|
-
const event = await updated;
|
|
378
|
-
|
|
379
|
-
validateMessageEvent(event, message, actor);
|
|
380
|
-
})
|
|
381
|
-
);
|
|
382
|
-
});
|
|
383
|
-
it('update a message in a room and validates the messages:update event, parameter messageId, and altMessage with text and roomId', () => {
|
|
384
|
-
let message;
|
|
385
|
-
const text = 'This message will be updated';
|
|
386
|
-
|
|
387
|
-
beforeEach(() =>
|
|
388
|
-
webex.messages
|
|
389
|
-
.create({
|
|
390
|
-
roomId: room.id,
|
|
391
|
-
text,
|
|
392
|
-
})
|
|
393
|
-
.then((m) => {
|
|
394
|
-
message = m;
|
|
395
|
-
validateMessage(m, text);
|
|
396
|
-
})
|
|
397
|
-
);
|
|
398
|
-
|
|
399
|
-
// "Block" this test with a promise that will
|
|
400
|
-
// resolve after the messages:created arrives.
|
|
401
|
-
const updated = new Promise((resolve) => {
|
|
402
|
-
webex.messages.on('updated', (event) => {
|
|
403
|
-
debug('message updated event called');
|
|
404
|
-
resolve(event);
|
|
405
|
-
});
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
text = 'This is updated message';
|
|
409
|
-
|
|
410
|
-
return webex.messages.listen().then(() =>
|
|
411
|
-
webex.messages
|
|
412
|
-
.update({
|
|
413
|
-
message: message.id,
|
|
414
|
-
altMessage: {
|
|
415
|
-
roomId: room.id,
|
|
416
|
-
text: text
|
|
417
|
-
},
|
|
418
|
-
})
|
|
419
|
-
.then(async (m) => {
|
|
420
|
-
message = m;
|
|
421
|
-
validateMessage(message, text);
|
|
422
|
-
const event = await updated;
|
|
423
|
-
validateMessageEvent(event, message, actor);
|
|
424
|
-
})
|
|
425
|
-
);
|
|
426
|
-
});
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
describe('#remove()', () => {
|
|
430
|
-
let message;
|
|
431
|
-
const text = 'This message will be deleted';
|
|
432
|
-
|
|
433
|
-
beforeEach(() =>
|
|
434
|
-
webex.messages
|
|
435
|
-
.create({
|
|
436
|
-
roomId: room.id,
|
|
437
|
-
text,
|
|
438
|
-
})
|
|
439
|
-
.then((m) => {
|
|
440
|
-
message = m;
|
|
441
|
-
validateMessage(m, text);
|
|
442
|
-
})
|
|
443
|
-
);
|
|
444
|
-
|
|
445
|
-
it('deletes a single message and validates the message:deleted event', () => {
|
|
446
|
-
const deleted = new Promise((resolve) => {
|
|
447
|
-
webex.messages.on('deleted', (event) => {
|
|
448
|
-
debug('message deleted event called');
|
|
449
|
-
resolve(event);
|
|
450
|
-
});
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
return webex.messages.listen().then(() =>
|
|
454
|
-
webex.messages
|
|
455
|
-
.remove(message)
|
|
456
|
-
.then((body) => {
|
|
457
|
-
assert.notOk(body);
|
|
458
|
-
|
|
459
|
-
return assert.isRejected(webex.messages.get(message));
|
|
460
|
-
})
|
|
461
|
-
.then(async (reason) => {
|
|
462
|
-
assert.instanceOf(reason, WebexHttpError.NotFound);
|
|
463
|
-
const event = await deleted;
|
|
464
|
-
|
|
465
|
-
validateMessageEvent(event, message, actor);
|
|
466
|
-
})
|
|
467
|
-
);
|
|
468
|
-
});
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
describe('get()', () => {
|
|
472
|
-
let message;
|
|
473
|
-
const text = 'A test message';
|
|
474
|
-
|
|
475
|
-
before(() => {
|
|
476
|
-
// The above tests validate all the events
|
|
477
|
-
// Turn off the event listener for the remainder of the tests
|
|
478
|
-
webex.messages.off('created');
|
|
479
|
-
webex.messages.off('deleted');
|
|
480
|
-
|
|
481
|
-
return webex.messages
|
|
482
|
-
.create({
|
|
483
|
-
roomId: room.id,
|
|
484
|
-
text,
|
|
485
|
-
})
|
|
486
|
-
.then((m) => {
|
|
487
|
-
message = m;
|
|
488
|
-
validateMessage(message, text);
|
|
489
|
-
});
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
it('returns a single message', () =>
|
|
493
|
-
webex.messages.get(message).then((m) => {
|
|
494
|
-
assert.isMessage(m);
|
|
495
|
-
assert.deepEqual(m, message);
|
|
496
|
-
}));
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
describe('#list()', () => {
|
|
500
|
-
before(() =>
|
|
501
|
-
webex.rooms
|
|
502
|
-
.create({
|
|
503
|
-
title: 'Room List Test',
|
|
504
|
-
})
|
|
505
|
-
.then((r) => {
|
|
506
|
-
room = r;
|
|
507
|
-
})
|
|
508
|
-
);
|
|
509
|
-
|
|
510
|
-
before(() =>
|
|
511
|
-
[1, 2, 3].reduce(
|
|
512
|
-
(promise, value) =>
|
|
513
|
-
promise.then(() =>
|
|
514
|
-
webex.messages.create({
|
|
515
|
-
roomId: room.id,
|
|
516
|
-
text: `message: ${value}`,
|
|
517
|
-
})
|
|
518
|
-
),
|
|
519
|
-
Promise.resolve()
|
|
520
|
-
)
|
|
521
|
-
);
|
|
522
|
-
|
|
523
|
-
it('returns all messages for a room', () =>
|
|
524
|
-
webex.messages.list({ roomId: room.id }).then((messages) => {
|
|
525
|
-
assert.isDefined(messages);
|
|
526
|
-
assert.lengthOf(messages, 3);
|
|
527
|
-
for (const message of messages) {
|
|
528
|
-
assert.isMessage(message);
|
|
529
|
-
}
|
|
530
|
-
}));
|
|
531
|
-
|
|
532
|
-
it('returns a bounded set of messages for a room', () => {
|
|
533
|
-
const spy = sinon.spy();
|
|
534
|
-
|
|
535
|
-
return webex.messages
|
|
536
|
-
.list({ roomId: room.id, max: 2 })
|
|
537
|
-
.then((messages) => {
|
|
538
|
-
assert.lengthOf(messages, 2);
|
|
539
|
-
|
|
540
|
-
return (function f(page) {
|
|
541
|
-
for (const message of page) {
|
|
542
|
-
spy(message.id);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
if (page.hasNext()) {
|
|
546
|
-
return page.next().then(f);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
return Promise.resolve();
|
|
550
|
-
})(messages);
|
|
551
|
-
})
|
|
552
|
-
.then(() => {
|
|
553
|
-
assert.calledThrice(spy);
|
|
554
|
-
});
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
describe('when a message is threaded', () => {
|
|
558
|
-
let parentId;
|
|
559
|
-
|
|
560
|
-
before(() =>
|
|
561
|
-
webex.rooms
|
|
562
|
-
.create({
|
|
563
|
-
title: 'Room List Test',
|
|
564
|
-
})
|
|
565
|
-
.then((r) => {
|
|
566
|
-
room = r;
|
|
567
|
-
})
|
|
568
|
-
);
|
|
569
|
-
|
|
570
|
-
before(() => {
|
|
571
|
-
const createdParent = new Promise((resolve) => {
|
|
572
|
-
webex.messages.once('created', (event) => {
|
|
573
|
-
debug('Threaded Test: parent message created event called');
|
|
574
|
-
resolve(event);
|
|
575
|
-
});
|
|
576
|
-
});
|
|
577
|
-
|
|
578
|
-
return webex.messages.listen().then(() =>
|
|
579
|
-
webex.messages
|
|
580
|
-
.create({
|
|
581
|
-
roomId: room.id,
|
|
582
|
-
text: 'This is the parent message',
|
|
583
|
-
})
|
|
584
|
-
.then(async (message) => {
|
|
585
|
-
parentId = message.id;
|
|
586
|
-
|
|
587
|
-
validateMessage(message);
|
|
588
|
-
const event = await createdParent;
|
|
589
|
-
|
|
590
|
-
validateMessageEvent(event, message, actor);
|
|
591
|
-
const createdReply = new Promise((resolve) => {
|
|
592
|
-
webex.messages.once('created', (e) => {
|
|
593
|
-
debug('Threaded Test: reply message created event called');
|
|
594
|
-
resolve(e);
|
|
595
|
-
});
|
|
596
|
-
});
|
|
597
|
-
|
|
598
|
-
return webex.messages
|
|
599
|
-
.create({
|
|
600
|
-
roomId: room.id,
|
|
601
|
-
text: 'This is the reply',
|
|
602
|
-
parentId,
|
|
603
|
-
})
|
|
604
|
-
.then(async (message2) => {
|
|
605
|
-
validateMessage(message2);
|
|
606
|
-
const event2 = await createdReply;
|
|
607
|
-
|
|
608
|
-
return Promise.resolve(validateMessageEvent(event2, message2, actor));
|
|
609
|
-
});
|
|
610
|
-
})
|
|
611
|
-
);
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
it('returns all messages for a room', () =>
|
|
615
|
-
webex.messages.list({ roomId: room.id }).then((messages) => {
|
|
616
|
-
assert.isDefined(messages);
|
|
617
|
-
assert.lengthOf(messages.items, 2);
|
|
618
|
-
for (const message of messages.items) {
|
|
619
|
-
assert.isMessage(message);
|
|
620
|
-
if (message.parentId) {
|
|
621
|
-
assert.equal(message.parentId, parentId);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
}));
|
|
625
|
-
|
|
626
|
-
it('returns only the replies for particular message thread', () =>
|
|
627
|
-
webex.messages.list({ roomId: room.id, parentId }).then((messages) => {
|
|
628
|
-
assert.lengthOf(messages.items, 1);
|
|
629
|
-
const message = messages.items[0];
|
|
630
|
-
|
|
631
|
-
assert.isMessage(message);
|
|
632
|
-
assert.strictEqual(message.parentId, parentId);
|
|
633
|
-
}));
|
|
634
|
-
});
|
|
635
|
-
});
|
|
636
|
-
});
|
|
637
|
-
});
|
|
638
|
-
|
|
639
|
-
/**
|
|
640
|
-
* Validate a Message object.
|
|
641
|
-
* @param {Object} message
|
|
642
|
-
* @param {String} text -- optional message text to check
|
|
643
|
-
* @param {Boolean} numFiles
|
|
644
|
-
* @param {Object} attachment
|
|
645
|
-
* @returns {void}
|
|
646
|
-
*/
|
|
647
|
-
function validateMessage(message, text = '', numFiles = 0, attachment = null) {
|
|
648
|
-
assert.isDefined(message);
|
|
649
|
-
assert.isMessage(message);
|
|
650
|
-
if (text) {
|
|
651
|
-
assert.equal(message.text, text);
|
|
652
|
-
}
|
|
653
|
-
if (attachment) {
|
|
654
|
-
validateAdaptiveCard(message, attachment);
|
|
655
|
-
}
|
|
656
|
-
if (numFiles) {
|
|
657
|
-
assert.property(message, 'files');
|
|
658
|
-
assert.isDefined(message.files);
|
|
659
|
-
assert.isArray(message.files);
|
|
660
|
-
assert.lengthOf(message.files, numFiles);
|
|
661
|
-
}
|
|
662
|
-
debug('message validated');
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
/**
|
|
666
|
-
* Validate a Attachment Action.
|
|
667
|
-
* @param {Object} message -- message returned from the API
|
|
668
|
-
* @param {Object} attachment - adaptive card object that was sent to the API
|
|
669
|
-
* @returns {void}
|
|
670
|
-
*/
|
|
671
|
-
function validateAdaptiveCard(message, attachment) {
|
|
672
|
-
assert.isArray(message.attachments);
|
|
673
|
-
assert.isDefined(message.attachments.length);
|
|
674
|
-
const card = message.attachments[0];
|
|
675
|
-
|
|
676
|
-
// Cannot do a deepEqual compare because the image URLs are remapped
|
|
677
|
-
// Validate some aspects of the card data
|
|
678
|
-
|
|
679
|
-
assert.isDefined(card.contentType);
|
|
680
|
-
assert.isDefined(attachment.contentType);
|
|
681
|
-
assert.equal(card.contentType, attachment.contentType);
|
|
682
|
-
assert.isDefined(card.content);
|
|
683
|
-
assert.isDefined(attachment.content);
|
|
684
|
-
assert.equal(card.content.type, attachment.content.type);
|
|
685
|
-
assert.equal(card.content.version, attachment.content.version);
|
|
686
|
-
assert.isDefined(card.content.body);
|
|
687
|
-
assert.isArray(attachment.content.body);
|
|
688
|
-
assert.isDefined(card.content.body.length);
|
|
689
|
-
assert.equal(card.content.body.length, attachment.content.body.length);
|
|
690
|
-
for (let i = 0; i < card.content.body.length; i += 1) {
|
|
691
|
-
if (card.content.body[i].type.toLowerCase() === 'textblock') {
|
|
692
|
-
assert.deepEqual(card.content.body[i], attachment.content.body[i]);
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
/**
|
|
698
|
-
* Validate a Message event.
|
|
699
|
-
* @param {Object} event - message event
|
|
700
|
-
* @param {Object} message -- return from the API that generate this event
|
|
701
|
-
* @param {Object} actor - person object for user who performed action
|
|
702
|
-
* @returns {void}
|
|
703
|
-
*/
|
|
704
|
-
function validateMessageEvent(event, message, actor) {
|
|
705
|
-
assert.equal(event.resource, SDK_EVENT.EXTERNAL.RESOURCE.MESSAGES, 'not a message event');
|
|
706
|
-
assert.isDefined(event.event, 'message event type not set');
|
|
707
|
-
assert.isDefined(event.created, 'event listener created date not set');
|
|
708
|
-
assert.equal(event.createdBy, actor.id, 'event listener createdBy not set to our actor');
|
|
709
|
-
assert.equal(event.orgId, actor.orgId, "event listener orgId not === to our actor's");
|
|
710
|
-
assert.equal(event.ownedBy, 'creator', 'event listener not owned by creator');
|
|
711
|
-
assert.equal(event.status, 'active', 'event listener status not active');
|
|
712
|
-
assert.equal(event.actorId, actor.id, "event actorId not equal to our actor's id");
|
|
713
|
-
|
|
714
|
-
// Ensure event data matches data returned from function call
|
|
715
|
-
assert.equal(event.data.id, message.id, 'event/message.id not equal');
|
|
716
|
-
assert.equal(event.data.roomId, message.roomId, 'event/message.roomId not equal');
|
|
717
|
-
assert.equal(event.data.personId, message.personId, 'event/message.personId not equal');
|
|
718
|
-
assert.equal(event.data.personEmail, message.personEmail, 'event/message.personEmail not equal');
|
|
719
|
-
assert.equal(event.data.roomType, message.roomType, 'event/message.roomType not equal');
|
|
720
|
-
if (event.event === SDK_EVENT.EXTERNAL.EVENT_TYPE.DELETED) {
|
|
721
|
-
return;
|
|
722
|
-
}
|
|
723
|
-
if (message.text) {
|
|
724
|
-
assert.equal(event.data.text, message.text, 'event/message.text not equal');
|
|
725
|
-
}
|
|
726
|
-
if (message.files) {
|
|
727
|
-
assert.isArray(event.data.files, 'event.data.files is not array');
|
|
728
|
-
assert.isArray(message.files, 'message.files is not array');
|
|
729
|
-
assert.equal(
|
|
730
|
-
event.data.files.length,
|
|
731
|
-
message.files.length,
|
|
732
|
-
'event/message file arrays are different lengths'
|
|
733
|
-
);
|
|
734
|
-
for (let i = 0; i < message.files.length; i += 1) {
|
|
735
|
-
// The gateway returned by the API is apialpha.ciscospark.com
|
|
736
|
-
// The gateway returned in the event is api.ciscospark.com -- expected?
|
|
737
|
-
assert.equal(
|
|
738
|
-
event.data.files[i].substr(event.data.files[i].lastIndexOf('/') + 1),
|
|
739
|
-
message.files[i].substr(message.files[i].lastIndexOf('/') + 1),
|
|
740
|
-
'event/message file urls do not match'
|
|
741
|
-
);
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
if (message.attachments) {
|
|
745
|
-
assert.isArray(event.data.attachments);
|
|
746
|
-
assert.isDefined(event.data.attachments.length);
|
|
747
|
-
validateAdaptiveCard(message, event.data.attachments[0]);
|
|
748
|
-
}
|
|
749
|
-
if (message.parentId) {
|
|
750
|
-
assert.equal(message.parentId, event.data.parentId);
|
|
751
|
-
}
|
|
752
|
-
}
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import '@webex/internal-plugin-device';
|
|
6
|
+
import '@webex/plugin-logger';
|
|
7
|
+
import '@webex/plugin-rooms';
|
|
8
|
+
import '@webex/plugin-people';
|
|
9
|
+
import '@webex/plugin-messages';
|
|
10
|
+
import WebexCore, { WebexHttpError } from '@webex/webex-core';
|
|
11
|
+
import { SDK_EVENT } from '@webex/common';
|
|
12
|
+
import { assert } from '@webex/test-helper-chai';
|
|
13
|
+
import sinon from 'sinon';
|
|
14
|
+
import testUsers from '@webex/test-helper-test-users';
|
|
15
|
+
import fh from '@webex/test-helper-file';
|
|
16
|
+
import { browserOnly, flaky, nodeOnly } from '@webex/test-helper-mocha';
|
|
17
|
+
|
|
18
|
+
const debug = require('debug')('messages');
|
|
19
|
+
|
|
20
|
+
const KNOWN_HOSTED_IMAGE_URL = 'https://download.ciscospark.com/test/photo.png';
|
|
21
|
+
|
|
22
|
+
describe('plugin-messages', function () {
|
|
23
|
+
this.timeout(60000);
|
|
24
|
+
|
|
25
|
+
let webex;
|
|
26
|
+
let webexEU;
|
|
27
|
+
let actor;
|
|
28
|
+
let actorEU;
|
|
29
|
+
|
|
30
|
+
before(() =>
|
|
31
|
+
Promise.all([
|
|
32
|
+
testUsers.create({ count: 1 }),
|
|
33
|
+
testUsers.create({ count: 1, config: { orgId: process.env.EU_PRIMARY_ORG_ID } }),
|
|
34
|
+
]).then(([user, usersEU]) => {
|
|
35
|
+
[actor] = user;
|
|
36
|
+
[actorEU] = usersEU;
|
|
37
|
+
|
|
38
|
+
webex = new WebexCore({ credentials: actor.token });
|
|
39
|
+
webexEU = new WebexCore({ credentials: actorEU.token });
|
|
40
|
+
|
|
41
|
+
webex.people.get('me').then((person) => {
|
|
42
|
+
actor = person;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
webexEU.people.get('me').then((person) => {
|
|
46
|
+
actorEU = person;
|
|
47
|
+
});
|
|
48
|
+
})
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
describe('#messages', () => {
|
|
52
|
+
let room;
|
|
53
|
+
let roomEU;
|
|
54
|
+
|
|
55
|
+
before(() =>
|
|
56
|
+
Promise.all([
|
|
57
|
+
webex.rooms.create({ title: 'Webex Test Room' }),
|
|
58
|
+
webexEU.rooms.create({ title: 'Webex Test Room for EU' }),
|
|
59
|
+
]).then(([r, rEU]) => {
|
|
60
|
+
room = r;
|
|
61
|
+
roomEU = rEU;
|
|
62
|
+
const text = 'First Message';
|
|
63
|
+
|
|
64
|
+
webex.messages
|
|
65
|
+
.create({
|
|
66
|
+
roomId: room.id,
|
|
67
|
+
text,
|
|
68
|
+
})
|
|
69
|
+
.then((message) => {
|
|
70
|
+
validateMessage(message, text);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
webexEU.messages
|
|
74
|
+
.create({
|
|
75
|
+
roomId: roomEU.id,
|
|
76
|
+
text,
|
|
77
|
+
})
|
|
78
|
+
.then((message) => {
|
|
79
|
+
validateMessage(message, text);
|
|
80
|
+
});
|
|
81
|
+
})
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// eslint-disable-next-line consistent-return
|
|
85
|
+
after(() => Promise.all([webex.rooms.remove(room), webexEU.rooms.remove(roomEU)]));
|
|
86
|
+
|
|
87
|
+
afterEach(() => webex.messages.stopListening());
|
|
88
|
+
|
|
89
|
+
describe('#create()', () => {
|
|
90
|
+
it('posts a message in a room and validates the messages:created event', () => {
|
|
91
|
+
let message;
|
|
92
|
+
|
|
93
|
+
// "Block" this test with a promise that will
|
|
94
|
+
// resolve after the messages:created arrives.
|
|
95
|
+
const created = new Promise((resolve) => {
|
|
96
|
+
webex.messages.on('created', (event) => {
|
|
97
|
+
debug('message created event called');
|
|
98
|
+
resolve(event);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const text = 'A test message';
|
|
103
|
+
|
|
104
|
+
return webex.messages.listen().then(() =>
|
|
105
|
+
webex.messages
|
|
106
|
+
.create({
|
|
107
|
+
roomId: room.id,
|
|
108
|
+
text,
|
|
109
|
+
})
|
|
110
|
+
.then(async (m) => {
|
|
111
|
+
message = m;
|
|
112
|
+
validateMessage(message, text);
|
|
113
|
+
const event = await created;
|
|
114
|
+
|
|
115
|
+
validateMessageEvent(event, message, actor);
|
|
116
|
+
})
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('posts a message by an EU user in a room and validates the messages:created event', () => {
|
|
121
|
+
let message;
|
|
122
|
+
|
|
123
|
+
// "Block" this test with a promise that will
|
|
124
|
+
// resolve after the messages:created arrives.
|
|
125
|
+
const created = new Promise((resolve) => {
|
|
126
|
+
webexEU.messages.on('created', (event) => {
|
|
127
|
+
debug('message created event called');
|
|
128
|
+
resolve(event);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const text = 'A test message';
|
|
133
|
+
|
|
134
|
+
return webexEU.messages.listen().then(() =>
|
|
135
|
+
webexEU.messages
|
|
136
|
+
.create({
|
|
137
|
+
roomId: roomEU.id,
|
|
138
|
+
text,
|
|
139
|
+
})
|
|
140
|
+
.then(async (m) => {
|
|
141
|
+
message = m;
|
|
142
|
+
validateMessage(message, text);
|
|
143
|
+
const event = await created;
|
|
144
|
+
|
|
145
|
+
validateMessageEvent(event, message, actorEU);
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("posts a file to a room by specifying the file's url and validates the event", () => {
|
|
151
|
+
const created = new Promise((resolve) => {
|
|
152
|
+
webex.messages.on('created', (event) => {
|
|
153
|
+
debug('message created event called');
|
|
154
|
+
resolve(event);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
return webex.messages.listen().then(() =>
|
|
159
|
+
webex.messages
|
|
160
|
+
.create({
|
|
161
|
+
roomId: room.id,
|
|
162
|
+
files: [KNOWN_HOSTED_IMAGE_URL],
|
|
163
|
+
})
|
|
164
|
+
.then(async (message) => {
|
|
165
|
+
validateMessage(message);
|
|
166
|
+
const event = await created;
|
|
167
|
+
|
|
168
|
+
validateMessageEvent(event, message, actor);
|
|
169
|
+
})
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
let blob, buffer;
|
|
174
|
+
const text = 'A File';
|
|
175
|
+
|
|
176
|
+
browserOnly(before)(() =>
|
|
177
|
+
fh.fetch('sample-image-small-one.png').then((file) => {
|
|
178
|
+
blob = file;
|
|
179
|
+
|
|
180
|
+
return new Promise((resolve) => {
|
|
181
|
+
/* global FileReader */
|
|
182
|
+
const fileReader = new FileReader();
|
|
183
|
+
|
|
184
|
+
fileReader.onload = function () {
|
|
185
|
+
buffer = this.result;
|
|
186
|
+
resolve();
|
|
187
|
+
};
|
|
188
|
+
fileReader.readAsArrayBuffer(blob);
|
|
189
|
+
});
|
|
190
|
+
})
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
nodeOnly(before)(() =>
|
|
194
|
+
fh.fetchWithoutMagic('sample-image-small-one.png').then((file) => {
|
|
195
|
+
buffer = file;
|
|
196
|
+
})
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
browserOnly(it)(
|
|
200
|
+
'posts a file to a room by directly supplying its blob and validates the event',
|
|
201
|
+
() => {
|
|
202
|
+
const created = new Promise((resolve) => {
|
|
203
|
+
webex.messages.on('created', (event) => {
|
|
204
|
+
debug('message created event called');
|
|
205
|
+
resolve(event);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
return webex.messages.listen().then(() =>
|
|
210
|
+
webex.messages
|
|
211
|
+
.create({
|
|
212
|
+
roomId: room.id,
|
|
213
|
+
files: [blob],
|
|
214
|
+
text,
|
|
215
|
+
})
|
|
216
|
+
.then(async (message) => {
|
|
217
|
+
validateMessage(message);
|
|
218
|
+
const event = await created;
|
|
219
|
+
|
|
220
|
+
validateMessageEvent(event, message, actor);
|
|
221
|
+
})
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
// Disabling it gating pipelines because it failes a lot and we get
|
|
227
|
+
// mostly adequate coverage via blob upload
|
|
228
|
+
flaky(it, process.env.SKIP_FLAKY_TESTS)(
|
|
229
|
+
'posts a file to a room by directly supplying its buffer and validates the event',
|
|
230
|
+
() =>
|
|
231
|
+
webex.messages
|
|
232
|
+
.create({
|
|
233
|
+
roomId: room.id,
|
|
234
|
+
files: [buffer],
|
|
235
|
+
})
|
|
236
|
+
.then((message) => {
|
|
237
|
+
validateMessage(message, '', 1);
|
|
238
|
+
})
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
it("posts a file with a message to a room by specifying the file's url and validates the event", () => {
|
|
242
|
+
const created = new Promise((resolve) => {
|
|
243
|
+
webex.messages.on('created', (event) => {
|
|
244
|
+
debug('message created event called');
|
|
245
|
+
resolve(event);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return webex.messages.listen().then(() =>
|
|
250
|
+
webex.messages
|
|
251
|
+
.create({
|
|
252
|
+
roomId: room.id,
|
|
253
|
+
files: [KNOWN_HOSTED_IMAGE_URL],
|
|
254
|
+
text,
|
|
255
|
+
})
|
|
256
|
+
.then(async (message) => {
|
|
257
|
+
validateMessage(message);
|
|
258
|
+
let event = await created;
|
|
259
|
+
|
|
260
|
+
// When using this method to attach a file to
|
|
261
|
+
// a message, sometimes, the first event does not
|
|
262
|
+
// include all the data included in the message.
|
|
263
|
+
// kms then triggers a second event that includes
|
|
264
|
+
// all of the data in the message object.
|
|
265
|
+
if (event.data.id !== message.id) {
|
|
266
|
+
const createdCombined = new Promise((resolve) => {
|
|
267
|
+
webex.messages.on('created', (e) => {
|
|
268
|
+
debug('message created event called');
|
|
269
|
+
resolve(e);
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
event = await createdCombined;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
validateMessageEvent(event, message, actor);
|
|
277
|
+
})
|
|
278
|
+
);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('posts a message to a card to a room validates the event', () => {
|
|
282
|
+
const created = new Promise((resolve) => {
|
|
283
|
+
webex.messages.on('created', (event) => {
|
|
284
|
+
debug('message created event called');
|
|
285
|
+
resolve(event);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
const attachment = {
|
|
289
|
+
contentType: 'application/vnd.microsoft.card.adaptive',
|
|
290
|
+
content: {
|
|
291
|
+
type: 'AdaptiveCard',
|
|
292
|
+
version: '1.0',
|
|
293
|
+
body: [
|
|
294
|
+
{
|
|
295
|
+
type: 'TextBlock',
|
|
296
|
+
text: 'Here is an image',
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
type: 'Image',
|
|
300
|
+
url: KNOWN_HOSTED_IMAGE_URL,
|
|
301
|
+
size: 'small',
|
|
302
|
+
},
|
|
303
|
+
],
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
return webex.messages.listen().then(() =>
|
|
308
|
+
webex.messages
|
|
309
|
+
.create({
|
|
310
|
+
roomId: room.id,
|
|
311
|
+
text,
|
|
312
|
+
attachments: [attachment],
|
|
313
|
+
})
|
|
314
|
+
.then(async (message) => {
|
|
315
|
+
// // Assert that the message shape is valid and contains attachment data.
|
|
316
|
+
validateMessage(message, text, 0, attachment);
|
|
317
|
+
let event = await created;
|
|
318
|
+
|
|
319
|
+
// When using this method to attach a file to
|
|
320
|
+
// a message, sometimes, the first event does not
|
|
321
|
+
// include all the data included in the message.
|
|
322
|
+
// kms then triggers a second event that includes
|
|
323
|
+
// all of the data in the message object.
|
|
324
|
+
if (event.data.id !== message.id) {
|
|
325
|
+
const createdCombined = new Promise((resolve) => {
|
|
326
|
+
webex.messages.on('created', (e) => {
|
|
327
|
+
debug('message created event called');
|
|
328
|
+
resolve(e);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
event = await createdCombined;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
validateMessageEvent(event, message, actor);
|
|
336
|
+
})
|
|
337
|
+
);
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
describe('#update()', () => {
|
|
341
|
+
it('update a message in a room and validates the messages:update event, params message and altMessage objects', () => {
|
|
342
|
+
let message;
|
|
343
|
+
const text = 'This message will be updated';
|
|
344
|
+
|
|
345
|
+
beforeEach(() =>
|
|
346
|
+
webex.messages
|
|
347
|
+
.create({
|
|
348
|
+
roomId: room.id,
|
|
349
|
+
text,
|
|
350
|
+
})
|
|
351
|
+
.then((m) => {
|
|
352
|
+
message = m;
|
|
353
|
+
validateMessage(m, text);
|
|
354
|
+
})
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
// "Block" this test with a promise that will
|
|
358
|
+
// resolve after the messages:created arrives.
|
|
359
|
+
const updated = new Promise((resolve) => {
|
|
360
|
+
webex.messages.on('updated', (event) => {
|
|
361
|
+
debug('message updated event called');
|
|
362
|
+
resolve(event);
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
text = 'This is updated message';
|
|
367
|
+
|
|
368
|
+
return webex.messages.listen().then(() =>
|
|
369
|
+
webex.messages
|
|
370
|
+
.update({
|
|
371
|
+
message: message,
|
|
372
|
+
altMessage: { text: text },
|
|
373
|
+
})
|
|
374
|
+
.then(async (m) => {
|
|
375
|
+
message = m;
|
|
376
|
+
validateMessage(message, text);
|
|
377
|
+
const event = await updated;
|
|
378
|
+
|
|
379
|
+
validateMessageEvent(event, message, actor);
|
|
380
|
+
})
|
|
381
|
+
);
|
|
382
|
+
});
|
|
383
|
+
it('update a message in a room and validates the messages:update event, parameter messageId, and altMessage with text and roomId', () => {
|
|
384
|
+
let message;
|
|
385
|
+
const text = 'This message will be updated';
|
|
386
|
+
|
|
387
|
+
beforeEach(() =>
|
|
388
|
+
webex.messages
|
|
389
|
+
.create({
|
|
390
|
+
roomId: room.id,
|
|
391
|
+
text,
|
|
392
|
+
})
|
|
393
|
+
.then((m) => {
|
|
394
|
+
message = m;
|
|
395
|
+
validateMessage(m, text);
|
|
396
|
+
})
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
// "Block" this test with a promise that will
|
|
400
|
+
// resolve after the messages:created arrives.
|
|
401
|
+
const updated = new Promise((resolve) => {
|
|
402
|
+
webex.messages.on('updated', (event) => {
|
|
403
|
+
debug('message updated event called');
|
|
404
|
+
resolve(event);
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
text = 'This is updated message';
|
|
409
|
+
|
|
410
|
+
return webex.messages.listen().then(() =>
|
|
411
|
+
webex.messages
|
|
412
|
+
.update({
|
|
413
|
+
message: message.id,
|
|
414
|
+
altMessage: {
|
|
415
|
+
roomId: room.id,
|
|
416
|
+
text: text
|
|
417
|
+
},
|
|
418
|
+
})
|
|
419
|
+
.then(async (m) => {
|
|
420
|
+
message = m;
|
|
421
|
+
validateMessage(message, text);
|
|
422
|
+
const event = await updated;
|
|
423
|
+
validateMessageEvent(event, message, actor);
|
|
424
|
+
})
|
|
425
|
+
);
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
describe('#remove()', () => {
|
|
430
|
+
let message;
|
|
431
|
+
const text = 'This message will be deleted';
|
|
432
|
+
|
|
433
|
+
beforeEach(() =>
|
|
434
|
+
webex.messages
|
|
435
|
+
.create({
|
|
436
|
+
roomId: room.id,
|
|
437
|
+
text,
|
|
438
|
+
})
|
|
439
|
+
.then((m) => {
|
|
440
|
+
message = m;
|
|
441
|
+
validateMessage(m, text);
|
|
442
|
+
})
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
it('deletes a single message and validates the message:deleted event', () => {
|
|
446
|
+
const deleted = new Promise((resolve) => {
|
|
447
|
+
webex.messages.on('deleted', (event) => {
|
|
448
|
+
debug('message deleted event called');
|
|
449
|
+
resolve(event);
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
return webex.messages.listen().then(() =>
|
|
454
|
+
webex.messages
|
|
455
|
+
.remove(message)
|
|
456
|
+
.then((body) => {
|
|
457
|
+
assert.notOk(body);
|
|
458
|
+
|
|
459
|
+
return assert.isRejected(webex.messages.get(message));
|
|
460
|
+
})
|
|
461
|
+
.then(async (reason) => {
|
|
462
|
+
assert.instanceOf(reason, WebexHttpError.NotFound);
|
|
463
|
+
const event = await deleted;
|
|
464
|
+
|
|
465
|
+
validateMessageEvent(event, message, actor);
|
|
466
|
+
})
|
|
467
|
+
);
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
describe('get()', () => {
|
|
472
|
+
let message;
|
|
473
|
+
const text = 'A test message';
|
|
474
|
+
|
|
475
|
+
before(() => {
|
|
476
|
+
// The above tests validate all the events
|
|
477
|
+
// Turn off the event listener for the remainder of the tests
|
|
478
|
+
webex.messages.off('created');
|
|
479
|
+
webex.messages.off('deleted');
|
|
480
|
+
|
|
481
|
+
return webex.messages
|
|
482
|
+
.create({
|
|
483
|
+
roomId: room.id,
|
|
484
|
+
text,
|
|
485
|
+
})
|
|
486
|
+
.then((m) => {
|
|
487
|
+
message = m;
|
|
488
|
+
validateMessage(message, text);
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it('returns a single message', () =>
|
|
493
|
+
webex.messages.get(message).then((m) => {
|
|
494
|
+
assert.isMessage(m);
|
|
495
|
+
assert.deepEqual(m, message);
|
|
496
|
+
}));
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
describe('#list()', () => {
|
|
500
|
+
before(() =>
|
|
501
|
+
webex.rooms
|
|
502
|
+
.create({
|
|
503
|
+
title: 'Room List Test',
|
|
504
|
+
})
|
|
505
|
+
.then((r) => {
|
|
506
|
+
room = r;
|
|
507
|
+
})
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
before(() =>
|
|
511
|
+
[1, 2, 3].reduce(
|
|
512
|
+
(promise, value) =>
|
|
513
|
+
promise.then(() =>
|
|
514
|
+
webex.messages.create({
|
|
515
|
+
roomId: room.id,
|
|
516
|
+
text: `message: ${value}`,
|
|
517
|
+
})
|
|
518
|
+
),
|
|
519
|
+
Promise.resolve()
|
|
520
|
+
)
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
it('returns all messages for a room', () =>
|
|
524
|
+
webex.messages.list({ roomId: room.id }).then((messages) => {
|
|
525
|
+
assert.isDefined(messages);
|
|
526
|
+
assert.lengthOf(messages, 3);
|
|
527
|
+
for (const message of messages) {
|
|
528
|
+
assert.isMessage(message);
|
|
529
|
+
}
|
|
530
|
+
}));
|
|
531
|
+
|
|
532
|
+
it('returns a bounded set of messages for a room', () => {
|
|
533
|
+
const spy = sinon.spy();
|
|
534
|
+
|
|
535
|
+
return webex.messages
|
|
536
|
+
.list({ roomId: room.id, max: 2 })
|
|
537
|
+
.then((messages) => {
|
|
538
|
+
assert.lengthOf(messages, 2);
|
|
539
|
+
|
|
540
|
+
return (function f(page) {
|
|
541
|
+
for (const message of page) {
|
|
542
|
+
spy(message.id);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (page.hasNext()) {
|
|
546
|
+
return page.next().then(f);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return Promise.resolve();
|
|
550
|
+
})(messages);
|
|
551
|
+
})
|
|
552
|
+
.then(() => {
|
|
553
|
+
assert.calledThrice(spy);
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
describe('when a message is threaded', () => {
|
|
558
|
+
let parentId;
|
|
559
|
+
|
|
560
|
+
before(() =>
|
|
561
|
+
webex.rooms
|
|
562
|
+
.create({
|
|
563
|
+
title: 'Room List Test',
|
|
564
|
+
})
|
|
565
|
+
.then((r) => {
|
|
566
|
+
room = r;
|
|
567
|
+
})
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
before(() => {
|
|
571
|
+
const createdParent = new Promise((resolve) => {
|
|
572
|
+
webex.messages.once('created', (event) => {
|
|
573
|
+
debug('Threaded Test: parent message created event called');
|
|
574
|
+
resolve(event);
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
return webex.messages.listen().then(() =>
|
|
579
|
+
webex.messages
|
|
580
|
+
.create({
|
|
581
|
+
roomId: room.id,
|
|
582
|
+
text: 'This is the parent message',
|
|
583
|
+
})
|
|
584
|
+
.then(async (message) => {
|
|
585
|
+
parentId = message.id;
|
|
586
|
+
|
|
587
|
+
validateMessage(message);
|
|
588
|
+
const event = await createdParent;
|
|
589
|
+
|
|
590
|
+
validateMessageEvent(event, message, actor);
|
|
591
|
+
const createdReply = new Promise((resolve) => {
|
|
592
|
+
webex.messages.once('created', (e) => {
|
|
593
|
+
debug('Threaded Test: reply message created event called');
|
|
594
|
+
resolve(e);
|
|
595
|
+
});
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
return webex.messages
|
|
599
|
+
.create({
|
|
600
|
+
roomId: room.id,
|
|
601
|
+
text: 'This is the reply',
|
|
602
|
+
parentId,
|
|
603
|
+
})
|
|
604
|
+
.then(async (message2) => {
|
|
605
|
+
validateMessage(message2);
|
|
606
|
+
const event2 = await createdReply;
|
|
607
|
+
|
|
608
|
+
return Promise.resolve(validateMessageEvent(event2, message2, actor));
|
|
609
|
+
});
|
|
610
|
+
})
|
|
611
|
+
);
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
it('returns all messages for a room', () =>
|
|
615
|
+
webex.messages.list({ roomId: room.id }).then((messages) => {
|
|
616
|
+
assert.isDefined(messages);
|
|
617
|
+
assert.lengthOf(messages.items, 2);
|
|
618
|
+
for (const message of messages.items) {
|
|
619
|
+
assert.isMessage(message);
|
|
620
|
+
if (message.parentId) {
|
|
621
|
+
assert.equal(message.parentId, parentId);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}));
|
|
625
|
+
|
|
626
|
+
it('returns only the replies for particular message thread', () =>
|
|
627
|
+
webex.messages.list({ roomId: room.id, parentId }).then((messages) => {
|
|
628
|
+
assert.lengthOf(messages.items, 1);
|
|
629
|
+
const message = messages.items[0];
|
|
630
|
+
|
|
631
|
+
assert.isMessage(message);
|
|
632
|
+
assert.strictEqual(message.parentId, parentId);
|
|
633
|
+
}));
|
|
634
|
+
});
|
|
635
|
+
});
|
|
636
|
+
});
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Validate a Message object.
|
|
641
|
+
* @param {Object} message
|
|
642
|
+
* @param {String} text -- optional message text to check
|
|
643
|
+
* @param {Boolean} numFiles
|
|
644
|
+
* @param {Object} attachment
|
|
645
|
+
* @returns {void}
|
|
646
|
+
*/
|
|
647
|
+
function validateMessage(message, text = '', numFiles = 0, attachment = null) {
|
|
648
|
+
assert.isDefined(message);
|
|
649
|
+
assert.isMessage(message);
|
|
650
|
+
if (text) {
|
|
651
|
+
assert.equal(message.text, text);
|
|
652
|
+
}
|
|
653
|
+
if (attachment) {
|
|
654
|
+
validateAdaptiveCard(message, attachment);
|
|
655
|
+
}
|
|
656
|
+
if (numFiles) {
|
|
657
|
+
assert.property(message, 'files');
|
|
658
|
+
assert.isDefined(message.files);
|
|
659
|
+
assert.isArray(message.files);
|
|
660
|
+
assert.lengthOf(message.files, numFiles);
|
|
661
|
+
}
|
|
662
|
+
debug('message validated');
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Validate a Attachment Action.
|
|
667
|
+
* @param {Object} message -- message returned from the API
|
|
668
|
+
* @param {Object} attachment - adaptive card object that was sent to the API
|
|
669
|
+
* @returns {void}
|
|
670
|
+
*/
|
|
671
|
+
function validateAdaptiveCard(message, attachment) {
|
|
672
|
+
assert.isArray(message.attachments);
|
|
673
|
+
assert.isDefined(message.attachments.length);
|
|
674
|
+
const card = message.attachments[0];
|
|
675
|
+
|
|
676
|
+
// Cannot do a deepEqual compare because the image URLs are remapped
|
|
677
|
+
// Validate some aspects of the card data
|
|
678
|
+
|
|
679
|
+
assert.isDefined(card.contentType);
|
|
680
|
+
assert.isDefined(attachment.contentType);
|
|
681
|
+
assert.equal(card.contentType, attachment.contentType);
|
|
682
|
+
assert.isDefined(card.content);
|
|
683
|
+
assert.isDefined(attachment.content);
|
|
684
|
+
assert.equal(card.content.type, attachment.content.type);
|
|
685
|
+
assert.equal(card.content.version, attachment.content.version);
|
|
686
|
+
assert.isDefined(card.content.body);
|
|
687
|
+
assert.isArray(attachment.content.body);
|
|
688
|
+
assert.isDefined(card.content.body.length);
|
|
689
|
+
assert.equal(card.content.body.length, attachment.content.body.length);
|
|
690
|
+
for (let i = 0; i < card.content.body.length; i += 1) {
|
|
691
|
+
if (card.content.body[i].type.toLowerCase() === 'textblock') {
|
|
692
|
+
assert.deepEqual(card.content.body[i], attachment.content.body[i]);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Validate a Message event.
|
|
699
|
+
* @param {Object} event - message event
|
|
700
|
+
* @param {Object} message -- return from the API that generate this event
|
|
701
|
+
* @param {Object} actor - person object for user who performed action
|
|
702
|
+
* @returns {void}
|
|
703
|
+
*/
|
|
704
|
+
function validateMessageEvent(event, message, actor) {
|
|
705
|
+
assert.equal(event.resource, SDK_EVENT.EXTERNAL.RESOURCE.MESSAGES, 'not a message event');
|
|
706
|
+
assert.isDefined(event.event, 'message event type not set');
|
|
707
|
+
assert.isDefined(event.created, 'event listener created date not set');
|
|
708
|
+
assert.equal(event.createdBy, actor.id, 'event listener createdBy not set to our actor');
|
|
709
|
+
assert.equal(event.orgId, actor.orgId, "event listener orgId not === to our actor's");
|
|
710
|
+
assert.equal(event.ownedBy, 'creator', 'event listener not owned by creator');
|
|
711
|
+
assert.equal(event.status, 'active', 'event listener status not active');
|
|
712
|
+
assert.equal(event.actorId, actor.id, "event actorId not equal to our actor's id");
|
|
713
|
+
|
|
714
|
+
// Ensure event data matches data returned from function call
|
|
715
|
+
assert.equal(event.data.id, message.id, 'event/message.id not equal');
|
|
716
|
+
assert.equal(event.data.roomId, message.roomId, 'event/message.roomId not equal');
|
|
717
|
+
assert.equal(event.data.personId, message.personId, 'event/message.personId not equal');
|
|
718
|
+
assert.equal(event.data.personEmail, message.personEmail, 'event/message.personEmail not equal');
|
|
719
|
+
assert.equal(event.data.roomType, message.roomType, 'event/message.roomType not equal');
|
|
720
|
+
if (event.event === SDK_EVENT.EXTERNAL.EVENT_TYPE.DELETED) {
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
if (message.text) {
|
|
724
|
+
assert.equal(event.data.text, message.text, 'event/message.text not equal');
|
|
725
|
+
}
|
|
726
|
+
if (message.files) {
|
|
727
|
+
assert.isArray(event.data.files, 'event.data.files is not array');
|
|
728
|
+
assert.isArray(message.files, 'message.files is not array');
|
|
729
|
+
assert.equal(
|
|
730
|
+
event.data.files.length,
|
|
731
|
+
message.files.length,
|
|
732
|
+
'event/message file arrays are different lengths'
|
|
733
|
+
);
|
|
734
|
+
for (let i = 0; i < message.files.length; i += 1) {
|
|
735
|
+
// The gateway returned by the API is apialpha.ciscospark.com
|
|
736
|
+
// The gateway returned in the event is api.ciscospark.com -- expected?
|
|
737
|
+
assert.equal(
|
|
738
|
+
event.data.files[i].substr(event.data.files[i].lastIndexOf('/') + 1),
|
|
739
|
+
message.files[i].substr(message.files[i].lastIndexOf('/') + 1),
|
|
740
|
+
'event/message file urls do not match'
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (message.attachments) {
|
|
745
|
+
assert.isArray(event.data.attachments);
|
|
746
|
+
assert.isDefined(event.data.attachments.length);
|
|
747
|
+
validateAdaptiveCard(message, event.data.attachments[0]);
|
|
748
|
+
}
|
|
749
|
+
if (message.parentId) {
|
|
750
|
+
assert.equal(message.parentId, event.data.parentId);
|
|
751
|
+
}
|
|
752
|
+
}
|