@webex/internal-plugin-conversation 2.59.1 → 2.59.3-next.1
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 +47 -47
- package/babel.config.js +3 -3
- package/dist/activities.js +4 -4
- package/dist/activities.js.map +1 -1
- package/dist/activity-thread-ordering.js +34 -34
- package/dist/activity-thread-ordering.js.map +1 -1
- package/dist/config.js +12 -12
- package/dist/config.js.map +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/conversation.js +474 -474
- package/dist/conversation.js.map +1 -1
- package/dist/convo-error.js +4 -4
- package/dist/convo-error.js.map +1 -1
- package/dist/decryption-transforms.js +155 -155
- package/dist/decryption-transforms.js.map +1 -1
- package/dist/encryption-transforms.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/share-activity.js +57 -57
- package/dist/share-activity.js.map +1 -1
- package/dist/to-array.js +7 -7
- package/dist/to-array.js.map +1 -1
- package/jest.config.js +3 -3
- package/package.json +21 -20
- package/process +1 -1
- package/src/activities.js +157 -157
- package/src/activity-thread-ordering.js +283 -283
- package/src/activity-threading.md +282 -282
- package/src/config.js +37 -37
- package/src/constants.js +3 -3
- package/src/conversation.js +2535 -2535
- package/src/convo-error.js +15 -15
- package/src/decryption-transforms.js +541 -541
- package/src/encryption-transforms.js +345 -345
- package/src/index.js +327 -327
- package/src/share-activity.js +436 -436
- package/src/to-array.js +29 -29
- package/test/integration/spec/create.js +290 -290
- package/test/integration/spec/encryption.js +333 -333
- package/test/integration/spec/get.js +1255 -1255
- package/test/integration/spec/mercury.js +94 -94
- package/test/integration/spec/share.js +537 -537
- package/test/integration/spec/verbs.js +1041 -1041
- package/test/unit/spec/conversation.js +823 -823
- package/test/unit/spec/decrypt-transforms.js +460 -460
- package/test/unit/spec/encryption-transforms.js +93 -93
- package/test/unit/spec/share-activity.js +178 -178
|
@@ -1,537 +1,537 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import '@webex/internal-plugin-conversation';
|
|
6
|
-
|
|
7
|
-
import {Defer} from '@webex/common';
|
|
8
|
-
import WebexCore from '@webex/webex-core';
|
|
9
|
-
import fh from '@webex/test-helper-file';
|
|
10
|
-
import sinon from 'sinon';
|
|
11
|
-
import {assert} from '@webex/test-helper-chai';
|
|
12
|
-
import testUsers from '@webex/test-helper-test-users';
|
|
13
|
-
import {find} from 'lodash';
|
|
14
|
-
import uuid from 'uuid';
|
|
15
|
-
import {flaky, skipInNode, browserOnly} from '@webex/test-helper-mocha';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Resolves with the first argument passed in, after applying `fn()` on that
|
|
19
|
-
* argument
|
|
20
|
-
* @param {Function} fn
|
|
21
|
-
* @returns {Promise<mixed>}
|
|
22
|
-
*/
|
|
23
|
-
function returnFirstArg(fn) {
|
|
24
|
-
return (result) => Promise.resolve(fn(result)).then(() => result);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
describe('plugin-conversation', function () {
|
|
28
|
-
this.timeout(120000);
|
|
29
|
-
describe('share', () => {
|
|
30
|
-
let mccoy, participants, webex, spock;
|
|
31
|
-
|
|
32
|
-
before(() =>
|
|
33
|
-
testUsers.create({count: 3}).then(async (users) => {
|
|
34
|
-
participants = users;
|
|
35
|
-
[spock, mccoy] = participants;
|
|
36
|
-
|
|
37
|
-
// Pause for 5 seconds for CI
|
|
38
|
-
await new Promise((done) => setTimeout(done, 5000));
|
|
39
|
-
|
|
40
|
-
webex = new WebexCore({
|
|
41
|
-
credentials: {
|
|
42
|
-
authorization: spock.token,
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
mccoy.webex = new WebexCore({
|
|
47
|
-
credentials: {
|
|
48
|
-
authorization: mccoy.token,
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
return Promise.all([
|
|
53
|
-
webex.internal.mercury.connect(),
|
|
54
|
-
mccoy.webex.internal.mercury.connect(),
|
|
55
|
-
]);
|
|
56
|
-
})
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
after(() =>
|
|
60
|
-
Promise.all([
|
|
61
|
-
webex && webex.internal.mercury.disconnect(),
|
|
62
|
-
mccoy && mccoy.webex.internal.mercury.disconnect(),
|
|
63
|
-
])
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
let conversation;
|
|
67
|
-
|
|
68
|
-
beforeEach(() => {
|
|
69
|
-
if (conversation) {
|
|
70
|
-
return Promise.resolve();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return webex.internal.conversation.create({participants}).then((c) => {
|
|
74
|
-
conversation = c;
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
let hashTestText = '#test.txt';
|
|
79
|
-
let sampleImageSmallOnePng = 'sample-image-small-one.png';
|
|
80
|
-
let sampleImageSmallTwoPng = 'sample-image-small-two.png';
|
|
81
|
-
let sampleImageLargeJpg = 'sample-image-large.jpg';
|
|
82
|
-
let sampleImageLargeNoEXIFJpg = 'sample-image-large-no-exif.jpg';
|
|
83
|
-
let samplePowerpointTwoPagePpt = 'sample-powerpoint-two-page.ppt';
|
|
84
|
-
let sampleTextOne = 'sample-text-one.txt';
|
|
85
|
-
let sampleTextTwo = 'sample-text-two.txt';
|
|
86
|
-
const sampleGif = 'sample-gif.gif';
|
|
87
|
-
|
|
88
|
-
before(() =>
|
|
89
|
-
Promise.all([
|
|
90
|
-
fh.fetchWithoutMagic(hashTestText),
|
|
91
|
-
fh.fetchWithoutMagic(sampleImageSmallOnePng),
|
|
92
|
-
fh.fetchWithoutMagic(sampleImageSmallTwoPng),
|
|
93
|
-
fh.fetchWithoutMagic(sampleImageLargeJpg),
|
|
94
|
-
fh.fetchWithoutMagic(sampleImageLargeNoEXIFJpg),
|
|
95
|
-
fh.fetchWithoutMagic(samplePowerpointTwoPagePpt),
|
|
96
|
-
fh.fetchWithoutMagic(sampleTextOne),
|
|
97
|
-
fh.fetchWithoutMagic(sampleTextTwo),
|
|
98
|
-
]).then((res) => {
|
|
99
|
-
[
|
|
100
|
-
hashTestText,
|
|
101
|
-
sampleImageSmallOnePng,
|
|
102
|
-
sampleImageSmallTwoPng,
|
|
103
|
-
sampleImageLargeJpg,
|
|
104
|
-
sampleImageLargeNoEXIFJpg,
|
|
105
|
-
samplePowerpointTwoPagePpt,
|
|
106
|
-
sampleTextOne,
|
|
107
|
-
sampleTextTwo,
|
|
108
|
-
] = res;
|
|
109
|
-
})
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
describe('#share()', () => {
|
|
113
|
-
it('shares the specified file to the specified conversation', () =>
|
|
114
|
-
webex.internal.conversation
|
|
115
|
-
.share(conversation, [sampleTextOne])
|
|
116
|
-
.then((activity) => {
|
|
117
|
-
assert.isActivity(activity);
|
|
118
|
-
assert.isEncryptedActivity(activity);
|
|
119
|
-
assert.isFileItem(activity.object.files.items[0]);
|
|
120
|
-
|
|
121
|
-
return webex.internal.conversation.download(activity.object.files.items[0]);
|
|
122
|
-
})
|
|
123
|
-
.then(returnFirstArg((f) => assert.match(f.type, /text\/plain/)))
|
|
124
|
-
.then((f) =>
|
|
125
|
-
fh.isMatchingFile(f, sampleTextOne).then((result) => assert.isTrue(result))
|
|
126
|
-
));
|
|
127
|
-
|
|
128
|
-
it('shares the specified set of files to the specified conversation', () =>
|
|
129
|
-
webex.internal.conversation
|
|
130
|
-
.share(conversation, [sampleTextOne, sampleTextTwo])
|
|
131
|
-
.then((activity) => {
|
|
132
|
-
assert.isActivity(activity);
|
|
133
|
-
assert.isEncryptedActivity(activity);
|
|
134
|
-
assert.isFileItem(activity.object.files.items[0]);
|
|
135
|
-
assert.isFileItem(activity.object.files.items[1]);
|
|
136
|
-
|
|
137
|
-
return Promise.all([
|
|
138
|
-
webex.internal.conversation
|
|
139
|
-
.download(activity.object.files.items[0])
|
|
140
|
-
.then(returnFirstArg((f) => assert.match(f.type, /text\/plain/))),
|
|
141
|
-
webex.internal.conversation
|
|
142
|
-
.download(activity.object.files.items[1])
|
|
143
|
-
.then(returnFirstArg((f) => assert.match(f.type, /text\/plain/))),
|
|
144
|
-
]);
|
|
145
|
-
})
|
|
146
|
-
.then(([file0, file1]) =>
|
|
147
|
-
Promise.all([
|
|
148
|
-
fh.isMatchingFile(file0, sampleTextOne).then((result) => assert.isTrue(result)),
|
|
149
|
-
fh.isMatchingFile(file1, sampleTextTwo).then((result) => assert.isTrue(result)),
|
|
150
|
-
])
|
|
151
|
-
));
|
|
152
|
-
|
|
153
|
-
describe('files with special characters', () => {
|
|
154
|
-
it('shares the specified file to the specified conversation', () =>
|
|
155
|
-
webex.internal.conversation
|
|
156
|
-
.share(conversation, [hashTestText])
|
|
157
|
-
.then((activity) => {
|
|
158
|
-
assert.isActivity(activity);
|
|
159
|
-
assert.isEncryptedActivity(activity);
|
|
160
|
-
assert.isFileItem(activity.object.files.items[0]);
|
|
161
|
-
|
|
162
|
-
return webex.internal.conversation.download(activity.object.files.items[0]);
|
|
163
|
-
})
|
|
164
|
-
// in node, this'll be 'text/plain', in a browser, it'll be
|
|
165
|
-
// 'text/html'. I'm pretty sure it's caused by the # convincing
|
|
166
|
-
// express it's a hashroute and treating it as html. The discrepancy
|
|
167
|
-
// has no bearing on the test's validity. Further, we need to use
|
|
168
|
-
// match rather than equal because some browser append the charset.
|
|
169
|
-
.then(returnFirstArg((f) => assert.match(f.type, hashTestText.type || /text\/plain/)))
|
|
170
|
-
.then((f) =>
|
|
171
|
-
fh.isMatchingFile(f, hashTestText).then((result) => assert.isTrue(result))
|
|
172
|
-
));
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it('shares an image with no EXIF data to the specified conversation and correctly error handles', () =>
|
|
176
|
-
webex.internal.conversation
|
|
177
|
-
.share(conversation, [sampleImageLargeNoEXIFJpg])
|
|
178
|
-
.then((activity) => {
|
|
179
|
-
assert.isActivity(activity);
|
|
180
|
-
assert.isEncryptedActivity(activity);
|
|
181
|
-
|
|
182
|
-
const fileItem = activity.object.files.items[0];
|
|
183
|
-
|
|
184
|
-
assert.isFileItem(fileItem);
|
|
185
|
-
|
|
186
|
-
const thumbnailItem = activity.object.files.items[0].image;
|
|
187
|
-
|
|
188
|
-
assert.isThumbnailItem(thumbnailItem);
|
|
189
|
-
assert.equal(thumbnailItem.width, 640);
|
|
190
|
-
assert.isAbove(thumbnailItem.height, 358);
|
|
191
|
-
assert.isBelow(thumbnailItem.height, 361);
|
|
192
|
-
|
|
193
|
-
return webex.internal.conversation.download(activity.object.files.items[0]);
|
|
194
|
-
})
|
|
195
|
-
.then(returnFirstArg((f) => assert.equal(f.type, 'image/jpeg')))
|
|
196
|
-
.then((f) =>
|
|
197
|
-
fh.isMatchingFile(f, sampleImageLargeNoEXIFJpg).then((result) => assert.isTrue(result))
|
|
198
|
-
));
|
|
199
|
-
|
|
200
|
-
it('shares the specified image to the specified conversation', () =>
|
|
201
|
-
webex.internal.conversation
|
|
202
|
-
.share(conversation, [sampleImageLargeJpg])
|
|
203
|
-
.then((activity) => {
|
|
204
|
-
assert.isActivity(activity);
|
|
205
|
-
assert.isEncryptedActivity(activity);
|
|
206
|
-
|
|
207
|
-
const fileItem = activity.object.files.items[0];
|
|
208
|
-
|
|
209
|
-
assert.isFileItem(fileItem);
|
|
210
|
-
|
|
211
|
-
const thumbnailItem = activity.object.files.items[0].image;
|
|
212
|
-
|
|
213
|
-
assert.isThumbnailItem(thumbnailItem);
|
|
214
|
-
assert.equal(thumbnailItem.width, 640);
|
|
215
|
-
assert.isAbove(thumbnailItem.height, 330);
|
|
216
|
-
assert.isBelow(thumbnailItem.height, 361);
|
|
217
|
-
|
|
218
|
-
return webex.internal.conversation.download(activity.object.files.items[0]);
|
|
219
|
-
})
|
|
220
|
-
.then(returnFirstArg((f) => assert.equal(f.type, 'image/jpeg')))
|
|
221
|
-
.then((f) =>
|
|
222
|
-
fh.isMatchingFile(f, sampleImageLargeJpg).then((result) => assert.isTrue(result))
|
|
223
|
-
));
|
|
224
|
-
|
|
225
|
-
it('shares the specified set of images the specified conversation', () =>
|
|
226
|
-
webex.internal.conversation
|
|
227
|
-
.share(conversation, [sampleImageSmallOnePng, sampleImageSmallTwoPng])
|
|
228
|
-
.then((activity) => {
|
|
229
|
-
assert.isActivity(activity);
|
|
230
|
-
assert.isEncryptedActivity(activity);
|
|
231
|
-
assert.isFileItem(activity.object.files.items[0]);
|
|
232
|
-
assert.isFileItem(activity.object.files.items[1]);
|
|
233
|
-
assert.isThumbnailItem(activity.object.files.items[0].image);
|
|
234
|
-
assert.isThumbnailItem(activity.object.files.items[1].image);
|
|
235
|
-
|
|
236
|
-
return Promise.all([
|
|
237
|
-
webex.internal.conversation
|
|
238
|
-
.download(activity.object.files.items[0])
|
|
239
|
-
.then(returnFirstArg((f) => assert.equal(f.type, 'image/png'))),
|
|
240
|
-
webex.internal.conversation
|
|
241
|
-
.download(activity.object.files.items[1])
|
|
242
|
-
.then(returnFirstArg((f) => assert.equal(f.type, 'image/png'))),
|
|
243
|
-
]);
|
|
244
|
-
})
|
|
245
|
-
.then(([file0, file1]) =>
|
|
246
|
-
Promise.all([
|
|
247
|
-
fh
|
|
248
|
-
.isMatchingFile(file0, sampleImageSmallOnePng)
|
|
249
|
-
.then((result) => assert.isTrue(result)),
|
|
250
|
-
fh
|
|
251
|
-
.isMatchingFile(file1, sampleImageSmallTwoPng)
|
|
252
|
-
.then((result) => assert.isTrue(result)),
|
|
253
|
-
])
|
|
254
|
-
));
|
|
255
|
-
|
|
256
|
-
describe('when it shares a transcodable file', () => {
|
|
257
|
-
let activities;
|
|
258
|
-
let blockUntilTranscode;
|
|
259
|
-
let clientTempId;
|
|
260
|
-
let objectUrl;
|
|
261
|
-
|
|
262
|
-
beforeEach(() => {
|
|
263
|
-
clientTempId = uuid.v4();
|
|
264
|
-
activities = [];
|
|
265
|
-
webex.internal.mercury.on('event:conversation.activity', onMessage);
|
|
266
|
-
blockUntilTranscode = new Defer();
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
afterEach(
|
|
270
|
-
() => webex && webex.internal.mercury.off('event:conversation.activity', onMessage)
|
|
271
|
-
);
|
|
272
|
-
|
|
273
|
-
function onMessage(message) {
|
|
274
|
-
activities.push(message.data.activity);
|
|
275
|
-
|
|
276
|
-
if (message.data.activity.clientTempId === clientTempId) {
|
|
277
|
-
objectUrl = message.data.activity.object.url;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (objectUrl) {
|
|
281
|
-
const updateActivity = find(
|
|
282
|
-
activities,
|
|
283
|
-
(activity) => activity.verb === 'update' && activity.object.url === objectUrl
|
|
284
|
-
);
|
|
285
|
-
|
|
286
|
-
if (updateActivity) {
|
|
287
|
-
blockUntilTranscode.resolve(updateActivity);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// doesn't seem like we get mercury event back to update transcoded file in time
|
|
293
|
-
// https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-166178
|
|
294
|
-
it.skip('mercury receives an update', () =>
|
|
295
|
-
webex.internal.conversation
|
|
296
|
-
.share(conversation, {
|
|
297
|
-
object: {
|
|
298
|
-
files: [samplePowerpointTwoPagePpt],
|
|
299
|
-
},
|
|
300
|
-
clientTempId,
|
|
301
|
-
})
|
|
302
|
-
.then((activity) => {
|
|
303
|
-
assert.equal(activity.clientTempId, clientTempId);
|
|
304
|
-
activities.push(activity);
|
|
305
|
-
|
|
306
|
-
return webex.internal.conversation
|
|
307
|
-
.download(activity.object.files.items[0])
|
|
308
|
-
.then((f) => assert.equal(f.type, 'application/vnd.ms-powerpoint'))
|
|
309
|
-
.then(() => blockUntilTranscode.promise)
|
|
310
|
-
.then((updateActivity) => {
|
|
311
|
-
assert.equal(updateActivity.object.url, activity.object.url);
|
|
312
|
-
assert.lengthOf(
|
|
313
|
-
updateActivity.object.files.items[0].transcodedCollection.items[0].files.items,
|
|
314
|
-
2
|
|
315
|
-
);
|
|
316
|
-
// Prove that the newly transcoded file can be downloaded and
|
|
317
|
-
// decrypted
|
|
318
|
-
const firstItem =
|
|
319
|
-
updateActivity.object.files.items[0].transcodedCollection.items[0].files
|
|
320
|
-
.items[0];
|
|
321
|
-
|
|
322
|
-
return webex.internal.conversation.download(firstItem);
|
|
323
|
-
});
|
|
324
|
-
}));
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
it('shares a whiteboard', () => {
|
|
328
|
-
const activity = webex.internal.conversation.makeShare(conversation);
|
|
329
|
-
|
|
330
|
-
activity.add(sampleImageSmallOnePng, {
|
|
331
|
-
actions: [
|
|
332
|
-
{
|
|
333
|
-
type: 'edit',
|
|
334
|
-
mimeType: 'application/x-cisco-webex-whiteboard',
|
|
335
|
-
url: 'https://boards.example.com/boards/1',
|
|
336
|
-
},
|
|
337
|
-
],
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
return webex.internal.conversation
|
|
341
|
-
.share(conversation, activity)
|
|
342
|
-
.then((share) => {
|
|
343
|
-
assert.isActivity(share);
|
|
344
|
-
assert.isEncryptedActivity(share);
|
|
345
|
-
assert.isFileItem(share.object.files.items[0]);
|
|
346
|
-
assert.isThumbnailItem(share.object.files.items[0].image);
|
|
347
|
-
assert.equal(share.object.contentCategory, 'documents');
|
|
348
|
-
assert.isArray(share.object.files.items[0].actions);
|
|
349
|
-
assert.equal(share.object.files.items[0].actions[0].type, 'edit');
|
|
350
|
-
assert.equal(
|
|
351
|
-
share.object.files.items[0].actions[0].mimeType,
|
|
352
|
-
'application/x-cisco-webex-whiteboard'
|
|
353
|
-
);
|
|
354
|
-
assert.equal(
|
|
355
|
-
share.object.files.items[0].actions[0].url,
|
|
356
|
-
'https://boards.example.com/boards/1'
|
|
357
|
-
);
|
|
358
|
-
|
|
359
|
-
return webex.internal.conversation
|
|
360
|
-
.download(share.object.files.items[0])
|
|
361
|
-
.then(returnFirstArg((f) => assert.equal(f.type, 'image/png')));
|
|
362
|
-
})
|
|
363
|
-
.then((file0) =>
|
|
364
|
-
fh.isMatchingFile(file0, sampleImageSmallOnePng).then((result) => assert.isTrue(result))
|
|
365
|
-
);
|
|
366
|
-
});
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
describe('#makeShare', () => {
|
|
370
|
-
// http-core doesn't current do upload progress events in node, so this
|
|
371
|
-
// test is browser-only for now
|
|
372
|
-
skipInNode(flaky(it, process.env.SKIP_FLAKY_TESTS))(
|
|
373
|
-
'provides an interface for file upload events',
|
|
374
|
-
() => {
|
|
375
|
-
const spy = sinon.spy();
|
|
376
|
-
const share = webex.internal.conversation.makeShare(conversation);
|
|
377
|
-
const emitter = share.add(sampleImageSmallOnePng);
|
|
378
|
-
|
|
379
|
-
emitter.on('progress', spy);
|
|
380
|
-
|
|
381
|
-
return webex.internal.conversation
|
|
382
|
-
.share(conversation, share)
|
|
383
|
-
.then(() => assert.called(spy));
|
|
384
|
-
}
|
|
385
|
-
);
|
|
386
|
-
|
|
387
|
-
it('shares a file with a name', () => {
|
|
388
|
-
const share = webex.internal.conversation.makeShare(conversation);
|
|
389
|
-
|
|
390
|
-
share.add(sampleImageSmallOnePng);
|
|
391
|
-
share.object = {
|
|
392
|
-
displayName: 'a name',
|
|
393
|
-
};
|
|
394
|
-
|
|
395
|
-
return webex.internal.conversation
|
|
396
|
-
.share(conversation, share)
|
|
397
|
-
.then((activity) => {
|
|
398
|
-
assert.equal(activity.object.displayName, 'a name');
|
|
399
|
-
|
|
400
|
-
return webex.internal.conversation.download(activity.object.files.items[0]);
|
|
401
|
-
})
|
|
402
|
-
.then(returnFirstArg((f) => assert.equal(f.type, 'image/png')))
|
|
403
|
-
.then((file) => fh.isMatchingFile(file, sampleImageSmallOnePng));
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
it('allows removal of a file from the share', () => {
|
|
407
|
-
const share = webex.internal.conversation.makeShare(conversation);
|
|
408
|
-
|
|
409
|
-
share.add(sampleImageSmallOnePng);
|
|
410
|
-
share.add(sampleImageSmallTwoPng);
|
|
411
|
-
share.remove(sampleImageSmallOnePng);
|
|
412
|
-
share.object = {
|
|
413
|
-
displayName: 'a name',
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
return webex.internal.conversation
|
|
417
|
-
.share(conversation, share)
|
|
418
|
-
.then((activity) => {
|
|
419
|
-
assert.equal(activity.object.displayName, 'a name');
|
|
420
|
-
assert.lengthOf(activity.object.files.items, 1);
|
|
421
|
-
|
|
422
|
-
return webex.internal.conversation.download(activity.object.files.items[0]);
|
|
423
|
-
})
|
|
424
|
-
.then(returnFirstArg((f) => assert.equal(f.type, 'image/png')))
|
|
425
|
-
.then((file) => fh.isMatchingFile(file, sampleImageSmallTwoPng));
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
it('shares a file to a thread', () => {
|
|
429
|
-
const share = webex.internal.conversation.makeShare(conversation);
|
|
430
|
-
|
|
431
|
-
share.add(sampleImageSmallOnePng);
|
|
432
|
-
share.object = {
|
|
433
|
-
displayName: 'a name',
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
let parentActivityId;
|
|
437
|
-
|
|
438
|
-
return webex.internal.conversation
|
|
439
|
-
.share(conversation, share)
|
|
440
|
-
.then((activity) => {
|
|
441
|
-
assert.equal(activity.object.displayName, 'a name');
|
|
442
|
-
const threadShare = webex.internal.conversation.makeShare(conversation);
|
|
443
|
-
|
|
444
|
-
threadShare.add(sampleImageSmallOnePng);
|
|
445
|
-
threadShare.object = {
|
|
446
|
-
displayName: 'a thread share name',
|
|
447
|
-
};
|
|
448
|
-
threadShare.activityType = 'reply';
|
|
449
|
-
threadShare.parentActivityId = activity.id;
|
|
450
|
-
parentActivityId = activity.id;
|
|
451
|
-
|
|
452
|
-
return webex.internal.conversation.share(conversation, threadShare);
|
|
453
|
-
})
|
|
454
|
-
.then((activity) => {
|
|
455
|
-
const {id, type} = activity.parent;
|
|
456
|
-
|
|
457
|
-
assert.equal(id, parentActivityId);
|
|
458
|
-
assert.equal(type, 'reply');
|
|
459
|
-
|
|
460
|
-
return webex.internal.conversation.download(activity.object.files.items[0]);
|
|
461
|
-
})
|
|
462
|
-
.then(returnFirstArg((f) => assert.equal(f.type, 'image/png')))
|
|
463
|
-
.then((file) => fh.isMatchingFile(file, sampleImageSmallOnePng));
|
|
464
|
-
});
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
describe('#addGif', () => {
|
|
468
|
-
let blob, buffer;
|
|
469
|
-
|
|
470
|
-
// Read file as buffer
|
|
471
|
-
browserOnly(before)(() =>
|
|
472
|
-
fh.fetch(sampleGif).then((file) => {
|
|
473
|
-
blob = file;
|
|
474
|
-
|
|
475
|
-
return new Promise((resolve) => {
|
|
476
|
-
/* global FileReader */
|
|
477
|
-
const fileReader = new FileReader();
|
|
478
|
-
|
|
479
|
-
fileReader.onload = function () {
|
|
480
|
-
buffer = this.result;
|
|
481
|
-
resolve();
|
|
482
|
-
};
|
|
483
|
-
fileReader.readAsArrayBuffer(blob);
|
|
484
|
-
});
|
|
485
|
-
})
|
|
486
|
-
);
|
|
487
|
-
|
|
488
|
-
browserOnly(it)(
|
|
489
|
-
'if the giphy does not exist, then we check it gets added to this.uploads',
|
|
490
|
-
(done) => {
|
|
491
|
-
// eslint-disable-next-line no-undef
|
|
492
|
-
const file = new File([buffer], blob.name, {type: 'image/gif'});
|
|
493
|
-
|
|
494
|
-
const originalGiphyURL = 'https://media1.giphy.com/media/nXxOjZrbnbRxS/giphy.gif';
|
|
495
|
-
const originalGiphyStillURL = 'https://media1.giphy.com/media/nXxOjZrbnbRxS/giphy_s.gif';
|
|
496
|
-
const url = 'https://giphy.com';
|
|
497
|
-
|
|
498
|
-
// simulate in web client where
|
|
499
|
-
Object.defineProperty(file, 'url', {value: originalGiphyURL});
|
|
500
|
-
// define thumbnail
|
|
501
|
-
Object.defineProperty(file, 'image', {
|
|
502
|
-
value: {
|
|
503
|
-
height: file.width,
|
|
504
|
-
width: file.height,
|
|
505
|
-
url: originalGiphyStillURL,
|
|
506
|
-
},
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
const share = webex.internal.conversation.makeShare(conversation);
|
|
510
|
-
|
|
511
|
-
// Check that initially there were no uploads
|
|
512
|
-
assert.isTrue(share.uploads.size === 0);
|
|
513
|
-
share.addGif(file).then(() => {
|
|
514
|
-
assert.equal(share.uploads.size, 1);
|
|
515
|
-
assert.equal(share.uploads.get(file).objectType, 'file');
|
|
516
|
-
assert.equal(share.uploads.get(file).displayName, sampleGif);
|
|
517
|
-
assert.equal(share.uploads.get(file).mimeType, 'image/gif');
|
|
518
|
-
assert.equal(share.uploads.get(file).fileSize, 473119);
|
|
519
|
-
assert.equal(share.uploads.get(file).width, 200);
|
|
520
|
-
assert.equal(share.uploads.get(file).height, 270);
|
|
521
|
-
assert.equal(share.uploads.get(file).url, url);
|
|
522
|
-
assert.exists(share.uploads.get(file).scr);
|
|
523
|
-
assert.equal(share.uploads.get(file).scr.loc, originalGiphyURL);
|
|
524
|
-
|
|
525
|
-
assert.exists(share.uploads.get(file).image);
|
|
526
|
-
assert.equal(share.uploads.get(file).image.width, 200);
|
|
527
|
-
assert.equal(share.uploads.get(file).image.height, 270);
|
|
528
|
-
assert.equal(share.uploads.get(file).image.url, url);
|
|
529
|
-
assert.exists(share.uploads.get(file).image.scr);
|
|
530
|
-
assert.equal(share.uploads.get(file).image.scr.loc, originalGiphyStillURL);
|
|
531
|
-
});
|
|
532
|
-
done();
|
|
533
|
-
}
|
|
534
|
-
);
|
|
535
|
-
});
|
|
536
|
-
});
|
|
537
|
-
});
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import '@webex/internal-plugin-conversation';
|
|
6
|
+
|
|
7
|
+
import {Defer} from '@webex/common';
|
|
8
|
+
import WebexCore from '@webex/webex-core';
|
|
9
|
+
import fh from '@webex/test-helper-file';
|
|
10
|
+
import sinon from 'sinon';
|
|
11
|
+
import {assert} from '@webex/test-helper-chai';
|
|
12
|
+
import testUsers from '@webex/test-helper-test-users';
|
|
13
|
+
import {find} from 'lodash';
|
|
14
|
+
import uuid from 'uuid';
|
|
15
|
+
import {flaky, skipInNode, browserOnly} from '@webex/test-helper-mocha';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Resolves with the first argument passed in, after applying `fn()` on that
|
|
19
|
+
* argument
|
|
20
|
+
* @param {Function} fn
|
|
21
|
+
* @returns {Promise<mixed>}
|
|
22
|
+
*/
|
|
23
|
+
function returnFirstArg(fn) {
|
|
24
|
+
return (result) => Promise.resolve(fn(result)).then(() => result);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('plugin-conversation', function () {
|
|
28
|
+
this.timeout(120000);
|
|
29
|
+
describe('share', () => {
|
|
30
|
+
let mccoy, participants, webex, spock;
|
|
31
|
+
|
|
32
|
+
before(() =>
|
|
33
|
+
testUsers.create({count: 3}).then(async (users) => {
|
|
34
|
+
participants = users;
|
|
35
|
+
[spock, mccoy] = participants;
|
|
36
|
+
|
|
37
|
+
// Pause for 5 seconds for CI
|
|
38
|
+
await new Promise((done) => setTimeout(done, 5000));
|
|
39
|
+
|
|
40
|
+
webex = new WebexCore({
|
|
41
|
+
credentials: {
|
|
42
|
+
authorization: spock.token,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
mccoy.webex = new WebexCore({
|
|
47
|
+
credentials: {
|
|
48
|
+
authorization: mccoy.token,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return Promise.all([
|
|
53
|
+
webex.internal.mercury.connect(),
|
|
54
|
+
mccoy.webex.internal.mercury.connect(),
|
|
55
|
+
]);
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
after(() =>
|
|
60
|
+
Promise.all([
|
|
61
|
+
webex && webex.internal.mercury.disconnect(),
|
|
62
|
+
mccoy && mccoy.webex.internal.mercury.disconnect(),
|
|
63
|
+
])
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
let conversation;
|
|
67
|
+
|
|
68
|
+
beforeEach(() => {
|
|
69
|
+
if (conversation) {
|
|
70
|
+
return Promise.resolve();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return webex.internal.conversation.create({participants}).then((c) => {
|
|
74
|
+
conversation = c;
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
let hashTestText = '#test.txt';
|
|
79
|
+
let sampleImageSmallOnePng = 'sample-image-small-one.png';
|
|
80
|
+
let sampleImageSmallTwoPng = 'sample-image-small-two.png';
|
|
81
|
+
let sampleImageLargeJpg = 'sample-image-large.jpg';
|
|
82
|
+
let sampleImageLargeNoEXIFJpg = 'sample-image-large-no-exif.jpg';
|
|
83
|
+
let samplePowerpointTwoPagePpt = 'sample-powerpoint-two-page.ppt';
|
|
84
|
+
let sampleTextOne = 'sample-text-one.txt';
|
|
85
|
+
let sampleTextTwo = 'sample-text-two.txt';
|
|
86
|
+
const sampleGif = 'sample-gif.gif';
|
|
87
|
+
|
|
88
|
+
before(() =>
|
|
89
|
+
Promise.all([
|
|
90
|
+
fh.fetchWithoutMagic(hashTestText),
|
|
91
|
+
fh.fetchWithoutMagic(sampleImageSmallOnePng),
|
|
92
|
+
fh.fetchWithoutMagic(sampleImageSmallTwoPng),
|
|
93
|
+
fh.fetchWithoutMagic(sampleImageLargeJpg),
|
|
94
|
+
fh.fetchWithoutMagic(sampleImageLargeNoEXIFJpg),
|
|
95
|
+
fh.fetchWithoutMagic(samplePowerpointTwoPagePpt),
|
|
96
|
+
fh.fetchWithoutMagic(sampleTextOne),
|
|
97
|
+
fh.fetchWithoutMagic(sampleTextTwo),
|
|
98
|
+
]).then((res) => {
|
|
99
|
+
[
|
|
100
|
+
hashTestText,
|
|
101
|
+
sampleImageSmallOnePng,
|
|
102
|
+
sampleImageSmallTwoPng,
|
|
103
|
+
sampleImageLargeJpg,
|
|
104
|
+
sampleImageLargeNoEXIFJpg,
|
|
105
|
+
samplePowerpointTwoPagePpt,
|
|
106
|
+
sampleTextOne,
|
|
107
|
+
sampleTextTwo,
|
|
108
|
+
] = res;
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
describe('#share()', () => {
|
|
113
|
+
it('shares the specified file to the specified conversation', () =>
|
|
114
|
+
webex.internal.conversation
|
|
115
|
+
.share(conversation, [sampleTextOne])
|
|
116
|
+
.then((activity) => {
|
|
117
|
+
assert.isActivity(activity);
|
|
118
|
+
assert.isEncryptedActivity(activity);
|
|
119
|
+
assert.isFileItem(activity.object.files.items[0]);
|
|
120
|
+
|
|
121
|
+
return webex.internal.conversation.download(activity.object.files.items[0]);
|
|
122
|
+
})
|
|
123
|
+
.then(returnFirstArg((f) => assert.match(f.type, /text\/plain/)))
|
|
124
|
+
.then((f) =>
|
|
125
|
+
fh.isMatchingFile(f, sampleTextOne).then((result) => assert.isTrue(result))
|
|
126
|
+
));
|
|
127
|
+
|
|
128
|
+
it('shares the specified set of files to the specified conversation', () =>
|
|
129
|
+
webex.internal.conversation
|
|
130
|
+
.share(conversation, [sampleTextOne, sampleTextTwo])
|
|
131
|
+
.then((activity) => {
|
|
132
|
+
assert.isActivity(activity);
|
|
133
|
+
assert.isEncryptedActivity(activity);
|
|
134
|
+
assert.isFileItem(activity.object.files.items[0]);
|
|
135
|
+
assert.isFileItem(activity.object.files.items[1]);
|
|
136
|
+
|
|
137
|
+
return Promise.all([
|
|
138
|
+
webex.internal.conversation
|
|
139
|
+
.download(activity.object.files.items[0])
|
|
140
|
+
.then(returnFirstArg((f) => assert.match(f.type, /text\/plain/))),
|
|
141
|
+
webex.internal.conversation
|
|
142
|
+
.download(activity.object.files.items[1])
|
|
143
|
+
.then(returnFirstArg((f) => assert.match(f.type, /text\/plain/))),
|
|
144
|
+
]);
|
|
145
|
+
})
|
|
146
|
+
.then(([file0, file1]) =>
|
|
147
|
+
Promise.all([
|
|
148
|
+
fh.isMatchingFile(file0, sampleTextOne).then((result) => assert.isTrue(result)),
|
|
149
|
+
fh.isMatchingFile(file1, sampleTextTwo).then((result) => assert.isTrue(result)),
|
|
150
|
+
])
|
|
151
|
+
));
|
|
152
|
+
|
|
153
|
+
describe('files with special characters', () => {
|
|
154
|
+
it('shares the specified file to the specified conversation', () =>
|
|
155
|
+
webex.internal.conversation
|
|
156
|
+
.share(conversation, [hashTestText])
|
|
157
|
+
.then((activity) => {
|
|
158
|
+
assert.isActivity(activity);
|
|
159
|
+
assert.isEncryptedActivity(activity);
|
|
160
|
+
assert.isFileItem(activity.object.files.items[0]);
|
|
161
|
+
|
|
162
|
+
return webex.internal.conversation.download(activity.object.files.items[0]);
|
|
163
|
+
})
|
|
164
|
+
// in node, this'll be 'text/plain', in a browser, it'll be
|
|
165
|
+
// 'text/html'. I'm pretty sure it's caused by the # convincing
|
|
166
|
+
// express it's a hashroute and treating it as html. The discrepancy
|
|
167
|
+
// has no bearing on the test's validity. Further, we need to use
|
|
168
|
+
// match rather than equal because some browser append the charset.
|
|
169
|
+
.then(returnFirstArg((f) => assert.match(f.type, hashTestText.type || /text\/plain/)))
|
|
170
|
+
.then((f) =>
|
|
171
|
+
fh.isMatchingFile(f, hashTestText).then((result) => assert.isTrue(result))
|
|
172
|
+
));
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('shares an image with no EXIF data to the specified conversation and correctly error handles', () =>
|
|
176
|
+
webex.internal.conversation
|
|
177
|
+
.share(conversation, [sampleImageLargeNoEXIFJpg])
|
|
178
|
+
.then((activity) => {
|
|
179
|
+
assert.isActivity(activity);
|
|
180
|
+
assert.isEncryptedActivity(activity);
|
|
181
|
+
|
|
182
|
+
const fileItem = activity.object.files.items[0];
|
|
183
|
+
|
|
184
|
+
assert.isFileItem(fileItem);
|
|
185
|
+
|
|
186
|
+
const thumbnailItem = activity.object.files.items[0].image;
|
|
187
|
+
|
|
188
|
+
assert.isThumbnailItem(thumbnailItem);
|
|
189
|
+
assert.equal(thumbnailItem.width, 640);
|
|
190
|
+
assert.isAbove(thumbnailItem.height, 358);
|
|
191
|
+
assert.isBelow(thumbnailItem.height, 361);
|
|
192
|
+
|
|
193
|
+
return webex.internal.conversation.download(activity.object.files.items[0]);
|
|
194
|
+
})
|
|
195
|
+
.then(returnFirstArg((f) => assert.equal(f.type, 'image/jpeg')))
|
|
196
|
+
.then((f) =>
|
|
197
|
+
fh.isMatchingFile(f, sampleImageLargeNoEXIFJpg).then((result) => assert.isTrue(result))
|
|
198
|
+
));
|
|
199
|
+
|
|
200
|
+
it('shares the specified image to the specified conversation', () =>
|
|
201
|
+
webex.internal.conversation
|
|
202
|
+
.share(conversation, [sampleImageLargeJpg])
|
|
203
|
+
.then((activity) => {
|
|
204
|
+
assert.isActivity(activity);
|
|
205
|
+
assert.isEncryptedActivity(activity);
|
|
206
|
+
|
|
207
|
+
const fileItem = activity.object.files.items[0];
|
|
208
|
+
|
|
209
|
+
assert.isFileItem(fileItem);
|
|
210
|
+
|
|
211
|
+
const thumbnailItem = activity.object.files.items[0].image;
|
|
212
|
+
|
|
213
|
+
assert.isThumbnailItem(thumbnailItem);
|
|
214
|
+
assert.equal(thumbnailItem.width, 640);
|
|
215
|
+
assert.isAbove(thumbnailItem.height, 330);
|
|
216
|
+
assert.isBelow(thumbnailItem.height, 361);
|
|
217
|
+
|
|
218
|
+
return webex.internal.conversation.download(activity.object.files.items[0]);
|
|
219
|
+
})
|
|
220
|
+
.then(returnFirstArg((f) => assert.equal(f.type, 'image/jpeg')))
|
|
221
|
+
.then((f) =>
|
|
222
|
+
fh.isMatchingFile(f, sampleImageLargeJpg).then((result) => assert.isTrue(result))
|
|
223
|
+
));
|
|
224
|
+
|
|
225
|
+
it('shares the specified set of images the specified conversation', () =>
|
|
226
|
+
webex.internal.conversation
|
|
227
|
+
.share(conversation, [sampleImageSmallOnePng, sampleImageSmallTwoPng])
|
|
228
|
+
.then((activity) => {
|
|
229
|
+
assert.isActivity(activity);
|
|
230
|
+
assert.isEncryptedActivity(activity);
|
|
231
|
+
assert.isFileItem(activity.object.files.items[0]);
|
|
232
|
+
assert.isFileItem(activity.object.files.items[1]);
|
|
233
|
+
assert.isThumbnailItem(activity.object.files.items[0].image);
|
|
234
|
+
assert.isThumbnailItem(activity.object.files.items[1].image);
|
|
235
|
+
|
|
236
|
+
return Promise.all([
|
|
237
|
+
webex.internal.conversation
|
|
238
|
+
.download(activity.object.files.items[0])
|
|
239
|
+
.then(returnFirstArg((f) => assert.equal(f.type, 'image/png'))),
|
|
240
|
+
webex.internal.conversation
|
|
241
|
+
.download(activity.object.files.items[1])
|
|
242
|
+
.then(returnFirstArg((f) => assert.equal(f.type, 'image/png'))),
|
|
243
|
+
]);
|
|
244
|
+
})
|
|
245
|
+
.then(([file0, file1]) =>
|
|
246
|
+
Promise.all([
|
|
247
|
+
fh
|
|
248
|
+
.isMatchingFile(file0, sampleImageSmallOnePng)
|
|
249
|
+
.then((result) => assert.isTrue(result)),
|
|
250
|
+
fh
|
|
251
|
+
.isMatchingFile(file1, sampleImageSmallTwoPng)
|
|
252
|
+
.then((result) => assert.isTrue(result)),
|
|
253
|
+
])
|
|
254
|
+
));
|
|
255
|
+
|
|
256
|
+
describe('when it shares a transcodable file', () => {
|
|
257
|
+
let activities;
|
|
258
|
+
let blockUntilTranscode;
|
|
259
|
+
let clientTempId;
|
|
260
|
+
let objectUrl;
|
|
261
|
+
|
|
262
|
+
beforeEach(() => {
|
|
263
|
+
clientTempId = uuid.v4();
|
|
264
|
+
activities = [];
|
|
265
|
+
webex.internal.mercury.on('event:conversation.activity', onMessage);
|
|
266
|
+
blockUntilTranscode = new Defer();
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
afterEach(
|
|
270
|
+
() => webex && webex.internal.mercury.off('event:conversation.activity', onMessage)
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
function onMessage(message) {
|
|
274
|
+
activities.push(message.data.activity);
|
|
275
|
+
|
|
276
|
+
if (message.data.activity.clientTempId === clientTempId) {
|
|
277
|
+
objectUrl = message.data.activity.object.url;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (objectUrl) {
|
|
281
|
+
const updateActivity = find(
|
|
282
|
+
activities,
|
|
283
|
+
(activity) => activity.verb === 'update' && activity.object.url === objectUrl
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
if (updateActivity) {
|
|
287
|
+
blockUntilTranscode.resolve(updateActivity);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// doesn't seem like we get mercury event back to update transcoded file in time
|
|
293
|
+
// https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-166178
|
|
294
|
+
it.skip('mercury receives an update', () =>
|
|
295
|
+
webex.internal.conversation
|
|
296
|
+
.share(conversation, {
|
|
297
|
+
object: {
|
|
298
|
+
files: [samplePowerpointTwoPagePpt],
|
|
299
|
+
},
|
|
300
|
+
clientTempId,
|
|
301
|
+
})
|
|
302
|
+
.then((activity) => {
|
|
303
|
+
assert.equal(activity.clientTempId, clientTempId);
|
|
304
|
+
activities.push(activity);
|
|
305
|
+
|
|
306
|
+
return webex.internal.conversation
|
|
307
|
+
.download(activity.object.files.items[0])
|
|
308
|
+
.then((f) => assert.equal(f.type, 'application/vnd.ms-powerpoint'))
|
|
309
|
+
.then(() => blockUntilTranscode.promise)
|
|
310
|
+
.then((updateActivity) => {
|
|
311
|
+
assert.equal(updateActivity.object.url, activity.object.url);
|
|
312
|
+
assert.lengthOf(
|
|
313
|
+
updateActivity.object.files.items[0].transcodedCollection.items[0].files.items,
|
|
314
|
+
2
|
|
315
|
+
);
|
|
316
|
+
// Prove that the newly transcoded file can be downloaded and
|
|
317
|
+
// decrypted
|
|
318
|
+
const firstItem =
|
|
319
|
+
updateActivity.object.files.items[0].transcodedCollection.items[0].files
|
|
320
|
+
.items[0];
|
|
321
|
+
|
|
322
|
+
return webex.internal.conversation.download(firstItem);
|
|
323
|
+
});
|
|
324
|
+
}));
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('shares a whiteboard', () => {
|
|
328
|
+
const activity = webex.internal.conversation.makeShare(conversation);
|
|
329
|
+
|
|
330
|
+
activity.add(sampleImageSmallOnePng, {
|
|
331
|
+
actions: [
|
|
332
|
+
{
|
|
333
|
+
type: 'edit',
|
|
334
|
+
mimeType: 'application/x-cisco-webex-whiteboard',
|
|
335
|
+
url: 'https://boards.example.com/boards/1',
|
|
336
|
+
},
|
|
337
|
+
],
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
return webex.internal.conversation
|
|
341
|
+
.share(conversation, activity)
|
|
342
|
+
.then((share) => {
|
|
343
|
+
assert.isActivity(share);
|
|
344
|
+
assert.isEncryptedActivity(share);
|
|
345
|
+
assert.isFileItem(share.object.files.items[0]);
|
|
346
|
+
assert.isThumbnailItem(share.object.files.items[0].image);
|
|
347
|
+
assert.equal(share.object.contentCategory, 'documents');
|
|
348
|
+
assert.isArray(share.object.files.items[0].actions);
|
|
349
|
+
assert.equal(share.object.files.items[0].actions[0].type, 'edit');
|
|
350
|
+
assert.equal(
|
|
351
|
+
share.object.files.items[0].actions[0].mimeType,
|
|
352
|
+
'application/x-cisco-webex-whiteboard'
|
|
353
|
+
);
|
|
354
|
+
assert.equal(
|
|
355
|
+
share.object.files.items[0].actions[0].url,
|
|
356
|
+
'https://boards.example.com/boards/1'
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
return webex.internal.conversation
|
|
360
|
+
.download(share.object.files.items[0])
|
|
361
|
+
.then(returnFirstArg((f) => assert.equal(f.type, 'image/png')));
|
|
362
|
+
})
|
|
363
|
+
.then((file0) =>
|
|
364
|
+
fh.isMatchingFile(file0, sampleImageSmallOnePng).then((result) => assert.isTrue(result))
|
|
365
|
+
);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
describe('#makeShare', () => {
|
|
370
|
+
// http-core doesn't current do upload progress events in node, so this
|
|
371
|
+
// test is browser-only for now
|
|
372
|
+
skipInNode(flaky(it, process.env.SKIP_FLAKY_TESTS))(
|
|
373
|
+
'provides an interface for file upload events',
|
|
374
|
+
() => {
|
|
375
|
+
const spy = sinon.spy();
|
|
376
|
+
const share = webex.internal.conversation.makeShare(conversation);
|
|
377
|
+
const emitter = share.add(sampleImageSmallOnePng);
|
|
378
|
+
|
|
379
|
+
emitter.on('progress', spy);
|
|
380
|
+
|
|
381
|
+
return webex.internal.conversation
|
|
382
|
+
.share(conversation, share)
|
|
383
|
+
.then(() => assert.called(spy));
|
|
384
|
+
}
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
it('shares a file with a name', () => {
|
|
388
|
+
const share = webex.internal.conversation.makeShare(conversation);
|
|
389
|
+
|
|
390
|
+
share.add(sampleImageSmallOnePng);
|
|
391
|
+
share.object = {
|
|
392
|
+
displayName: 'a name',
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
return webex.internal.conversation
|
|
396
|
+
.share(conversation, share)
|
|
397
|
+
.then((activity) => {
|
|
398
|
+
assert.equal(activity.object.displayName, 'a name');
|
|
399
|
+
|
|
400
|
+
return webex.internal.conversation.download(activity.object.files.items[0]);
|
|
401
|
+
})
|
|
402
|
+
.then(returnFirstArg((f) => assert.equal(f.type, 'image/png')))
|
|
403
|
+
.then((file) => fh.isMatchingFile(file, sampleImageSmallOnePng));
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it('allows removal of a file from the share', () => {
|
|
407
|
+
const share = webex.internal.conversation.makeShare(conversation);
|
|
408
|
+
|
|
409
|
+
share.add(sampleImageSmallOnePng);
|
|
410
|
+
share.add(sampleImageSmallTwoPng);
|
|
411
|
+
share.remove(sampleImageSmallOnePng);
|
|
412
|
+
share.object = {
|
|
413
|
+
displayName: 'a name',
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
return webex.internal.conversation
|
|
417
|
+
.share(conversation, share)
|
|
418
|
+
.then((activity) => {
|
|
419
|
+
assert.equal(activity.object.displayName, 'a name');
|
|
420
|
+
assert.lengthOf(activity.object.files.items, 1);
|
|
421
|
+
|
|
422
|
+
return webex.internal.conversation.download(activity.object.files.items[0]);
|
|
423
|
+
})
|
|
424
|
+
.then(returnFirstArg((f) => assert.equal(f.type, 'image/png')))
|
|
425
|
+
.then((file) => fh.isMatchingFile(file, sampleImageSmallTwoPng));
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('shares a file to a thread', () => {
|
|
429
|
+
const share = webex.internal.conversation.makeShare(conversation);
|
|
430
|
+
|
|
431
|
+
share.add(sampleImageSmallOnePng);
|
|
432
|
+
share.object = {
|
|
433
|
+
displayName: 'a name',
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
let parentActivityId;
|
|
437
|
+
|
|
438
|
+
return webex.internal.conversation
|
|
439
|
+
.share(conversation, share)
|
|
440
|
+
.then((activity) => {
|
|
441
|
+
assert.equal(activity.object.displayName, 'a name');
|
|
442
|
+
const threadShare = webex.internal.conversation.makeShare(conversation);
|
|
443
|
+
|
|
444
|
+
threadShare.add(sampleImageSmallOnePng);
|
|
445
|
+
threadShare.object = {
|
|
446
|
+
displayName: 'a thread share name',
|
|
447
|
+
};
|
|
448
|
+
threadShare.activityType = 'reply';
|
|
449
|
+
threadShare.parentActivityId = activity.id;
|
|
450
|
+
parentActivityId = activity.id;
|
|
451
|
+
|
|
452
|
+
return webex.internal.conversation.share(conversation, threadShare);
|
|
453
|
+
})
|
|
454
|
+
.then((activity) => {
|
|
455
|
+
const {id, type} = activity.parent;
|
|
456
|
+
|
|
457
|
+
assert.equal(id, parentActivityId);
|
|
458
|
+
assert.equal(type, 'reply');
|
|
459
|
+
|
|
460
|
+
return webex.internal.conversation.download(activity.object.files.items[0]);
|
|
461
|
+
})
|
|
462
|
+
.then(returnFirstArg((f) => assert.equal(f.type, 'image/png')))
|
|
463
|
+
.then((file) => fh.isMatchingFile(file, sampleImageSmallOnePng));
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
describe('#addGif', () => {
|
|
468
|
+
let blob, buffer;
|
|
469
|
+
|
|
470
|
+
// Read file as buffer
|
|
471
|
+
browserOnly(before)(() =>
|
|
472
|
+
fh.fetch(sampleGif).then((file) => {
|
|
473
|
+
blob = file;
|
|
474
|
+
|
|
475
|
+
return new Promise((resolve) => {
|
|
476
|
+
/* global FileReader */
|
|
477
|
+
const fileReader = new FileReader();
|
|
478
|
+
|
|
479
|
+
fileReader.onload = function () {
|
|
480
|
+
buffer = this.result;
|
|
481
|
+
resolve();
|
|
482
|
+
};
|
|
483
|
+
fileReader.readAsArrayBuffer(blob);
|
|
484
|
+
});
|
|
485
|
+
})
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
browserOnly(it)(
|
|
489
|
+
'if the giphy does not exist, then we check it gets added to this.uploads',
|
|
490
|
+
(done) => {
|
|
491
|
+
// eslint-disable-next-line no-undef
|
|
492
|
+
const file = new File([buffer], blob.name, {type: 'image/gif'});
|
|
493
|
+
|
|
494
|
+
const originalGiphyURL = 'https://media1.giphy.com/media/nXxOjZrbnbRxS/giphy.gif';
|
|
495
|
+
const originalGiphyStillURL = 'https://media1.giphy.com/media/nXxOjZrbnbRxS/giphy_s.gif';
|
|
496
|
+
const url = 'https://giphy.com';
|
|
497
|
+
|
|
498
|
+
// simulate in web client where
|
|
499
|
+
Object.defineProperty(file, 'url', {value: originalGiphyURL});
|
|
500
|
+
// define thumbnail
|
|
501
|
+
Object.defineProperty(file, 'image', {
|
|
502
|
+
value: {
|
|
503
|
+
height: file.width,
|
|
504
|
+
width: file.height,
|
|
505
|
+
url: originalGiphyStillURL,
|
|
506
|
+
},
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
const share = webex.internal.conversation.makeShare(conversation);
|
|
510
|
+
|
|
511
|
+
// Check that initially there were no uploads
|
|
512
|
+
assert.isTrue(share.uploads.size === 0);
|
|
513
|
+
share.addGif(file).then(() => {
|
|
514
|
+
assert.equal(share.uploads.size, 1);
|
|
515
|
+
assert.equal(share.uploads.get(file).objectType, 'file');
|
|
516
|
+
assert.equal(share.uploads.get(file).displayName, sampleGif);
|
|
517
|
+
assert.equal(share.uploads.get(file).mimeType, 'image/gif');
|
|
518
|
+
assert.equal(share.uploads.get(file).fileSize, 473119);
|
|
519
|
+
assert.equal(share.uploads.get(file).width, 200);
|
|
520
|
+
assert.equal(share.uploads.get(file).height, 270);
|
|
521
|
+
assert.equal(share.uploads.get(file).url, url);
|
|
522
|
+
assert.exists(share.uploads.get(file).scr);
|
|
523
|
+
assert.equal(share.uploads.get(file).scr.loc, originalGiphyURL);
|
|
524
|
+
|
|
525
|
+
assert.exists(share.uploads.get(file).image);
|
|
526
|
+
assert.equal(share.uploads.get(file).image.width, 200);
|
|
527
|
+
assert.equal(share.uploads.get(file).image.height, 270);
|
|
528
|
+
assert.equal(share.uploads.get(file).image.url, url);
|
|
529
|
+
assert.exists(share.uploads.get(file).image.scr);
|
|
530
|
+
assert.equal(share.uploads.get(file).image.scr.loc, originalGiphyStillURL);
|
|
531
|
+
});
|
|
532
|
+
done();
|
|
533
|
+
}
|
|
534
|
+
);
|
|
535
|
+
});
|
|
536
|
+
});
|
|
537
|
+
});
|