@webex/internal-plugin-mercury 3.0.0-beta.4 → 3.0.0-beta.400

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.
@@ -14,7 +14,7 @@ import {
14
14
  ConnectionError,
15
15
  Forbidden,
16
16
  NotAuthorized,
17
- UnknownResponse
17
+ UnknownResponse,
18
18
  // NotFound
19
19
  } from '../errors';
20
20
 
@@ -30,6 +30,7 @@ export default class Socket extends EventEmitter {
30
30
  */
31
31
  constructor() {
32
32
  super();
33
+ this._domain = 'unknown-domain';
33
34
  this.onmessage = this.onmessage.bind(this);
34
35
  this.onclose = this.onclose.bind(this);
35
36
  }
@@ -88,7 +89,9 @@ export default class Socket extends EventEmitter {
88
89
  * @returns {WebSocket}
89
90
  */
90
91
  static getWebSocketConstructor() {
91
- throw new Error('Socket.getWebSocketConstructor() must be implemented in an environmentally appropriate way');
92
+ throw new Error(
93
+ 'Socket.getWebSocketConstructor() must be implemented in an environmentally appropriate way'
94
+ );
92
95
  }
93
96
 
94
97
  /**
@@ -109,10 +112,10 @@ export default class Socket extends EventEmitter {
109
112
  return;
110
113
  }
111
114
  // logger is defined once open is called
112
- this.logger.info('socket: closing');
115
+ this.logger.info(`socket,${this._domain}: closing`);
113
116
 
114
117
  if (socket.readyState === 2 || socket.readyState === 3) {
115
- this.logger.info('socket: already closed');
118
+ this.logger.info(`socket,${this._domain}: already closed`);
116
119
  resolve();
117
120
 
118
121
  return;
@@ -127,24 +130,25 @@ export default class Socket extends EventEmitter {
127
130
 
128
131
  options = defaults(options, {
129
132
  code: 1000,
130
- reason: 'Done'
133
+ reason: 'Done',
131
134
  });
132
135
 
133
136
  const closeTimer = safeSetTimeout(() => {
134
137
  try {
135
- this.logger.info('socket: no close event received, forcing closure');
136
- resolve(this.onclose({
137
- code: 1000,
138
- reason: 'Done (forced)'
139
- }));
140
- }
141
- catch (error) {
142
- this.logger.warn('socket: force-close failed', error);
138
+ this.logger.info(`socket,${this._domain}: no close event received, forcing closure`);
139
+ resolve(
140
+ this.onclose({
141
+ code: 1000,
142
+ reason: 'Done (forced)',
143
+ })
144
+ );
145
+ } catch (error) {
146
+ this.logger.warn(`socket,${this._domain}: force-close failed`, error);
143
147
  }
144
148
  }, this.forceCloseDelay);
145
149
 
146
150
  socket.onclose = (event) => {
147
- this.logger.info('socket: close event fired', event.code, event.reason);
151
+ this.logger.info(`socket,${this._domain}: close event fired`, event.code, event.reason);
148
152
  clearTimeout(closeTimer);
149
153
  this.onclose(event);
150
154
  resolve(event);
@@ -168,6 +172,12 @@ export default class Socket extends EventEmitter {
168
172
  * @returns {Promise}
169
173
  */
170
174
  open(url, options) {
175
+ try {
176
+ this._domain = new URL(url).hostname;
177
+ } catch {
178
+ this._domain = url;
179
+ }
180
+
171
181
  return new Promise((resolve, reject) => {
172
182
  /* eslint complexity: [0] */
173
183
  if (!url) {
@@ -184,25 +194,21 @@ export default class Socket extends EventEmitter {
184
194
 
185
195
  options = options || {};
186
196
 
187
- checkRequired([
188
- 'forceCloseDelay',
189
- 'pingInterval',
190
- 'pongTimeout',
191
- 'token',
192
- 'trackingId',
193
- 'logger'
194
- ], options);
197
+ checkRequired(
198
+ ['forceCloseDelay', 'pingInterval', 'pongTimeout', 'token', 'trackingId', 'logger'],
199
+ options
200
+ );
195
201
 
196
202
  Object.keys(options).forEach((key) => {
197
203
  Reflect.defineProperty(this, key, {
198
204
  enumerable: false,
199
- value: options[key]
205
+ value: options[key],
200
206
  });
201
207
  });
202
208
 
203
209
  const WebSocket = Socket.getWebSocketConstructor();
204
210
 
205
- this.logger.info('socket: creating WebSocket');
211
+ this.logger.info(`socket,${this._domain}: creating WebSocket`);
206
212
  const socket = new WebSocket(url, [], options);
207
213
 
208
214
  socket.binaryType = 'arraybuffer';
@@ -210,13 +216,13 @@ export default class Socket extends EventEmitter {
210
216
 
211
217
  socket.onclose = (event) => {
212
218
  event = this._fixCloseCode(event);
213
- this.logger.info('socket: closed before open', event.code, event.reason);
219
+ this.logger.info(`socket,${this._domain}: closed before open`, event.code, event.reason);
214
220
  switch (event.code) {
215
221
  case 1005:
216
- // IE 11 doesn't seem to allow 4XXX codes, so if we get a 1005, assume
217
- // it's a bad websocket url. That'll trigger a device refresh; if it
218
- // turns out we had a bad token, the device refresh should 401 and
219
- // trigger a token refresh.
222
+ // IE 11 doesn't seem to allow 4XXX codes, so if we get a 1005, assume
223
+ // it's a bad websocket url. That'll trigger a device refresh; if it
224
+ // turns out we had a bad token, the device refresh should 401 and
225
+ // trigger a token refresh.
220
226
  return reject(new UnknownResponse(event));
221
227
  case 4400:
222
228
  return reject(new BadRequest(event));
@@ -224,18 +230,18 @@ export default class Socket extends EventEmitter {
224
230
  return reject(new NotAuthorized(event));
225
231
  case 4403:
226
232
  return reject(new Forbidden(event));
227
- // case 4404:
228
- // return reject(new NotFound(event));
233
+ // case 4404:
234
+ // return reject(new NotFound(event));
229
235
  default:
230
236
  return reject(new ConnectionError(event));
231
237
  }
232
238
  };
233
239
 
234
240
  socket.onopen = () => {
235
- this.logger.info('socket: connected');
241
+ this.logger.info(`socket,${this._domain}: connected`);
236
242
  this._authorize()
237
243
  .then(() => {
238
- this.logger.info('socket: authorized');
244
+ this.logger.info(`socket,${this._domain}: authorized`);
239
245
  socket.onclose = this.onclose;
240
246
  resolve();
241
247
  })
@@ -243,11 +249,11 @@ export default class Socket extends EventEmitter {
243
249
  };
244
250
 
245
251
  socket.onerror = (event) => {
246
- this.logger.warn('socket: error event fired', event);
252
+ this.logger.warn(`socket,${this._domain}: error event fired`, event);
247
253
  };
248
254
 
249
255
  sockets.set(this, socket);
250
- this.logger.info('socket: waiting for server');
256
+ this.logger.info(`socket,${this._domain}: waiting for server`);
251
257
  });
252
258
  }
253
259
 
@@ -257,7 +263,7 @@ export default class Socket extends EventEmitter {
257
263
  * @returns {undefined}
258
264
  */
259
265
  onclose(event) {
260
- this.logger.info('socket: closed', event.code, event.reason);
266
+ this.logger.info(`socket,${this._domain}: closed`, event.code, event.reason);
261
267
  clearTimeout(this.pongTimer);
262
268
  clearTimeout(this.pingTimer);
263
269
 
@@ -279,9 +285,11 @@ export default class Socket extends EventEmitter {
279
285
  const data = JSON.parse(event.data);
280
286
  const sequenceNumber = parseInt(data.sequenceNumber, 10);
281
287
 
282
- this.logger.debug('socket: sequence number: ', sequenceNumber);
288
+ this.logger.debug(`socket,${this._domain}: sequence number: `, sequenceNumber);
283
289
  if (this.expectedSequenceNumber && sequenceNumber !== this.expectedSequenceNumber) {
284
- this.logger.debug(`socket: sequence number mismatch indicates lost mercury message. expected: ${this.expectedSequenceNumber}, actual: ${sequenceNumber}`);
290
+ this.logger.debug(
291
+ `socket,${this._domain}: sequence number mismatch indicates lost mercury message. expected: ${this.expectedSequenceNumber}, actual: ${sequenceNumber}`
292
+ );
285
293
  this.emit('sequence-mismatch', sequenceNumber, this.expectedSequenceNumber);
286
294
  }
287
295
  this.expectedSequenceNumber = sequenceNumber + 1;
@@ -294,17 +302,15 @@ export default class Socket extends EventEmitter {
294
302
  this._acknowledge(processedEvent);
295
303
  if (data.type === 'pong') {
296
304
  this.emit('pong', processedEvent);
297
- }
298
- else {
305
+ } else {
299
306
  this.emit('message', processedEvent);
300
307
  }
301
- }
302
- catch (error) {
308
+ } catch (error) {
303
309
  // The above code should only be able to throw if we receive an unparsable
304
310
  // message from Mercury. At this time, the only action we have is to
305
311
  // ignore it and move on.
306
312
  /* istanbul ignore next */
307
- this.logger.warn('socket: error while receiving WebSocket message', error);
313
+ this.logger.warn(`socket,${this._domain}: error while receiving WebSocket message`, error);
308
314
  }
309
315
  }
310
316
 
@@ -347,7 +353,7 @@ export default class Socket extends EventEmitter {
347
353
 
348
354
  return this.send({
349
355
  messageId: event.data.id,
350
- type: 'ack'
356
+ type: 'ack',
351
357
  });
352
358
  }
353
359
 
@@ -358,19 +364,23 @@ export default class Socket extends EventEmitter {
358
364
  */
359
365
  _authorize() {
360
366
  return new Promise((resolve) => {
361
- this.logger.info('socket: authorizing');
367
+ this.logger.info(`socket,${this._domain}: authorizing`);
362
368
  this.send({
363
369
  id: uuid.v4(),
364
370
  type: 'authorization',
365
371
  data: {
366
- token: this.token
372
+ token: this.token,
367
373
  },
368
374
  trackingId: this.trackingId,
369
- logLevelToken: this.logLevelToken
375
+ logLevelToken: this.logLevelToken,
370
376
  });
371
377
 
372
378
  const waitForBufferState = (event) => {
373
- if (!event.data.type && (event.data.data.eventType === 'mercury.buffer_state' || event.data.data.eventType === 'mercury.registration_status')) {
379
+ if (
380
+ !event.data.type &&
381
+ (event.data.data.eventType === 'mercury.buffer_state' ||
382
+ event.data.data.eventType === 'mercury.registration_status')
383
+ ) {
374
384
  this.removeListener('message', waitForBufferState);
375
385
  this._ping();
376
386
  resolve();
@@ -392,12 +402,18 @@ export default class Socket extends EventEmitter {
392
402
  if (event.code === 1005 && event.reason) {
393
403
  switch (event.reason.toLowerCase()) {
394
404
  case 'replaced':
395
- this.logger.info('socket: fixing CloseEvent code for reason: ', event.reason);
405
+ this.logger.info(
406
+ `socket,${this._domain}: fixing CloseEvent code for reason: `,
407
+ event.reason
408
+ );
396
409
  event.code = 4000;
397
410
  break;
398
411
  case 'authentication failed':
399
412
  case 'authentication did not happen within the timeout window of 30000 seconds.':
400
- this.logger.info('socket: fixing CloseEvent code for reason: ', event.reason);
413
+ this.logger.info(
414
+ `socket,${this._domain}: fixing CloseEvent code for reason: `,
415
+ event.reason
416
+ );
401
417
  event.code = 1008;
402
418
  break;
403
419
  default:
@@ -417,40 +433,44 @@ export default class Socket extends EventEmitter {
417
433
  _ping(id) {
418
434
  const confirmPongId = (event) => {
419
435
  try {
420
- this.logger.debug('socket: pong', event.data.id);
436
+ this.logger.debug(`socket,${this._domain}: pong`, event.data.id);
421
437
  if (event.data && event.data.id !== id) {
422
- this.logger.info('socket: received pong for wrong ping id, closing socket');
423
- this.logger.debug('socket: expected', id, 'received', event.data.id);
438
+ this.logger.info(
439
+ `socket,${this._domain}: received pong for wrong ping id, closing socket`
440
+ );
441
+ this.logger.debug(`socket,${this._domain}: expected`, id, 'received', event.data.id);
424
442
  this.close({
425
443
  code: 1000,
426
- reason: 'Pong mismatch'
444
+ reason: 'Pong mismatch',
427
445
  });
428
446
  }
429
- }
430
- catch (error) {
447
+ } catch (error) {
431
448
  // This try/catch block was added as a debugging step; to the best of my
432
449
  // knowledge, the above can never throw.
433
450
  /* istanbul ignore next */
434
- this.logger.error('socket: error occurred in confirmPongId', error);
451
+ this.logger.error(`socket,${this._domain}: error occurred in confirmPongId`, error);
435
452
  }
436
453
  };
437
454
 
438
455
  const onPongNotReceived = () => {
439
456
  try {
440
- this.logger.info('socket: pong not receive in expected period, closing socket');
457
+ this.logger.info(
458
+ `socket,${this._domain}: pong not receive in expected period, closing socket`
459
+ );
441
460
  this.close({
442
461
  code: 1000,
443
- reason: 'Pong not received'
444
- })
445
- .catch((reason) => {
446
- this.logger.warn('socket: failed to close socket after missed pong', reason);
447
- });
448
- }
449
- catch (error) {
462
+ reason: 'Pong not received',
463
+ }).catch((reason) => {
464
+ this.logger.warn(
465
+ `socket,${this._domain}: failed to close socket after missed pong`,
466
+ reason
467
+ );
468
+ });
469
+ } catch (error) {
450
470
  // This try/catch block was added as a debugging step; to the best of my
451
471
  // knowledge, the above can never throw.
452
472
  /* istanbul ignore next */
453
- this.logger.error('socket: error occurred in onPongNotReceived', error);
473
+ this.logger.error(`socket,${this._domain}: error occurred in onPongNotReceived`, error);
454
474
  }
455
475
  };
456
476
 
@@ -458,25 +478,37 @@ export default class Socket extends EventEmitter {
458
478
  try {
459
479
  clearTimeout(this.pongTimer);
460
480
  this.pingTimer = safeSetTimeout(() => this._ping(), this.pingInterval);
461
- }
462
- catch (error) {
481
+ } catch (error) {
463
482
  // This try/catch block was added as a debugging step; to the best of my
464
483
  // knowledge, the above can never throw.
465
484
  /* istanbul ignore next */
466
- this.logger.error('socket: error occurred in scheduleNextPingAndCancelPongTimer', error);
485
+ this.logger.error(
486
+ `socket,${this._domain}: error occurred in scheduleNextPingAndCancelPongTimer`,
487
+ error
488
+ );
467
489
  }
468
490
  };
469
491
 
492
+ const calculateLatency = (pingTimestamp) => {
493
+ const now = performance.now();
494
+ const latency = now - pingTimestamp;
495
+
496
+ this.logger.debug(`socket,${this._domain}: latency: `, latency);
497
+ this.emit('ping-pong-latency', latency);
498
+ };
499
+
470
500
  id = id || uuid.v4();
501
+ const pingTimestamp = performance.now();
502
+
471
503
  this.pongTimer = safeSetTimeout(onPongNotReceived, this.pongTimeout);
472
504
  this.once('pong', scheduleNextPingAndCancelPongTimer);
473
505
  this.once('pong', confirmPongId);
474
-
475
- this.logger.debug(`socket: ping ${id}`);
506
+ this.once('pong', () => calculateLatency(pingTimestamp));
507
+ this.logger.debug(`socket,${this._domain}: ping ${id}`);
476
508
 
477
509
  return this.send({
478
510
  id,
479
- type: 'ping'
511
+ type: 'ping',
480
512
  });
481
513
  }
482
514
  }
@@ -1,3 +1,5 @@
1
+ /* eslint-disable no-restricted-globals */
2
+
1
3
  /*!
2
4
  * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
5
  */
@@ -12,18 +14,14 @@ Socket.getWebSocketConstructor = function getWebSocketConstructor() {
12
14
 
13
15
  if (typeof WebSocket !== 'undefined') {
14
16
  ws = WebSocket;
15
- }
16
- else if (typeof MozWebSocket !== 'undefined') {
17
+ } else if (typeof MozWebSocket !== 'undefined') {
17
18
  // eslint-disable-next-line no-undef
18
19
  ws = MozWebSocket;
19
- }
20
- else if (typeof global !== 'undefined') {
20
+ } else if (typeof global !== 'undefined') {
21
21
  ws = global.WebSocket || global.MozWebSocket;
22
- }
23
- else if (typeof window !== 'undefined') {
22
+ } else if (typeof window !== 'undefined') {
24
23
  ws = window.WebSocket || window.MozWebSocket;
25
- }
26
- else if (typeof self !== 'undefined') {
24
+ } else if (typeof self !== 'undefined') {
27
25
  ws = self.WebSocket || self.MozWebSocket;
28
26
  }
29
27
 
@@ -16,34 +16,37 @@ describe('plugin-mercury', function () {
16
16
  describe('Mercury', () => {
17
17
  let webex;
18
18
 
19
- beforeEach(() => testUsers.create({count: 1})
20
- .then((users) => {
19
+ beforeEach(() =>
20
+ testUsers.create({count: 1}).then((users) => {
21
21
  webex = new WebexCore({
22
22
  credentials: {
23
- supertoken: users[0].token
23
+ supertoken: users[0].token,
24
24
  },
25
25
  config: {
26
26
  credentials: {
27
- refreshCallback
28
- }
29
- }
27
+ refreshCallback,
28
+ },
29
+ },
30
30
  });
31
- }));
31
+ })
32
+ );
32
33
 
33
34
  afterEach(() => webex && webex.internal.mercury.disconnect());
34
35
 
35
36
  describe('#connect()', () => {
36
37
  it('connects to mercury', () => webex.internal.mercury.connect());
37
38
 
38
- it('refreshes the access token when a 4401 is received', () => webex.internal.device.register()
39
- .then(() => {
40
- // eslint-disable-next-line camelcase
41
- webex.credentials.supertoken.access_token = 'fake token';
39
+ it('refreshes the access token when a 4401 is received', () =>
40
+ webex.internal.device
41
+ .register()
42
+ .then(() => {
43
+ // eslint-disable-next-line camelcase
44
+ webex.credentials.supertoken.access_token = 'fake token';
42
45
 
43
- return webex.internal.mercury.connect();
44
- })
45
- // eslint-disable-next-line camelcase
46
- .then(() => assert.notEqual(webex.credentials.supertoken.access_token, 'fake token')));
46
+ return webex.internal.mercury.connect();
47
+ })
48
+ // eslint-disable-next-line camelcase
49
+ .then(() => assert.notEqual(webex.credentials.supertoken.access_token, 'fake token')));
47
50
 
48
51
  // This doesn't work as designed yet. The only way to get a 4404 is to try
49
52
  // to connect to someone else's valid registration; the intent was to get
@@ -70,26 +73,34 @@ describe('plugin-mercury', function () {
70
73
  });
71
74
 
72
75
  describe('when web-high-availability is enabled', () => {
73
- flaky(it, process.env.SKIP_FLAKY_TESTS)('connects to mercury using service catalog url', () => {
74
- let defaultWebSocketUrl;
75
-
76
- // we need to ensure the feature is set for user before "registering"
77
- // the device
78
- return webex.internal.device.register()
79
- .then(() => webex.internal.feature.setFeature('developer', 'web-high-availability', true))
80
- .then(() => webex.internal.device.unregister())
81
- // start the test flow the device list
82
- .then(() => webex.internal.device.register())
83
- .then(() => {
84
- defaultWebSocketUrl = webex.internal.device.webSocketUrl;
85
- })
86
- .then(() => webex.internal.mercury.connect())
87
- .then(() => webex.internal.device.getWebSocketUrl())
88
- .then((wsUrl) => {
89
- assert.notEqual(defaultWebSocketUrl, webex.internal.mercury.socket.url);
90
- assert.include(webex.internal.mercury.socket.url, wsUrl);
91
- });
92
- });
76
+ flaky(it, process.env.SKIP_FLAKY_TESTS)(
77
+ 'connects to mercury using service catalog url',
78
+ () => {
79
+ let defaultWebSocketUrl;
80
+
81
+ // we need to ensure the feature is set for user before "registering"
82
+ // the device
83
+ return (
84
+ webex.internal.device
85
+ .register()
86
+ .then(() =>
87
+ webex.internal.feature.setFeature('developer', 'web-high-availability', true)
88
+ )
89
+ .then(() => webex.internal.device.unregister())
90
+ // start the test flow the device list
91
+ .then(() => webex.internal.device.register())
92
+ .then(() => {
93
+ defaultWebSocketUrl = webex.internal.device.webSocketUrl;
94
+ })
95
+ .then(() => webex.internal.mercury.connect())
96
+ .then(() => webex.internal.device.getWebSocketUrl())
97
+ .then((wsUrl) => {
98
+ assert.notEqual(defaultWebSocketUrl, webex.internal.mercury.socket.url);
99
+ assert.include(webex.internal.mercury.socket.url, wsUrl);
100
+ })
101
+ );
102
+ }
103
+ );
93
104
  });
94
105
  });
95
106
 
@@ -98,10 +109,9 @@ describe('plugin-mercury', function () {
98
109
 
99
110
  webex.internal.mercury.on('event:mercury.buffer_state', spy);
100
111
 
101
- return webex.internal.mercury.connect()
102
- .then(() => {
103
- assert.calledOnce(spy);
104
- });
112
+ return webex.internal.mercury.connect().then(() => {
113
+ assert.calledOnce(spy);
114
+ });
105
115
  });
106
116
  });
107
117
  });
@@ -14,17 +14,19 @@ describe('plugin-mercury', function () {
14
14
  describe('Sharable Mercury', () => {
15
15
  let webex;
16
16
 
17
- beforeEach(() => testUsers.create({count: 1})
18
- .then((users) => {
17
+ beforeEach(() =>
18
+ testUsers.create({count: 1}).then((users) => {
19
19
  webex = new WebexCore({
20
20
  credentials: {
21
- supertoken: users[0].token
22
- }
21
+ supertoken: users[0].token,
22
+ },
23
23
  });
24
24
 
25
- return webex.internal.device.register()
25
+ return webex.internal.device
26
+ .register()
26
27
  .then(() => webex.internal.feature.setFeature('developer', 'web-shared-mercury', true));
27
- }));
28
+ })
29
+ );
28
30
 
29
31
  afterEach(() => webex && webex.internal.mercury.disconnect());
30
32
 
@@ -39,17 +41,19 @@ describe('plugin-mercury', function () {
39
41
  webex.internal.mercury.on('event:mercury.buffer_state', spy1);
40
42
  webex.internal.mercury.on('event:mercury.registration_status', spy2);
41
43
 
42
- return webex.internal.mercury.connect()
43
- .then(() => {
44
- assert.notCalled(spy1);
45
- assert.calledOnce(spy2);
46
- const {data} = spy2.args[0][0];
44
+ return webex.internal.mercury.connect().then(() => {
45
+ assert.notCalled(spy1);
46
+ assert.calledOnce(spy2);
47
+ const {data} = spy2.args[0][0];
47
48
 
48
- assert.property(data, 'bufferState');
49
- assert.property(data, 'localClusterServiceUrls');
49
+ assert.property(data, 'bufferState');
50
+ assert.property(data, 'localClusterServiceUrls');
50
51
 
51
- assert.deepEqual(webex.internal.mercury.localClusterServiceUrls, data.localClusterServiceUrls);
52
- });
52
+ assert.deepEqual(
53
+ webex.internal.mercury.localClusterServiceUrls,
54
+ data.localClusterServiceUrls
55
+ );
56
+ });
53
57
  });
54
58
  });
55
59
  });
@@ -14,29 +14,31 @@ describe('plugin-mercury', function () {
14
14
 
15
15
  let webex;
16
16
 
17
- beforeEach('create users', () => testUsers.create({count: 1})
18
- .then(async (users) => {
17
+ beforeEach('create users', () =>
18
+ testUsers.create({count: 1}).then(async (users) => {
19
19
  // Pause for 5 seconds for CI
20
20
  await new Promise((done) => setTimeout(done, 5000));
21
21
 
22
22
  webex = new WebexCore({
23
23
  credentials: {
24
- supertoken: users[0].token
25
- }
24
+ supertoken: users[0].token,
25
+ },
26
26
  });
27
27
  sinon.spy(webex.internal.mercury, 'disconnect');
28
28
  sinon.spy(webex.internal.device, 'unregister');
29
29
 
30
30
  return webex.internal.mercury.connect();
31
- }));
31
+ })
32
+ );
32
33
 
33
34
  describe('onBeforeLogout()', () => {
34
- it('disconnects the web socket', () => webex.logout({noRedirect: true})
35
- .then(() => {
35
+ it('disconnects the web socket', () => {
36
+ webex.logout({noRedirect: true}).then(() => {
36
37
  assert.called(webex.internal.mercury.disconnect);
37
38
  assert.isFalse(webex.internal.mercury.connected);
38
39
  assert.called(webex.internal.device.unregister);
39
40
  assert.isFalse(webex.internal.device.registered);
40
- }));
41
+ });
42
+ });
41
43
  });
42
44
  });