@webex/internal-plugin-board 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 +42 -42
- package/babel.config.js +3 -3
- package/dist/board.js +161 -161
- package/dist/board.js.map +1 -1
- package/dist/config.js +21 -21
- package/dist/config.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/realtime-channel-collection.js +4 -4
- package/dist/realtime-channel-collection.js.map +1 -1
- package/dist/realtime-channel.js +4 -4
- package/dist/realtime-channel.js.map +1 -1
- package/dist/realtime.js +48 -48
- package/dist/realtime.js.map +1 -1
- package/jest.config.js +3 -3
- package/package.json +20 -19
- package/process +1 -1
- package/src/board.js +764 -764
- package/src/config.js +44 -44
- package/src/index.js +105 -105
- package/src/realtime-channel-collection.js +18 -18
- package/src/realtime-channel.js +40 -40
- package/src/realtime.js +252 -252
- package/test/integration/spec/board.js +717 -717
- package/test/integration/spec/realtime.js +194 -194
- package/test/integration/spec/sharing-mercury.js +285 -285
- package/test/unit/spec/board.js +635 -635
- package/test/unit/spec/encryption.js +255 -255
- package/test/unit/spec/realtime.js +473 -473
|
@@ -1,717 +1,717 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import '@webex/internal-plugin-board';
|
|
6
|
-
|
|
7
|
-
import {assert} from '@webex/test-helper-chai';
|
|
8
|
-
import WebexCore from '@webex/webex-core';
|
|
9
|
-
import testUsers from '@webex/test-helper-test-users';
|
|
10
|
-
import fh from '@webex/test-helper-file';
|
|
11
|
-
import {find, map} from 'lodash';
|
|
12
|
-
import uuid from 'uuid';
|
|
13
|
-
|
|
14
|
-
function generateTonsOfContents(numOfContents) {
|
|
15
|
-
return new Promise((resolve) => {
|
|
16
|
-
const contents = [];
|
|
17
|
-
|
|
18
|
-
for (let i = 0; i < numOfContents; i += 1) {
|
|
19
|
-
contents.push({
|
|
20
|
-
type: 'curve',
|
|
21
|
-
payload: JSON.stringify({id: i, type: 'curve'}),
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
resolve(contents);
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
describe('plugin-board', () => {
|
|
29
|
-
describe('service', () => {
|
|
30
|
-
let board, conversation, fixture, participants;
|
|
31
|
-
|
|
32
|
-
before('create users', () =>
|
|
33
|
-
testUsers.create({count: 3}).then((users) => {
|
|
34
|
-
participants = users;
|
|
35
|
-
|
|
36
|
-
return Promise.all(
|
|
37
|
-
map(participants, (participant) => {
|
|
38
|
-
participant.webex = new WebexCore({
|
|
39
|
-
credentials: {
|
|
40
|
-
authorization: participant.token,
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
return participant.webex.internal.device
|
|
45
|
-
.register()
|
|
46
|
-
.then(() =>
|
|
47
|
-
participant.webex.internal.feature.setFeature('developer', 'files-acl-write', true)
|
|
48
|
-
);
|
|
49
|
-
})
|
|
50
|
-
);
|
|
51
|
-
})
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
before('create conversation', () =>
|
|
55
|
-
participants[0].webex.internal.conversation
|
|
56
|
-
.create({
|
|
57
|
-
displayName: 'Test Board Conversation',
|
|
58
|
-
participants,
|
|
59
|
-
})
|
|
60
|
-
.then((c) => {
|
|
61
|
-
conversation = c;
|
|
62
|
-
|
|
63
|
-
return conversation;
|
|
64
|
-
})
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
before('create channel (board)', () =>
|
|
68
|
-
participants[0].webex.internal.board.createChannel(conversation).then((channel) => {
|
|
69
|
-
board = channel;
|
|
70
|
-
|
|
71
|
-
return channel;
|
|
72
|
-
})
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
before('load fixture image', () =>
|
|
76
|
-
fh.fetch('sample-image-small-one.png').then((fetchedFixture) => {
|
|
77
|
-
fixture = fetchedFixture;
|
|
78
|
-
|
|
79
|
-
return fetchedFixture;
|
|
80
|
-
})
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
after('disconnect mercury', () =>
|
|
84
|
-
Promise.all(
|
|
85
|
-
map(participants, (participant) => participant.webex.internal.mercury.disconnect())
|
|
86
|
-
)
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
describe('#getChannel', () => {
|
|
90
|
-
it('gets the channel metadata', () =>
|
|
91
|
-
participants[0].webex.internal.board.getChannel(board).then((channel) => {
|
|
92
|
-
assert.property(channel, 'kmsResourceUrl');
|
|
93
|
-
assert.property(channel, 'aclUrl');
|
|
94
|
-
|
|
95
|
-
assert.equal(channel.channelUrl, board.channelUrl);
|
|
96
|
-
assert.equal(channel.aclUrlLink, conversation.aclUrl);
|
|
97
|
-
assert.notEqual(channel.kmsResourceUrl, conversation.kmsResourceObjectUrl);
|
|
98
|
-
assert.notEqual(channel.aclUrl, conversation.aclUrl);
|
|
99
|
-
assert.notEqual(
|
|
100
|
-
channel.defaultEncryptionKeyUrl,
|
|
101
|
-
conversation.defaultActivityEncryptionKeyUrl
|
|
102
|
-
);
|
|
103
|
-
}));
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
describe('#_uploadImage()', () => {
|
|
107
|
-
after(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
108
|
-
|
|
109
|
-
it('uploads image to webex files', () =>
|
|
110
|
-
participants[0].webex.internal.board
|
|
111
|
-
._uploadImage(board, fixture)
|
|
112
|
-
.then((scr) => participants[1].webex.internal.encryption.download(scr))
|
|
113
|
-
.then((downloadedFile) =>
|
|
114
|
-
fh
|
|
115
|
-
.isMatchingFile(downloadedFile, fixture)
|
|
116
|
-
.then((result) => assert.deepEqual(result, true))
|
|
117
|
-
));
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
describe('#setSnapshotImage()', () => {
|
|
121
|
-
after(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
122
|
-
|
|
123
|
-
it('uploads image to webex files and adds to channel', () => {
|
|
124
|
-
let imageRes;
|
|
125
|
-
|
|
126
|
-
return participants[0].webex.internal.board
|
|
127
|
-
.setSnapshotImage(board, fixture)
|
|
128
|
-
.then((res) => {
|
|
129
|
-
imageRes = res.image;
|
|
130
|
-
assert.isDefined(res.image, 'image field is included');
|
|
131
|
-
assert.equal(res.image.encryptionKeyUrl, board.defaultEncryptionKeyUrl);
|
|
132
|
-
assert.isAbove(res.image.scr.length, 0, 'scr string exists');
|
|
133
|
-
|
|
134
|
-
return participants[1].webex.internal.board.getChannel(board);
|
|
135
|
-
})
|
|
136
|
-
.then((res) => {
|
|
137
|
-
assert.deepEqual(imageRes, res.image);
|
|
138
|
-
|
|
139
|
-
// ensure others can download the image
|
|
140
|
-
return participants[2].webex.internal.encryption.decryptScr(
|
|
141
|
-
board.defaultEncryptionKeyUrl,
|
|
142
|
-
res.image.scr
|
|
143
|
-
);
|
|
144
|
-
})
|
|
145
|
-
.then((decryptedScr) => participants[2].webex.internal.encryption.download(decryptedScr))
|
|
146
|
-
.then((file) =>
|
|
147
|
-
fh.isMatchingFile(file, fixture).then((result) => assert.deepEqual(result, true))
|
|
148
|
-
);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
describe('#ping()', () => {
|
|
153
|
-
it('pings board service', () => participants[0].webex.internal.board.ping());
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
describe('#addImage()', () => {
|
|
157
|
-
let testContent, testScr;
|
|
158
|
-
|
|
159
|
-
after(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
160
|
-
|
|
161
|
-
it('uploads image to webex files', () =>
|
|
162
|
-
participants[0].webex.internal.board
|
|
163
|
-
.addImage(board, fixture, {displayName: fixture.name})
|
|
164
|
-
.then((fileContent) => {
|
|
165
|
-
testContent = fileContent[0].items[0];
|
|
166
|
-
assert.equal(testContent.type, 'FILE', 'content type should be image');
|
|
167
|
-
assert.property(testContent, 'contentUrl', 'content should contain contentId property');
|
|
168
|
-
assert.property(
|
|
169
|
-
testContent,
|
|
170
|
-
'channelUrl',
|
|
171
|
-
'content should contain contentUrl property'
|
|
172
|
-
);
|
|
173
|
-
assert.property(testContent, 'metadata', 'content should contain metadata property');
|
|
174
|
-
assert.property(testContent, 'file', 'content should contain file property');
|
|
175
|
-
assert.property(testContent.file, 'scr', 'content file should contain scr property');
|
|
176
|
-
}));
|
|
177
|
-
|
|
178
|
-
it('adds to presistence', () =>
|
|
179
|
-
participants[0].webex.internal.board.getContents(board).then((allContents) => {
|
|
180
|
-
const imageContent = find(allContents.items, {contentId: testContent.contentId});
|
|
181
|
-
|
|
182
|
-
assert.isDefined(imageContent);
|
|
183
|
-
assert.property(imageContent, 'file');
|
|
184
|
-
assert.property(imageContent.file, 'scr');
|
|
185
|
-
assert.equal(imageContent.metadata.displayName, 'sample-image-small-one.png');
|
|
186
|
-
testScr = imageContent.file.scr;
|
|
187
|
-
|
|
188
|
-
return imageContent.file.scr;
|
|
189
|
-
}));
|
|
190
|
-
|
|
191
|
-
it('matches file file downloaded', () =>
|
|
192
|
-
participants[0].webex.internal.encryption
|
|
193
|
-
.download(testScr)
|
|
194
|
-
.then((downloadedFile) =>
|
|
195
|
-
fh
|
|
196
|
-
.isMatchingFile(downloadedFile, fixture)
|
|
197
|
-
.then((result) => assert.deepEqual(result, true))
|
|
198
|
-
));
|
|
199
|
-
|
|
200
|
-
it('allows others to download image', () =>
|
|
201
|
-
participants[2].webex.internal.encryption
|
|
202
|
-
.download(testScr)
|
|
203
|
-
.then((downloadedFile) =>
|
|
204
|
-
fh
|
|
205
|
-
.isMatchingFile(downloadedFile, fixture)
|
|
206
|
-
.then((result) => assert.deepEqual(result, true))
|
|
207
|
-
));
|
|
208
|
-
|
|
209
|
-
describe('when image content has no metadata', () => {
|
|
210
|
-
before(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
211
|
-
|
|
212
|
-
it('decrypts no meta', () => {
|
|
213
|
-
let testContent, testScr;
|
|
214
|
-
|
|
215
|
-
return participants[0].webex.internal.board
|
|
216
|
-
.addImage(board, fixture)
|
|
217
|
-
.then((fileContent) => {
|
|
218
|
-
testContent = fileContent[0].items[0];
|
|
219
|
-
assert.equal(testContent.type, 'FILE', 'content type should be image');
|
|
220
|
-
assert.property(
|
|
221
|
-
testContent,
|
|
222
|
-
'contentUrl',
|
|
223
|
-
'content should contain contentId property'
|
|
224
|
-
);
|
|
225
|
-
assert.property(
|
|
226
|
-
testContent,
|
|
227
|
-
'channelUrl',
|
|
228
|
-
'content should contain contentUrl property'
|
|
229
|
-
);
|
|
230
|
-
assert.property(testContent, 'file', 'content should contain file property');
|
|
231
|
-
assert.property(testContent.file, 'scr', 'content file should contain scr property');
|
|
232
|
-
assert.deepEqual(testContent.metadata, {});
|
|
233
|
-
|
|
234
|
-
return participants[0].webex.internal.board.getContents(board);
|
|
235
|
-
})
|
|
236
|
-
.then((allContents) => {
|
|
237
|
-
const imageContent = find(allContents.items, {contentId: testContent.contentId});
|
|
238
|
-
|
|
239
|
-
assert.isDefined(imageContent);
|
|
240
|
-
assert.property(imageContent, 'file');
|
|
241
|
-
assert.property(imageContent.file, 'scr');
|
|
242
|
-
testScr = imageContent.file.scr;
|
|
243
|
-
|
|
244
|
-
return imageContent.file.scr;
|
|
245
|
-
})
|
|
246
|
-
.then(() =>
|
|
247
|
-
participants[0].webex.internal.encryption
|
|
248
|
-
.download(testScr)
|
|
249
|
-
.then((downloadedFile) => fh.isMatchingFile(downloadedFile, fixture))
|
|
250
|
-
.then((res) => assert.isTrue(res))
|
|
251
|
-
);
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
describe('#getChannels()', () => {
|
|
257
|
-
it('retrieves a newly created board for a specified conversation within a single page', () =>
|
|
258
|
-
participants[0].webex.internal.board.getChannels(conversation).then((getChannelsResp) => {
|
|
259
|
-
const channelFound = find(getChannelsResp.items, {channelId: board.channelId});
|
|
260
|
-
|
|
261
|
-
assert.isDefined(channelFound);
|
|
262
|
-
assert.notProperty(getChannelsResp.links, 'next');
|
|
263
|
-
}));
|
|
264
|
-
|
|
265
|
-
it('retrieves annotated board', () => {
|
|
266
|
-
let annotatedBoard;
|
|
267
|
-
|
|
268
|
-
return participants[0].webex.internal.board
|
|
269
|
-
.createChannel(conversation, {type: 'annotated'})
|
|
270
|
-
.then((res) => {
|
|
271
|
-
annotatedBoard = res;
|
|
272
|
-
|
|
273
|
-
return participants[0].webex.internal.board.getChannels(conversation, {
|
|
274
|
-
type: 'annotated',
|
|
275
|
-
});
|
|
276
|
-
})
|
|
277
|
-
.then((getChannelsResp) => {
|
|
278
|
-
const channelFound = find(getChannelsResp.items, {channelId: annotatedBoard.channelId});
|
|
279
|
-
|
|
280
|
-
assert.isUndefined(find(getChannelsResp.items, {channelId: board.channelId}));
|
|
281
|
-
assert.isDefined(channelFound);
|
|
282
|
-
assert.notProperty(getChannelsResp.links, 'next');
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
it('retrieves all boards for a specified conversation across multiple pages', () => {
|
|
287
|
-
const numChannelsToAdd = 12;
|
|
288
|
-
const pageLimit = 5;
|
|
289
|
-
const channelsCreated = [];
|
|
290
|
-
let channelsReceived = [];
|
|
291
|
-
let convo;
|
|
292
|
-
|
|
293
|
-
return (
|
|
294
|
-
participants[0].webex.internal.conversation
|
|
295
|
-
.create({
|
|
296
|
-
displayName: `Test Get Channels Conversation ${uuid.v4()}`,
|
|
297
|
-
participants,
|
|
298
|
-
})
|
|
299
|
-
.then((c) => {
|
|
300
|
-
convo = c;
|
|
301
|
-
const promises = [];
|
|
302
|
-
|
|
303
|
-
for (let i = 0; i < numChannelsToAdd; i += 1) {
|
|
304
|
-
promises.push(
|
|
305
|
-
participants[0].webex.internal.board.createChannel(convo).then((channel) => {
|
|
306
|
-
Reflect.deleteProperty(channel, 'kmsMessage');
|
|
307
|
-
channelsCreated.push(channel);
|
|
308
|
-
})
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
return Promise.all(promises);
|
|
313
|
-
})
|
|
314
|
-
// get boards, page 1
|
|
315
|
-
.then(() =>
|
|
316
|
-
participants[0].webex.internal.board.getChannels(convo, {
|
|
317
|
-
channelsLimit: pageLimit,
|
|
318
|
-
})
|
|
319
|
-
)
|
|
320
|
-
// get boards, page 2
|
|
321
|
-
.then((channelPage) => {
|
|
322
|
-
assert.lengthOf(channelPage.items, pageLimit);
|
|
323
|
-
assert.isTrue(channelPage.hasNext());
|
|
324
|
-
channelsReceived = channelsReceived.concat(channelPage.items);
|
|
325
|
-
|
|
326
|
-
return channelPage.next();
|
|
327
|
-
})
|
|
328
|
-
// get boards, page 3
|
|
329
|
-
.then((channelPage) => {
|
|
330
|
-
assert.lengthOf(channelPage.items, pageLimit);
|
|
331
|
-
assert.isTrue(channelPage.hasNext());
|
|
332
|
-
channelsReceived = channelsReceived.concat(channelPage.items);
|
|
333
|
-
|
|
334
|
-
return channelPage.next();
|
|
335
|
-
})
|
|
336
|
-
.then((channelPage) => {
|
|
337
|
-
assert.lengthOf(channelPage, 2);
|
|
338
|
-
assert.isFalse(channelPage.hasNext());
|
|
339
|
-
channelsReceived = channelsReceived.concat(channelPage.items);
|
|
340
|
-
|
|
341
|
-
channelsCreated.sort((a, b) => a.createdTime - b.createdTime);
|
|
342
|
-
channelsReceived.sort((a, b) => a.createdTime - b.createdTime);
|
|
343
|
-
|
|
344
|
-
if (channelsCreated.length === channelsReceived.length) {
|
|
345
|
-
channelsReceived.forEach((channel, i) => {
|
|
346
|
-
delete channel.format;
|
|
347
|
-
assert.deepEqual(channel, channelsCreated[i]);
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
})
|
|
351
|
-
);
|
|
352
|
-
});
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
describe('#getContents()', () => {
|
|
356
|
-
afterEach(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
357
|
-
|
|
358
|
-
it('adds and gets contents from the specified board', () => {
|
|
359
|
-
const contents = [{type: 'curve'}];
|
|
360
|
-
const data = [
|
|
361
|
-
{
|
|
362
|
-
type: contents[0].type,
|
|
363
|
-
payload: JSON.stringify(contents[0]),
|
|
364
|
-
},
|
|
365
|
-
];
|
|
366
|
-
|
|
367
|
-
return participants[0].webex.internal.board
|
|
368
|
-
.deleteAllContent(board)
|
|
369
|
-
.then(() => participants[0].webex.internal.board.addContent(board, data))
|
|
370
|
-
.then(() => participants[0].webex.internal.board.getContents(board))
|
|
371
|
-
.then((contentPage) => {
|
|
372
|
-
assert.equal(contentPage.length, data.length);
|
|
373
|
-
assert.equal(contentPage.items[0].payload, data[0].payload);
|
|
374
|
-
assert.equal(contentPage.items[0].type, data[0].type);
|
|
375
|
-
})
|
|
376
|
-
.then(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
it('allows other people to read contents', () => {
|
|
380
|
-
const contents = [{type: 'curve', points: [1, 2, 3, 4]}];
|
|
381
|
-
const data = [
|
|
382
|
-
{
|
|
383
|
-
type: contents[0].type,
|
|
384
|
-
payload: JSON.stringify(contents[0]),
|
|
385
|
-
},
|
|
386
|
-
];
|
|
387
|
-
|
|
388
|
-
return participants[0].webex.internal.board
|
|
389
|
-
.addContent(board, data)
|
|
390
|
-
.then(() => participants[1].webex.internal.board.getContents(board))
|
|
391
|
-
.then((contentPage) => {
|
|
392
|
-
assert.equal(contentPage.length, data.length);
|
|
393
|
-
assert.equal(contentPage.items[0].payload, data[0].payload);
|
|
394
|
-
|
|
395
|
-
return participants[2].webex.internal.board.getContents(board);
|
|
396
|
-
})
|
|
397
|
-
.then((contentPage) => {
|
|
398
|
-
assert.equal(contentPage.length, data.length);
|
|
399
|
-
assert.equal(contentPage.items[0].payload, data[0].payload);
|
|
400
|
-
});
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
it('allows other people to write contents', () => {
|
|
404
|
-
const contents = [{type: 'curve', points: [1, 2, 3, 4]}];
|
|
405
|
-
const data = [
|
|
406
|
-
{
|
|
407
|
-
type: contents[0].type,
|
|
408
|
-
payload: JSON.stringify(contents[0]),
|
|
409
|
-
},
|
|
410
|
-
];
|
|
411
|
-
|
|
412
|
-
return participants[2].webex.internal.board
|
|
413
|
-
.addContent(board, data)
|
|
414
|
-
.then(() => participants[1].webex.internal.board.getContents(board))
|
|
415
|
-
.then((contentPage) => {
|
|
416
|
-
assert.equal(contentPage.length, data.length);
|
|
417
|
-
assert.equal(contentPage.items[0].payload, data[0].payload);
|
|
418
|
-
});
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
describe('handles large data sets', () => {
|
|
422
|
-
const numberOfContents = 30;
|
|
423
|
-
let tonsOfContents;
|
|
424
|
-
|
|
425
|
-
before('generate contents', () =>
|
|
426
|
-
generateTonsOfContents(numberOfContents).then((res) => {
|
|
427
|
-
tonsOfContents = res;
|
|
428
|
-
})
|
|
429
|
-
);
|
|
430
|
-
|
|
431
|
-
beforeEach('create contents', () =>
|
|
432
|
-
participants[0].webex.internal.board.addContent(board, tonsOfContents)
|
|
433
|
-
);
|
|
434
|
-
|
|
435
|
-
it('using the default page limit', () =>
|
|
436
|
-
participants[0].webex.internal.board.getContents(board).then((res) => {
|
|
437
|
-
assert.lengthOf(res, numberOfContents);
|
|
438
|
-
assert.isFalse(res.hasNext());
|
|
439
|
-
|
|
440
|
-
for (let i = 0; i < res.length; i += 1) {
|
|
441
|
-
assert.equal(res.items[i].payload, tonsOfContents[i].payload, 'payload data matches');
|
|
442
|
-
}
|
|
443
|
-
}));
|
|
444
|
-
|
|
445
|
-
it('using a client defined page limit', () =>
|
|
446
|
-
participants[0].webex.internal.board
|
|
447
|
-
.getContents(board, {contentsLimit: 25})
|
|
448
|
-
.then((res) => {
|
|
449
|
-
assert.lengthOf(res, 25);
|
|
450
|
-
assert.isTrue(res.hasNext());
|
|
451
|
-
|
|
452
|
-
return res.next();
|
|
453
|
-
})
|
|
454
|
-
.then((res) => {
|
|
455
|
-
assert.lengthOf(res, numberOfContents - 25);
|
|
456
|
-
assert.isFalse(res.hasNext());
|
|
457
|
-
}));
|
|
458
|
-
});
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
describe('#deleteAllContent()', () => {
|
|
462
|
-
after(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
463
|
-
|
|
464
|
-
it('delete all contents from the specified board', () => {
|
|
465
|
-
const channel = board;
|
|
466
|
-
const contents = [
|
|
467
|
-
{
|
|
468
|
-
id: uuid.v4(),
|
|
469
|
-
type: 'file',
|
|
470
|
-
},
|
|
471
|
-
{
|
|
472
|
-
id: uuid.v4(),
|
|
473
|
-
type: 'string',
|
|
474
|
-
},
|
|
475
|
-
];
|
|
476
|
-
const data = [
|
|
477
|
-
{
|
|
478
|
-
type: contents[0].type,
|
|
479
|
-
payload: JSON.stringify(contents[0]),
|
|
480
|
-
},
|
|
481
|
-
{
|
|
482
|
-
type: contents[1].type,
|
|
483
|
-
payload: JSON.stringify(contents[1]),
|
|
484
|
-
},
|
|
485
|
-
];
|
|
486
|
-
|
|
487
|
-
return participants[0].webex.internal.board
|
|
488
|
-
.addContent(channel, data)
|
|
489
|
-
.then(() => participants[0].webex.internal.board.deleteAllContent(channel))
|
|
490
|
-
.then(() => participants[0].webex.internal.board.getContents(channel))
|
|
491
|
-
.then((res) => {
|
|
492
|
-
assert.lengthOf(res, 0);
|
|
493
|
-
|
|
494
|
-
return res;
|
|
495
|
-
});
|
|
496
|
-
});
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
describe('#deletePartialContent()', () => {
|
|
500
|
-
after(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
501
|
-
|
|
502
|
-
it('deletes some contents from the specified board', () => {
|
|
503
|
-
const channel = board;
|
|
504
|
-
const data = [
|
|
505
|
-
{
|
|
506
|
-
type: 'STRING',
|
|
507
|
-
payload: JSON.stringify({id: uuid.v4()}),
|
|
508
|
-
},
|
|
509
|
-
{
|
|
510
|
-
type: 'FILE',
|
|
511
|
-
payload: JSON.stringify({id: uuid.v4()}),
|
|
512
|
-
},
|
|
513
|
-
];
|
|
514
|
-
const contentsToKeep = [];
|
|
515
|
-
|
|
516
|
-
return participants[0].webex.internal.board
|
|
517
|
-
.addContent(channel, data)
|
|
518
|
-
.then(([firstPageRes]) => {
|
|
519
|
-
contentsToKeep.push(firstPageRes.items[1]);
|
|
520
|
-
})
|
|
521
|
-
.then(() =>
|
|
522
|
-
participants[0].webex.internal.board.deletePartialContent(channel, contentsToKeep)
|
|
523
|
-
)
|
|
524
|
-
.then(() => participants[0].webex.internal.board.getContents(channel))
|
|
525
|
-
.then((page) => {
|
|
526
|
-
assert.lengthOf(page, 1);
|
|
527
|
-
delete page.items[0].format;
|
|
528
|
-
assert.deepEqual(page.items[0], contentsToKeep[0]);
|
|
529
|
-
|
|
530
|
-
return page;
|
|
531
|
-
});
|
|
532
|
-
});
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
describe('when a user leaves conversation', () => {
|
|
536
|
-
it('does not allow board user to create board', () => {
|
|
537
|
-
let currentConvo;
|
|
538
|
-
|
|
539
|
-
return participants[0].webex.internal.conversation
|
|
540
|
-
.create({
|
|
541
|
-
displayName: 'Test Board Member Leave Conversation',
|
|
542
|
-
participants,
|
|
543
|
-
})
|
|
544
|
-
.then((c) => {
|
|
545
|
-
currentConvo = c;
|
|
546
|
-
|
|
547
|
-
return participants[1].webex.internal.conversation.leave(currentConvo);
|
|
548
|
-
})
|
|
549
|
-
.then(() =>
|
|
550
|
-
assert.isRejected(participants[1].webex.internal.board.createChannel(currentConvo))
|
|
551
|
-
);
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
it('does not allow board creator to access and decrypt contents', () => {
|
|
555
|
-
let currentConvo;
|
|
556
|
-
let currentBoard;
|
|
557
|
-
const encryptedBoardContent = {};
|
|
558
|
-
const data = [
|
|
559
|
-
{
|
|
560
|
-
type: 'curve',
|
|
561
|
-
payload: JSON.stringify({type: 'curve'}),
|
|
562
|
-
},
|
|
563
|
-
];
|
|
564
|
-
|
|
565
|
-
return (
|
|
566
|
-
participants[1].webex.internal.conversation
|
|
567
|
-
.create({
|
|
568
|
-
displayName: 'Test Board Creator Leave Conversation',
|
|
569
|
-
participants,
|
|
570
|
-
})
|
|
571
|
-
.then((c) => {
|
|
572
|
-
currentConvo = c;
|
|
573
|
-
|
|
574
|
-
return participants[1].webex.internal.board.createChannel(currentConvo);
|
|
575
|
-
})
|
|
576
|
-
.then((b) => {
|
|
577
|
-
currentBoard = b;
|
|
578
|
-
|
|
579
|
-
return participants[1].webex.internal.conversation.leave(currentConvo);
|
|
580
|
-
})
|
|
581
|
-
.then(() =>
|
|
582
|
-
participants[0].webex.internal.board.encryptContents(
|
|
583
|
-
currentBoard.defaultEncryptionKeyUrl,
|
|
584
|
-
data
|
|
585
|
-
)
|
|
586
|
-
)
|
|
587
|
-
.then((encryptedData) => {
|
|
588
|
-
encryptedBoardContent.items = encryptedData;
|
|
589
|
-
|
|
590
|
-
return assert.isRejected(
|
|
591
|
-
participants[1].webex.internal.board.getContents(currentBoard)
|
|
592
|
-
);
|
|
593
|
-
})
|
|
594
|
-
// ensure keys aren't cached
|
|
595
|
-
.then(() => participants[1].webex.unboundedStorage.clear())
|
|
596
|
-
.then(() =>
|
|
597
|
-
assert.isRejected(
|
|
598
|
-
participants[1].webex.internal.board.decryptContents(encryptedBoardContent)
|
|
599
|
-
)
|
|
600
|
-
)
|
|
601
|
-
);
|
|
602
|
-
});
|
|
603
|
-
});
|
|
604
|
-
|
|
605
|
-
describe('#deleteChannel()', () => {
|
|
606
|
-
it('deletes channel', () => {
|
|
607
|
-
let newChannel;
|
|
608
|
-
|
|
609
|
-
return participants[1].webex.internal.board
|
|
610
|
-
.createChannel(conversation)
|
|
611
|
-
.then((res) => {
|
|
612
|
-
newChannel = res;
|
|
613
|
-
|
|
614
|
-
return participants[1].webex.internal.board.deleteChannel(conversation, newChannel);
|
|
615
|
-
})
|
|
616
|
-
.then(() =>
|
|
617
|
-
assert.isRejected(participants[1].webex.internal.board.getChannel(newChannel))
|
|
618
|
-
);
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
describe('when preventDeleteActiveChannel is enabled', () => {
|
|
622
|
-
it('does not delete when a channel is being used', () => {
|
|
623
|
-
let activeChannel;
|
|
624
|
-
|
|
625
|
-
return participants[1].webex.internal.board
|
|
626
|
-
.createChannel(conversation)
|
|
627
|
-
.then((res) => {
|
|
628
|
-
activeChannel = res;
|
|
629
|
-
const data = [
|
|
630
|
-
{
|
|
631
|
-
type: 'curve',
|
|
632
|
-
payload: JSON.stringify({type: 'curve'}),
|
|
633
|
-
},
|
|
634
|
-
];
|
|
635
|
-
|
|
636
|
-
// this will mark the channel as being used
|
|
637
|
-
return participants[0].webex.internal.board.addContent(activeChannel, data);
|
|
638
|
-
})
|
|
639
|
-
.then(() =>
|
|
640
|
-
assert.isRejected(
|
|
641
|
-
participants[1].webex.internal.board.deleteChannel(conversation, activeChannel, {
|
|
642
|
-
preventDeleteActiveChannel: true,
|
|
643
|
-
})
|
|
644
|
-
)
|
|
645
|
-
)
|
|
646
|
-
.then(() => participants[1].webex.internal.board.getChannel(activeChannel));
|
|
647
|
-
});
|
|
648
|
-
|
|
649
|
-
it('deletes inactive channel', () => {
|
|
650
|
-
let inActiveChannel;
|
|
651
|
-
|
|
652
|
-
return participants[1].webex.internal.board
|
|
653
|
-
.createChannel(conversation)
|
|
654
|
-
.then((res) => {
|
|
655
|
-
inActiveChannel = res;
|
|
656
|
-
|
|
657
|
-
return participants[1].webex.internal.board.deleteChannel(
|
|
658
|
-
conversation,
|
|
659
|
-
inActiveChannel,
|
|
660
|
-
{preventDeleteActiveChannel: true}
|
|
661
|
-
);
|
|
662
|
-
})
|
|
663
|
-
.then(() =>
|
|
664
|
-
assert.isRejected(participants[1].webex.internal.board.getChannel(inActiveChannel))
|
|
665
|
-
);
|
|
666
|
-
});
|
|
667
|
-
});
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
describe('#lockChannelForDeletion()', () => {
|
|
671
|
-
it('locks a channel for deletion which rejects any incoming activities', () => {
|
|
672
|
-
let newChannel;
|
|
673
|
-
|
|
674
|
-
return participants[1].webex.internal.board
|
|
675
|
-
.createChannel(conversation)
|
|
676
|
-
.then((res) => {
|
|
677
|
-
newChannel = res;
|
|
678
|
-
|
|
679
|
-
return participants[1].webex.internal.board.lockChannelForDeletion(newChannel);
|
|
680
|
-
})
|
|
681
|
-
.then(() => {
|
|
682
|
-
const data = [
|
|
683
|
-
{
|
|
684
|
-
type: 'curve',
|
|
685
|
-
payload: JSON.stringify({type: 'curve'}),
|
|
686
|
-
},
|
|
687
|
-
];
|
|
688
|
-
|
|
689
|
-
return assert.isRejected(
|
|
690
|
-
participants[0].webex.internal.board.addContent(newChannel, data)
|
|
691
|
-
);
|
|
692
|
-
});
|
|
693
|
-
});
|
|
694
|
-
});
|
|
695
|
-
|
|
696
|
-
describe('#keepActive()', () => {
|
|
697
|
-
it('keeps a channel status as active', () => {
|
|
698
|
-
let newChannel;
|
|
699
|
-
|
|
700
|
-
return participants[1].webex.internal.board
|
|
701
|
-
.createChannel(conversation)
|
|
702
|
-
.then((res) => {
|
|
703
|
-
newChannel = res;
|
|
704
|
-
|
|
705
|
-
return participants[1].webex.internal.board.keepActive(newChannel);
|
|
706
|
-
})
|
|
707
|
-
.then(() =>
|
|
708
|
-
assert.isRejected(
|
|
709
|
-
participants[0].webex.internal.board.deleteChannel(conversation, newChannel, {
|
|
710
|
-
preventDeleteActiveChannel: true,
|
|
711
|
-
})
|
|
712
|
-
)
|
|
713
|
-
);
|
|
714
|
-
});
|
|
715
|
-
});
|
|
716
|
-
});
|
|
717
|
-
});
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import '@webex/internal-plugin-board';
|
|
6
|
+
|
|
7
|
+
import {assert} from '@webex/test-helper-chai';
|
|
8
|
+
import WebexCore from '@webex/webex-core';
|
|
9
|
+
import testUsers from '@webex/test-helper-test-users';
|
|
10
|
+
import fh from '@webex/test-helper-file';
|
|
11
|
+
import {find, map} from 'lodash';
|
|
12
|
+
import uuid from 'uuid';
|
|
13
|
+
|
|
14
|
+
function generateTonsOfContents(numOfContents) {
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
const contents = [];
|
|
17
|
+
|
|
18
|
+
for (let i = 0; i < numOfContents; i += 1) {
|
|
19
|
+
contents.push({
|
|
20
|
+
type: 'curve',
|
|
21
|
+
payload: JSON.stringify({id: i, type: 'curve'}),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
resolve(contents);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe('plugin-board', () => {
|
|
29
|
+
describe('service', () => {
|
|
30
|
+
let board, conversation, fixture, participants;
|
|
31
|
+
|
|
32
|
+
before('create users', () =>
|
|
33
|
+
testUsers.create({count: 3}).then((users) => {
|
|
34
|
+
participants = users;
|
|
35
|
+
|
|
36
|
+
return Promise.all(
|
|
37
|
+
map(participants, (participant) => {
|
|
38
|
+
participant.webex = new WebexCore({
|
|
39
|
+
credentials: {
|
|
40
|
+
authorization: participant.token,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return participant.webex.internal.device
|
|
45
|
+
.register()
|
|
46
|
+
.then(() =>
|
|
47
|
+
participant.webex.internal.feature.setFeature('developer', 'files-acl-write', true)
|
|
48
|
+
);
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
})
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
before('create conversation', () =>
|
|
55
|
+
participants[0].webex.internal.conversation
|
|
56
|
+
.create({
|
|
57
|
+
displayName: 'Test Board Conversation',
|
|
58
|
+
participants,
|
|
59
|
+
})
|
|
60
|
+
.then((c) => {
|
|
61
|
+
conversation = c;
|
|
62
|
+
|
|
63
|
+
return conversation;
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
before('create channel (board)', () =>
|
|
68
|
+
participants[0].webex.internal.board.createChannel(conversation).then((channel) => {
|
|
69
|
+
board = channel;
|
|
70
|
+
|
|
71
|
+
return channel;
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
before('load fixture image', () =>
|
|
76
|
+
fh.fetch('sample-image-small-one.png').then((fetchedFixture) => {
|
|
77
|
+
fixture = fetchedFixture;
|
|
78
|
+
|
|
79
|
+
return fetchedFixture;
|
|
80
|
+
})
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
after('disconnect mercury', () =>
|
|
84
|
+
Promise.all(
|
|
85
|
+
map(participants, (participant) => participant.webex.internal.mercury.disconnect())
|
|
86
|
+
)
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
describe('#getChannel', () => {
|
|
90
|
+
it('gets the channel metadata', () =>
|
|
91
|
+
participants[0].webex.internal.board.getChannel(board).then((channel) => {
|
|
92
|
+
assert.property(channel, 'kmsResourceUrl');
|
|
93
|
+
assert.property(channel, 'aclUrl');
|
|
94
|
+
|
|
95
|
+
assert.equal(channel.channelUrl, board.channelUrl);
|
|
96
|
+
assert.equal(channel.aclUrlLink, conversation.aclUrl);
|
|
97
|
+
assert.notEqual(channel.kmsResourceUrl, conversation.kmsResourceObjectUrl);
|
|
98
|
+
assert.notEqual(channel.aclUrl, conversation.aclUrl);
|
|
99
|
+
assert.notEqual(
|
|
100
|
+
channel.defaultEncryptionKeyUrl,
|
|
101
|
+
conversation.defaultActivityEncryptionKeyUrl
|
|
102
|
+
);
|
|
103
|
+
}));
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('#_uploadImage()', () => {
|
|
107
|
+
after(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
108
|
+
|
|
109
|
+
it('uploads image to webex files', () =>
|
|
110
|
+
participants[0].webex.internal.board
|
|
111
|
+
._uploadImage(board, fixture)
|
|
112
|
+
.then((scr) => participants[1].webex.internal.encryption.download(scr))
|
|
113
|
+
.then((downloadedFile) =>
|
|
114
|
+
fh
|
|
115
|
+
.isMatchingFile(downloadedFile, fixture)
|
|
116
|
+
.then((result) => assert.deepEqual(result, true))
|
|
117
|
+
));
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('#setSnapshotImage()', () => {
|
|
121
|
+
after(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
122
|
+
|
|
123
|
+
it('uploads image to webex files and adds to channel', () => {
|
|
124
|
+
let imageRes;
|
|
125
|
+
|
|
126
|
+
return participants[0].webex.internal.board
|
|
127
|
+
.setSnapshotImage(board, fixture)
|
|
128
|
+
.then((res) => {
|
|
129
|
+
imageRes = res.image;
|
|
130
|
+
assert.isDefined(res.image, 'image field is included');
|
|
131
|
+
assert.equal(res.image.encryptionKeyUrl, board.defaultEncryptionKeyUrl);
|
|
132
|
+
assert.isAbove(res.image.scr.length, 0, 'scr string exists');
|
|
133
|
+
|
|
134
|
+
return participants[1].webex.internal.board.getChannel(board);
|
|
135
|
+
})
|
|
136
|
+
.then((res) => {
|
|
137
|
+
assert.deepEqual(imageRes, res.image);
|
|
138
|
+
|
|
139
|
+
// ensure others can download the image
|
|
140
|
+
return participants[2].webex.internal.encryption.decryptScr(
|
|
141
|
+
board.defaultEncryptionKeyUrl,
|
|
142
|
+
res.image.scr
|
|
143
|
+
);
|
|
144
|
+
})
|
|
145
|
+
.then((decryptedScr) => participants[2].webex.internal.encryption.download(decryptedScr))
|
|
146
|
+
.then((file) =>
|
|
147
|
+
fh.isMatchingFile(file, fixture).then((result) => assert.deepEqual(result, true))
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('#ping()', () => {
|
|
153
|
+
it('pings board service', () => participants[0].webex.internal.board.ping());
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('#addImage()', () => {
|
|
157
|
+
let testContent, testScr;
|
|
158
|
+
|
|
159
|
+
after(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
160
|
+
|
|
161
|
+
it('uploads image to webex files', () =>
|
|
162
|
+
participants[0].webex.internal.board
|
|
163
|
+
.addImage(board, fixture, {displayName: fixture.name})
|
|
164
|
+
.then((fileContent) => {
|
|
165
|
+
testContent = fileContent[0].items[0];
|
|
166
|
+
assert.equal(testContent.type, 'FILE', 'content type should be image');
|
|
167
|
+
assert.property(testContent, 'contentUrl', 'content should contain contentId property');
|
|
168
|
+
assert.property(
|
|
169
|
+
testContent,
|
|
170
|
+
'channelUrl',
|
|
171
|
+
'content should contain contentUrl property'
|
|
172
|
+
);
|
|
173
|
+
assert.property(testContent, 'metadata', 'content should contain metadata property');
|
|
174
|
+
assert.property(testContent, 'file', 'content should contain file property');
|
|
175
|
+
assert.property(testContent.file, 'scr', 'content file should contain scr property');
|
|
176
|
+
}));
|
|
177
|
+
|
|
178
|
+
it('adds to presistence', () =>
|
|
179
|
+
participants[0].webex.internal.board.getContents(board).then((allContents) => {
|
|
180
|
+
const imageContent = find(allContents.items, {contentId: testContent.contentId});
|
|
181
|
+
|
|
182
|
+
assert.isDefined(imageContent);
|
|
183
|
+
assert.property(imageContent, 'file');
|
|
184
|
+
assert.property(imageContent.file, 'scr');
|
|
185
|
+
assert.equal(imageContent.metadata.displayName, 'sample-image-small-one.png');
|
|
186
|
+
testScr = imageContent.file.scr;
|
|
187
|
+
|
|
188
|
+
return imageContent.file.scr;
|
|
189
|
+
}));
|
|
190
|
+
|
|
191
|
+
it('matches file file downloaded', () =>
|
|
192
|
+
participants[0].webex.internal.encryption
|
|
193
|
+
.download(testScr)
|
|
194
|
+
.then((downloadedFile) =>
|
|
195
|
+
fh
|
|
196
|
+
.isMatchingFile(downloadedFile, fixture)
|
|
197
|
+
.then((result) => assert.deepEqual(result, true))
|
|
198
|
+
));
|
|
199
|
+
|
|
200
|
+
it('allows others to download image', () =>
|
|
201
|
+
participants[2].webex.internal.encryption
|
|
202
|
+
.download(testScr)
|
|
203
|
+
.then((downloadedFile) =>
|
|
204
|
+
fh
|
|
205
|
+
.isMatchingFile(downloadedFile, fixture)
|
|
206
|
+
.then((result) => assert.deepEqual(result, true))
|
|
207
|
+
));
|
|
208
|
+
|
|
209
|
+
describe('when image content has no metadata', () => {
|
|
210
|
+
before(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
211
|
+
|
|
212
|
+
it('decrypts no meta', () => {
|
|
213
|
+
let testContent, testScr;
|
|
214
|
+
|
|
215
|
+
return participants[0].webex.internal.board
|
|
216
|
+
.addImage(board, fixture)
|
|
217
|
+
.then((fileContent) => {
|
|
218
|
+
testContent = fileContent[0].items[0];
|
|
219
|
+
assert.equal(testContent.type, 'FILE', 'content type should be image');
|
|
220
|
+
assert.property(
|
|
221
|
+
testContent,
|
|
222
|
+
'contentUrl',
|
|
223
|
+
'content should contain contentId property'
|
|
224
|
+
);
|
|
225
|
+
assert.property(
|
|
226
|
+
testContent,
|
|
227
|
+
'channelUrl',
|
|
228
|
+
'content should contain contentUrl property'
|
|
229
|
+
);
|
|
230
|
+
assert.property(testContent, 'file', 'content should contain file property');
|
|
231
|
+
assert.property(testContent.file, 'scr', 'content file should contain scr property');
|
|
232
|
+
assert.deepEqual(testContent.metadata, {});
|
|
233
|
+
|
|
234
|
+
return participants[0].webex.internal.board.getContents(board);
|
|
235
|
+
})
|
|
236
|
+
.then((allContents) => {
|
|
237
|
+
const imageContent = find(allContents.items, {contentId: testContent.contentId});
|
|
238
|
+
|
|
239
|
+
assert.isDefined(imageContent);
|
|
240
|
+
assert.property(imageContent, 'file');
|
|
241
|
+
assert.property(imageContent.file, 'scr');
|
|
242
|
+
testScr = imageContent.file.scr;
|
|
243
|
+
|
|
244
|
+
return imageContent.file.scr;
|
|
245
|
+
})
|
|
246
|
+
.then(() =>
|
|
247
|
+
participants[0].webex.internal.encryption
|
|
248
|
+
.download(testScr)
|
|
249
|
+
.then((downloadedFile) => fh.isMatchingFile(downloadedFile, fixture))
|
|
250
|
+
.then((res) => assert.isTrue(res))
|
|
251
|
+
);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
describe('#getChannels()', () => {
|
|
257
|
+
it('retrieves a newly created board for a specified conversation within a single page', () =>
|
|
258
|
+
participants[0].webex.internal.board.getChannels(conversation).then((getChannelsResp) => {
|
|
259
|
+
const channelFound = find(getChannelsResp.items, {channelId: board.channelId});
|
|
260
|
+
|
|
261
|
+
assert.isDefined(channelFound);
|
|
262
|
+
assert.notProperty(getChannelsResp.links, 'next');
|
|
263
|
+
}));
|
|
264
|
+
|
|
265
|
+
it('retrieves annotated board', () => {
|
|
266
|
+
let annotatedBoard;
|
|
267
|
+
|
|
268
|
+
return participants[0].webex.internal.board
|
|
269
|
+
.createChannel(conversation, {type: 'annotated'})
|
|
270
|
+
.then((res) => {
|
|
271
|
+
annotatedBoard = res;
|
|
272
|
+
|
|
273
|
+
return participants[0].webex.internal.board.getChannels(conversation, {
|
|
274
|
+
type: 'annotated',
|
|
275
|
+
});
|
|
276
|
+
})
|
|
277
|
+
.then((getChannelsResp) => {
|
|
278
|
+
const channelFound = find(getChannelsResp.items, {channelId: annotatedBoard.channelId});
|
|
279
|
+
|
|
280
|
+
assert.isUndefined(find(getChannelsResp.items, {channelId: board.channelId}));
|
|
281
|
+
assert.isDefined(channelFound);
|
|
282
|
+
assert.notProperty(getChannelsResp.links, 'next');
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it('retrieves all boards for a specified conversation across multiple pages', () => {
|
|
287
|
+
const numChannelsToAdd = 12;
|
|
288
|
+
const pageLimit = 5;
|
|
289
|
+
const channelsCreated = [];
|
|
290
|
+
let channelsReceived = [];
|
|
291
|
+
let convo;
|
|
292
|
+
|
|
293
|
+
return (
|
|
294
|
+
participants[0].webex.internal.conversation
|
|
295
|
+
.create({
|
|
296
|
+
displayName: `Test Get Channels Conversation ${uuid.v4()}`,
|
|
297
|
+
participants,
|
|
298
|
+
})
|
|
299
|
+
.then((c) => {
|
|
300
|
+
convo = c;
|
|
301
|
+
const promises = [];
|
|
302
|
+
|
|
303
|
+
for (let i = 0; i < numChannelsToAdd; i += 1) {
|
|
304
|
+
promises.push(
|
|
305
|
+
participants[0].webex.internal.board.createChannel(convo).then((channel) => {
|
|
306
|
+
Reflect.deleteProperty(channel, 'kmsMessage');
|
|
307
|
+
channelsCreated.push(channel);
|
|
308
|
+
})
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return Promise.all(promises);
|
|
313
|
+
})
|
|
314
|
+
// get boards, page 1
|
|
315
|
+
.then(() =>
|
|
316
|
+
participants[0].webex.internal.board.getChannels(convo, {
|
|
317
|
+
channelsLimit: pageLimit,
|
|
318
|
+
})
|
|
319
|
+
)
|
|
320
|
+
// get boards, page 2
|
|
321
|
+
.then((channelPage) => {
|
|
322
|
+
assert.lengthOf(channelPage.items, pageLimit);
|
|
323
|
+
assert.isTrue(channelPage.hasNext());
|
|
324
|
+
channelsReceived = channelsReceived.concat(channelPage.items);
|
|
325
|
+
|
|
326
|
+
return channelPage.next();
|
|
327
|
+
})
|
|
328
|
+
// get boards, page 3
|
|
329
|
+
.then((channelPage) => {
|
|
330
|
+
assert.lengthOf(channelPage.items, pageLimit);
|
|
331
|
+
assert.isTrue(channelPage.hasNext());
|
|
332
|
+
channelsReceived = channelsReceived.concat(channelPage.items);
|
|
333
|
+
|
|
334
|
+
return channelPage.next();
|
|
335
|
+
})
|
|
336
|
+
.then((channelPage) => {
|
|
337
|
+
assert.lengthOf(channelPage, 2);
|
|
338
|
+
assert.isFalse(channelPage.hasNext());
|
|
339
|
+
channelsReceived = channelsReceived.concat(channelPage.items);
|
|
340
|
+
|
|
341
|
+
channelsCreated.sort((a, b) => a.createdTime - b.createdTime);
|
|
342
|
+
channelsReceived.sort((a, b) => a.createdTime - b.createdTime);
|
|
343
|
+
|
|
344
|
+
if (channelsCreated.length === channelsReceived.length) {
|
|
345
|
+
channelsReceived.forEach((channel, i) => {
|
|
346
|
+
delete channel.format;
|
|
347
|
+
assert.deepEqual(channel, channelsCreated[i]);
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
})
|
|
351
|
+
);
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
describe('#getContents()', () => {
|
|
356
|
+
afterEach(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
357
|
+
|
|
358
|
+
it('adds and gets contents from the specified board', () => {
|
|
359
|
+
const contents = [{type: 'curve'}];
|
|
360
|
+
const data = [
|
|
361
|
+
{
|
|
362
|
+
type: contents[0].type,
|
|
363
|
+
payload: JSON.stringify(contents[0]),
|
|
364
|
+
},
|
|
365
|
+
];
|
|
366
|
+
|
|
367
|
+
return participants[0].webex.internal.board
|
|
368
|
+
.deleteAllContent(board)
|
|
369
|
+
.then(() => participants[0].webex.internal.board.addContent(board, data))
|
|
370
|
+
.then(() => participants[0].webex.internal.board.getContents(board))
|
|
371
|
+
.then((contentPage) => {
|
|
372
|
+
assert.equal(contentPage.length, data.length);
|
|
373
|
+
assert.equal(contentPage.items[0].payload, data[0].payload);
|
|
374
|
+
assert.equal(contentPage.items[0].type, data[0].type);
|
|
375
|
+
})
|
|
376
|
+
.then(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('allows other people to read contents', () => {
|
|
380
|
+
const contents = [{type: 'curve', points: [1, 2, 3, 4]}];
|
|
381
|
+
const data = [
|
|
382
|
+
{
|
|
383
|
+
type: contents[0].type,
|
|
384
|
+
payload: JSON.stringify(contents[0]),
|
|
385
|
+
},
|
|
386
|
+
];
|
|
387
|
+
|
|
388
|
+
return participants[0].webex.internal.board
|
|
389
|
+
.addContent(board, data)
|
|
390
|
+
.then(() => participants[1].webex.internal.board.getContents(board))
|
|
391
|
+
.then((contentPage) => {
|
|
392
|
+
assert.equal(contentPage.length, data.length);
|
|
393
|
+
assert.equal(contentPage.items[0].payload, data[0].payload);
|
|
394
|
+
|
|
395
|
+
return participants[2].webex.internal.board.getContents(board);
|
|
396
|
+
})
|
|
397
|
+
.then((contentPage) => {
|
|
398
|
+
assert.equal(contentPage.length, data.length);
|
|
399
|
+
assert.equal(contentPage.items[0].payload, data[0].payload);
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it('allows other people to write contents', () => {
|
|
404
|
+
const contents = [{type: 'curve', points: [1, 2, 3, 4]}];
|
|
405
|
+
const data = [
|
|
406
|
+
{
|
|
407
|
+
type: contents[0].type,
|
|
408
|
+
payload: JSON.stringify(contents[0]),
|
|
409
|
+
},
|
|
410
|
+
];
|
|
411
|
+
|
|
412
|
+
return participants[2].webex.internal.board
|
|
413
|
+
.addContent(board, data)
|
|
414
|
+
.then(() => participants[1].webex.internal.board.getContents(board))
|
|
415
|
+
.then((contentPage) => {
|
|
416
|
+
assert.equal(contentPage.length, data.length);
|
|
417
|
+
assert.equal(contentPage.items[0].payload, data[0].payload);
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
describe('handles large data sets', () => {
|
|
422
|
+
const numberOfContents = 30;
|
|
423
|
+
let tonsOfContents;
|
|
424
|
+
|
|
425
|
+
before('generate contents', () =>
|
|
426
|
+
generateTonsOfContents(numberOfContents).then((res) => {
|
|
427
|
+
tonsOfContents = res;
|
|
428
|
+
})
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
beforeEach('create contents', () =>
|
|
432
|
+
participants[0].webex.internal.board.addContent(board, tonsOfContents)
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
it('using the default page limit', () =>
|
|
436
|
+
participants[0].webex.internal.board.getContents(board).then((res) => {
|
|
437
|
+
assert.lengthOf(res, numberOfContents);
|
|
438
|
+
assert.isFalse(res.hasNext());
|
|
439
|
+
|
|
440
|
+
for (let i = 0; i < res.length; i += 1) {
|
|
441
|
+
assert.equal(res.items[i].payload, tonsOfContents[i].payload, 'payload data matches');
|
|
442
|
+
}
|
|
443
|
+
}));
|
|
444
|
+
|
|
445
|
+
it('using a client defined page limit', () =>
|
|
446
|
+
participants[0].webex.internal.board
|
|
447
|
+
.getContents(board, {contentsLimit: 25})
|
|
448
|
+
.then((res) => {
|
|
449
|
+
assert.lengthOf(res, 25);
|
|
450
|
+
assert.isTrue(res.hasNext());
|
|
451
|
+
|
|
452
|
+
return res.next();
|
|
453
|
+
})
|
|
454
|
+
.then((res) => {
|
|
455
|
+
assert.lengthOf(res, numberOfContents - 25);
|
|
456
|
+
assert.isFalse(res.hasNext());
|
|
457
|
+
}));
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
describe('#deleteAllContent()', () => {
|
|
462
|
+
after(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
463
|
+
|
|
464
|
+
it('delete all contents from the specified board', () => {
|
|
465
|
+
const channel = board;
|
|
466
|
+
const contents = [
|
|
467
|
+
{
|
|
468
|
+
id: uuid.v4(),
|
|
469
|
+
type: 'file',
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
id: uuid.v4(),
|
|
473
|
+
type: 'string',
|
|
474
|
+
},
|
|
475
|
+
];
|
|
476
|
+
const data = [
|
|
477
|
+
{
|
|
478
|
+
type: contents[0].type,
|
|
479
|
+
payload: JSON.stringify(contents[0]),
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
type: contents[1].type,
|
|
483
|
+
payload: JSON.stringify(contents[1]),
|
|
484
|
+
},
|
|
485
|
+
];
|
|
486
|
+
|
|
487
|
+
return participants[0].webex.internal.board
|
|
488
|
+
.addContent(channel, data)
|
|
489
|
+
.then(() => participants[0].webex.internal.board.deleteAllContent(channel))
|
|
490
|
+
.then(() => participants[0].webex.internal.board.getContents(channel))
|
|
491
|
+
.then((res) => {
|
|
492
|
+
assert.lengthOf(res, 0);
|
|
493
|
+
|
|
494
|
+
return res;
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
describe('#deletePartialContent()', () => {
|
|
500
|
+
after(() => participants[0].webex.internal.board.deleteAllContent(board));
|
|
501
|
+
|
|
502
|
+
it('deletes some contents from the specified board', () => {
|
|
503
|
+
const channel = board;
|
|
504
|
+
const data = [
|
|
505
|
+
{
|
|
506
|
+
type: 'STRING',
|
|
507
|
+
payload: JSON.stringify({id: uuid.v4()}),
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
type: 'FILE',
|
|
511
|
+
payload: JSON.stringify({id: uuid.v4()}),
|
|
512
|
+
},
|
|
513
|
+
];
|
|
514
|
+
const contentsToKeep = [];
|
|
515
|
+
|
|
516
|
+
return participants[0].webex.internal.board
|
|
517
|
+
.addContent(channel, data)
|
|
518
|
+
.then(([firstPageRes]) => {
|
|
519
|
+
contentsToKeep.push(firstPageRes.items[1]);
|
|
520
|
+
})
|
|
521
|
+
.then(() =>
|
|
522
|
+
participants[0].webex.internal.board.deletePartialContent(channel, contentsToKeep)
|
|
523
|
+
)
|
|
524
|
+
.then(() => participants[0].webex.internal.board.getContents(channel))
|
|
525
|
+
.then((page) => {
|
|
526
|
+
assert.lengthOf(page, 1);
|
|
527
|
+
delete page.items[0].format;
|
|
528
|
+
assert.deepEqual(page.items[0], contentsToKeep[0]);
|
|
529
|
+
|
|
530
|
+
return page;
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
describe('when a user leaves conversation', () => {
|
|
536
|
+
it('does not allow board user to create board', () => {
|
|
537
|
+
let currentConvo;
|
|
538
|
+
|
|
539
|
+
return participants[0].webex.internal.conversation
|
|
540
|
+
.create({
|
|
541
|
+
displayName: 'Test Board Member Leave Conversation',
|
|
542
|
+
participants,
|
|
543
|
+
})
|
|
544
|
+
.then((c) => {
|
|
545
|
+
currentConvo = c;
|
|
546
|
+
|
|
547
|
+
return participants[1].webex.internal.conversation.leave(currentConvo);
|
|
548
|
+
})
|
|
549
|
+
.then(() =>
|
|
550
|
+
assert.isRejected(participants[1].webex.internal.board.createChannel(currentConvo))
|
|
551
|
+
);
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
it('does not allow board creator to access and decrypt contents', () => {
|
|
555
|
+
let currentConvo;
|
|
556
|
+
let currentBoard;
|
|
557
|
+
const encryptedBoardContent = {};
|
|
558
|
+
const data = [
|
|
559
|
+
{
|
|
560
|
+
type: 'curve',
|
|
561
|
+
payload: JSON.stringify({type: 'curve'}),
|
|
562
|
+
},
|
|
563
|
+
];
|
|
564
|
+
|
|
565
|
+
return (
|
|
566
|
+
participants[1].webex.internal.conversation
|
|
567
|
+
.create({
|
|
568
|
+
displayName: 'Test Board Creator Leave Conversation',
|
|
569
|
+
participants,
|
|
570
|
+
})
|
|
571
|
+
.then((c) => {
|
|
572
|
+
currentConvo = c;
|
|
573
|
+
|
|
574
|
+
return participants[1].webex.internal.board.createChannel(currentConvo);
|
|
575
|
+
})
|
|
576
|
+
.then((b) => {
|
|
577
|
+
currentBoard = b;
|
|
578
|
+
|
|
579
|
+
return participants[1].webex.internal.conversation.leave(currentConvo);
|
|
580
|
+
})
|
|
581
|
+
.then(() =>
|
|
582
|
+
participants[0].webex.internal.board.encryptContents(
|
|
583
|
+
currentBoard.defaultEncryptionKeyUrl,
|
|
584
|
+
data
|
|
585
|
+
)
|
|
586
|
+
)
|
|
587
|
+
.then((encryptedData) => {
|
|
588
|
+
encryptedBoardContent.items = encryptedData;
|
|
589
|
+
|
|
590
|
+
return assert.isRejected(
|
|
591
|
+
participants[1].webex.internal.board.getContents(currentBoard)
|
|
592
|
+
);
|
|
593
|
+
})
|
|
594
|
+
// ensure keys aren't cached
|
|
595
|
+
.then(() => participants[1].webex.unboundedStorage.clear())
|
|
596
|
+
.then(() =>
|
|
597
|
+
assert.isRejected(
|
|
598
|
+
participants[1].webex.internal.board.decryptContents(encryptedBoardContent)
|
|
599
|
+
)
|
|
600
|
+
)
|
|
601
|
+
);
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
describe('#deleteChannel()', () => {
|
|
606
|
+
it('deletes channel', () => {
|
|
607
|
+
let newChannel;
|
|
608
|
+
|
|
609
|
+
return participants[1].webex.internal.board
|
|
610
|
+
.createChannel(conversation)
|
|
611
|
+
.then((res) => {
|
|
612
|
+
newChannel = res;
|
|
613
|
+
|
|
614
|
+
return participants[1].webex.internal.board.deleteChannel(conversation, newChannel);
|
|
615
|
+
})
|
|
616
|
+
.then(() =>
|
|
617
|
+
assert.isRejected(participants[1].webex.internal.board.getChannel(newChannel))
|
|
618
|
+
);
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
describe('when preventDeleteActiveChannel is enabled', () => {
|
|
622
|
+
it('does not delete when a channel is being used', () => {
|
|
623
|
+
let activeChannel;
|
|
624
|
+
|
|
625
|
+
return participants[1].webex.internal.board
|
|
626
|
+
.createChannel(conversation)
|
|
627
|
+
.then((res) => {
|
|
628
|
+
activeChannel = res;
|
|
629
|
+
const data = [
|
|
630
|
+
{
|
|
631
|
+
type: 'curve',
|
|
632
|
+
payload: JSON.stringify({type: 'curve'}),
|
|
633
|
+
},
|
|
634
|
+
];
|
|
635
|
+
|
|
636
|
+
// this will mark the channel as being used
|
|
637
|
+
return participants[0].webex.internal.board.addContent(activeChannel, data);
|
|
638
|
+
})
|
|
639
|
+
.then(() =>
|
|
640
|
+
assert.isRejected(
|
|
641
|
+
participants[1].webex.internal.board.deleteChannel(conversation, activeChannel, {
|
|
642
|
+
preventDeleteActiveChannel: true,
|
|
643
|
+
})
|
|
644
|
+
)
|
|
645
|
+
)
|
|
646
|
+
.then(() => participants[1].webex.internal.board.getChannel(activeChannel));
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
it('deletes inactive channel', () => {
|
|
650
|
+
let inActiveChannel;
|
|
651
|
+
|
|
652
|
+
return participants[1].webex.internal.board
|
|
653
|
+
.createChannel(conversation)
|
|
654
|
+
.then((res) => {
|
|
655
|
+
inActiveChannel = res;
|
|
656
|
+
|
|
657
|
+
return participants[1].webex.internal.board.deleteChannel(
|
|
658
|
+
conversation,
|
|
659
|
+
inActiveChannel,
|
|
660
|
+
{preventDeleteActiveChannel: true}
|
|
661
|
+
);
|
|
662
|
+
})
|
|
663
|
+
.then(() =>
|
|
664
|
+
assert.isRejected(participants[1].webex.internal.board.getChannel(inActiveChannel))
|
|
665
|
+
);
|
|
666
|
+
});
|
|
667
|
+
});
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
describe('#lockChannelForDeletion()', () => {
|
|
671
|
+
it('locks a channel for deletion which rejects any incoming activities', () => {
|
|
672
|
+
let newChannel;
|
|
673
|
+
|
|
674
|
+
return participants[1].webex.internal.board
|
|
675
|
+
.createChannel(conversation)
|
|
676
|
+
.then((res) => {
|
|
677
|
+
newChannel = res;
|
|
678
|
+
|
|
679
|
+
return participants[1].webex.internal.board.lockChannelForDeletion(newChannel);
|
|
680
|
+
})
|
|
681
|
+
.then(() => {
|
|
682
|
+
const data = [
|
|
683
|
+
{
|
|
684
|
+
type: 'curve',
|
|
685
|
+
payload: JSON.stringify({type: 'curve'}),
|
|
686
|
+
},
|
|
687
|
+
];
|
|
688
|
+
|
|
689
|
+
return assert.isRejected(
|
|
690
|
+
participants[0].webex.internal.board.addContent(newChannel, data)
|
|
691
|
+
);
|
|
692
|
+
});
|
|
693
|
+
});
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
describe('#keepActive()', () => {
|
|
697
|
+
it('keeps a channel status as active', () => {
|
|
698
|
+
let newChannel;
|
|
699
|
+
|
|
700
|
+
return participants[1].webex.internal.board
|
|
701
|
+
.createChannel(conversation)
|
|
702
|
+
.then((res) => {
|
|
703
|
+
newChannel = res;
|
|
704
|
+
|
|
705
|
+
return participants[1].webex.internal.board.keepActive(newChannel);
|
|
706
|
+
})
|
|
707
|
+
.then(() =>
|
|
708
|
+
assert.isRejected(
|
|
709
|
+
participants[0].webex.internal.board.deleteChannel(conversation, newChannel, {
|
|
710
|
+
preventDeleteActiveChannel: true,
|
|
711
|
+
})
|
|
712
|
+
)
|
|
713
|
+
);
|
|
714
|
+
});
|
|
715
|
+
});
|
|
716
|
+
});
|
|
717
|
+
});
|