@webex/internal-plugin-mercury 3.7.0 → 3.8.0-next.2

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.
@@ -143,9 +143,9 @@ describe('plugin-mercury', () => {
143
143
  const envelope = {
144
144
  data: {
145
145
  featureToggle: {
146
- 'feature-name': true
147
- }
148
- }
146
+ 'feature-name': true,
147
+ },
148
+ },
149
149
  };
150
150
 
151
151
  assert.isFalse(mercury.connected, 'Mercury is not connected');
@@ -157,7 +157,10 @@ describe('plugin-mercury', () => {
157
157
  assert.isFalse(mercury.connecting, 'Mercury is not connecting');
158
158
  assert.calledWith(socketOpenStub, sinon.match(/ws:\/\/example.com/), sinon.match.any);
159
159
  mercury._emit('event:featureToggle_update', envelope);
160
- assert.calledOnceWithExactly(webex.internal.feature.updateFeature, envelope.data.featureToggle);
160
+ assert.calledOnceWithExactly(
161
+ webex.internal.feature.updateFeature,
162
+ envelope.data.featureToggle
163
+ );
161
164
  sinon.restore();
162
165
  });
163
166
  });
@@ -175,7 +178,6 @@ describe('plugin-mercury', () => {
175
178
  });
176
179
 
177
180
  describe('when `maxRetries` is set', () => {
178
-
179
181
  const check = () => {
180
182
  socketOpenStub.restore();
181
183
  socketOpenStub = sinon.stub(Socket.prototype, 'open');
@@ -209,7 +211,7 @@ describe('plugin-mercury', () => {
209
211
  .then(() => {
210
212
  assert.calledThrice(Socket.prototype.open);
211
213
  });
212
- }
214
+ };
213
215
 
214
216
  // skipping due to apparent bug with lolex in all browsers but Chrome.
215
217
  // if initial retries is zero and mercury has never connected max retries is used
@@ -504,24 +506,33 @@ describe('plugin-mercury', () => {
504
506
  });
505
507
 
506
508
  describe('#logout()', () => {
507
- it('calls disconnect', () => {
509
+ it('calls disconnect and logs', () => {
510
+ sinon.stub(mercury.logger, 'info');
508
511
  sinon.stub(mercury, 'disconnect');
509
512
  mercury.logout();
510
513
  assert.called(mercury.disconnect);
514
+ assert.calledTwice(mercury.logger.info);
515
+
516
+ assert.calledWith(mercury.logger.info.getCall(0), 'Mercury: logout() called');
517
+ assert.isTrue(
518
+ mercury.logger.info
519
+ .getCall(1)
520
+ .args[0].startsWith('Mercury: debug_mercury_logging stack: ')
521
+ );
511
522
  });
512
523
 
513
- it('uses the config.beforeLogoutOptionsCloseReason to disconnect and will send code 1050 for logout', () => {
524
+ it('uses the config.beforeLogoutOptionsCloseReason to disconnect and will send code 3050 for logout', () => {
514
525
  sinon.stub(mercury, 'disconnect');
515
526
  mercury.config.beforeLogoutOptionsCloseReason = 'done (permanent)';
516
527
  mercury.logout();
517
- assert.calledWith(mercury.disconnect, {code: 1050, reason: 'done (permanent)'});
528
+ assert.calledWith(mercury.disconnect, {code: 3050, reason: 'done (permanent)'});
518
529
  });
519
530
 
520
- it('uses the config.beforeLogoutOptionsCloseReason to disconnect and will send code 1050 for logout if the reason is different than standard', () => {
531
+ it('uses the config.beforeLogoutOptionsCloseReason to disconnect and will send code 3050 for logout if the reason is different than standard', () => {
521
532
  sinon.stub(mercury, 'disconnect');
522
533
  mercury.config.beforeLogoutOptionsCloseReason = 'test';
523
534
  mercury.logout();
524
- assert.calledWith(mercury.disconnect, {code: 1050, reason: 'test'});
535
+ assert.calledWith(mercury.disconnect, {code: 3050, reason: 'test'});
525
536
  });
526
537
 
527
538
  it('uses the config.beforeLogoutOptionsCloseReason to disconnect and will send undefined for logout if the reason is same as standard', () => {
@@ -554,26 +565,26 @@ describe('plugin-mercury', () => {
554
565
  assert.isUndefined(mercury.mockWebSocket, 'Mercury does not have a mockWebSocket');
555
566
  }));
556
567
 
557
- it('disconnects the WebSocket with code 1050', () =>
558
- mercury
559
- .connect()
560
- .then(() => {
561
- assert.isTrue(mercury.connected, 'Mercury is connected');
562
- assert.isFalse(mercury.connecting, 'Mercury is not connecting');
563
- const promise = mercury.disconnect();
564
-
565
- mockWebSocket.emit('close', {
566
- code: 1050,
567
- reason: 'done (permanent)',
568
- });
569
-
570
- return promise;
571
- })
572
- .then(() => {
573
- assert.isFalse(mercury.connected, 'Mercury is not connected');
574
- assert.isFalse(mercury.connecting, 'Mercury is not connecting');
575
- assert.isUndefined(mercury.mockWebSocket, 'Mercury does not have a mockWebSocket');
576
- }));
568
+ it('disconnects the WebSocket with code 3050', () =>
569
+ mercury
570
+ .connect()
571
+ .then(() => {
572
+ assert.isTrue(mercury.connected, 'Mercury is connected');
573
+ assert.isFalse(mercury.connecting, 'Mercury is not connecting');
574
+ const promise = mercury.disconnect();
575
+
576
+ mockWebSocket.emit('close', {
577
+ code: 3050,
578
+ reason: 'done (permanent)',
579
+ });
580
+
581
+ return promise;
582
+ })
583
+ .then(() => {
584
+ assert.isFalse(mercury.connected, 'Mercury is not connected');
585
+ assert.isFalse(mercury.connecting, 'Mercury is not connecting');
586
+ assert.isUndefined(mercury.mockWebSocket, 'Mercury does not have a mockWebSocket');
587
+ }));
577
588
 
578
589
  it('stops emitting message events', () => {
579
590
  const spy = sinon.spy();
@@ -687,7 +698,7 @@ describe('plugin-mercury', () => {
687
698
  return assert.isRejected(promise).then((error) => {
688
699
  const lastError = mercury.getLastError();
689
700
 
690
- assert.equal(error.message, "Mercury Connection Aborted");
701
+ assert.equal(error.message, 'Mercury Connection Aborted');
691
702
  assert.isDefined(lastError);
692
703
  assert.equal(lastError, realError);
693
704
  });
@@ -716,6 +727,21 @@ describe('plugin-mercury', () => {
716
727
  return res;
717
728
  });
718
729
  });
730
+
731
+ it('_onmessage without eventType', () => {
732
+ sinon.spy(mercury, '_getEventHandlers');
733
+ sinon.spy(mercury, '_emit');
734
+ const event = {data: {data: {eventType: undefined, mydata: 'some data'}}};
735
+ mercury.logger.error.restore();
736
+ sinon.stub(mercury.logger, 'error');
737
+ return Promise.resolve(mercury._onmessage(event)).then(() => {
738
+ assert.calledWith(mercury._getEventHandlers, undefined);
739
+ assert.calledWith(mercury._emit, 'event', event.data);
740
+ assert.notCalled(mercury.logger.error);
741
+ mercury._emit.restore();
742
+ mercury._getEventHandlers.restore();
743
+ });
744
+ });
719
745
  });
720
746
 
721
747
  describe('#_applyOverrides()', () => {
@@ -773,6 +799,41 @@ describe('plugin-mercury', () => {
773
799
  });
774
800
  });
775
801
 
802
+ describe('#_setTimeOffset', () => {
803
+ it('sets mercuryTimeOffset based on the difference between wsWriteTimestamp and now', () => {
804
+ const event = {
805
+ data: {
806
+ wsWriteTimestamp: Date.now() - 60000,
807
+ },
808
+ };
809
+ assert.isUndefined(mercury.mercuryTimeOffset);
810
+ mercury._setTimeOffset(event);
811
+ assert.isDefined(mercury.mercuryTimeOffset);
812
+ assert.isTrue(mercury.mercuryTimeOffset > 0);
813
+ });
814
+ it('handles negative offsets', () => {
815
+ const event = {
816
+ data: {
817
+ wsWriteTimestamp: Date.now() + 60000,
818
+ },
819
+ };
820
+ mercury._setTimeOffset(event);
821
+ assert.isTrue(mercury.mercuryTimeOffset < 0);
822
+ });
823
+ it('handles invalid wsWriteTimestamp', () => {
824
+ const invalidTimestamps = [null, -1, 'invalid', undefined];
825
+ invalidTimestamps.forEach((invalidTimestamp) => {
826
+ const event = {
827
+ data: {
828
+ wsWriteTimestamp: invalidTimestamp,
829
+ },
830
+ };
831
+ mercury._setTimeOffset(event);
832
+ assert.isUndefined(mercury.mercuryTimeOffset);
833
+ });
834
+ });
835
+ });
836
+
776
837
  describe('#_prepareUrl()', () => {
777
838
  beforeEach(() => {
778
839
  webex.internal.device.webSocketUrl = 'ws://example.com';
@@ -139,6 +139,7 @@ describe('plugin-mercury', () => {
139
139
  ));
140
140
 
141
141
  it('accepts a logLevelToken option', () => {
142
+ const acknowledgeSpy = sinon.spy(socket, '_acknowledge');
142
143
  const promise = socket.open('ws://example.com', {
143
144
  forceCloseDelay: mockoptions.forceCloseDelay,
144
145
  pingInterval: mockoptions.pingInterval,
@@ -147,6 +148,7 @@ describe('plugin-mercury', () => {
147
148
  token: 'mocktoken',
148
149
  trackingId: 'mocktrackingid',
149
150
  logLevelToken: 'mocklogleveltoken',
151
+ acknowledgementRequired: true,
150
152
  });
151
153
 
152
154
  mockWebSocket.readyState = 1;
@@ -162,9 +164,83 @@ describe('plugin-mercury', () => {
162
164
  });
163
165
 
164
166
  return promise.then(() => {
167
+ assert.called(acknowledgeSpy);
165
168
  assert.equal(socket.logLevelToken, 'mocklogleveltoken');
166
169
  });
167
170
  });
171
+
172
+ it('accepts acknowledgementRequired option as false and skip acknowledge', () => {
173
+ const acknowledgeSpy = sinon.spy(socket, '_acknowledge');
174
+ const promise = socket.open('ws://example.com', {
175
+ forceCloseDelay: mockoptions.forceCloseDelay,
176
+ pingInterval: mockoptions.pingInterval,
177
+ pongTimeout: mockoptions.pongTimeout,
178
+ logger: console,
179
+ token: 'mocktoken',
180
+ trackingId: 'mocktrackingid',
181
+ logLevelToken: 'mocklogleveltoken',
182
+ acknowledgementRequired: false,
183
+ });
184
+
185
+ mockWebSocket.readyState = 1;
186
+ mockWebSocket.emit('open');
187
+
188
+ mockWebSocket.emit('message', {
189
+ data: JSON.stringify({
190
+ id: uuid.v4(),
191
+ data: {
192
+ eventType: 'mercury.buffer_state',
193
+ },
194
+ }),
195
+ });
196
+
197
+ return promise.then(() => {
198
+ assert.notCalled(acknowledgeSpy);
199
+ assert.equal(socket.logLevelToken, 'mocklogleveltoken');
200
+ });
201
+ });
202
+
203
+ it('accepts authorizationRequired option as false and skip authorize', () => {
204
+ const s = new Socket();
205
+ const authorizeSpy = sinon.spy(socket, '_authorize');
206
+ socket.open('ws://example.com', {
207
+ forceCloseDelay: mockoptions.forceCloseDelay,
208
+ pingInterval: mockoptions.pingInterval,
209
+ pongTimeout: mockoptions.pongTimeout,
210
+ logger: console,
211
+ token: 'mocktoken',
212
+ trackingId: 'mocktrackingid',
213
+ logLevelToken: 'mocklogleveltoken',
214
+ authorizationRequired: false,
215
+ });
216
+
217
+ mockWebSocket.readyState = 1;
218
+ mockWebSocket.emit('open');
219
+
220
+ assert.notCalled(authorizeSpy);
221
+ assert.called(socket._ping);
222
+ });
223
+
224
+ it('accepts authorizationRequired option as true and calles authorize', () => {
225
+ const s = new Socket();
226
+ const authorizeSpy = sinon.spy(socket, '_authorize');
227
+ socket.open('ws://example.com', {
228
+ forceCloseDelay: mockoptions.forceCloseDelay,
229
+ pingInterval: mockoptions.pingInterval,
230
+ pongTimeout: mockoptions.pongTimeout,
231
+ logger: console,
232
+ token: 'mocktoken',
233
+ trackingId: 'mocktrackingid',
234
+ logLevelToken: 'mocklogleveltoken',
235
+ authorizationRequired: true,
236
+ });
237
+
238
+ mockWebSocket.readyState = 1;
239
+ mockWebSocket.emit('open');
240
+
241
+ assert.called(authorizeSpy);
242
+ assert.called(socket._ping);
243
+ });
168
244
  });
169
245
 
170
246
  describe('#binaryType', () => {
@@ -452,7 +528,7 @@ describe('plugin-mercury', () => {
452
528
  Promise.all([
453
529
  assert.isRejected(
454
530
  socket.close({code: 1001}),
455
- /`options.code` must be 1000 or 1050 or between 3000 and 4999 \(inclusive\)/
531
+ /`options.code` must be 1000 or between 3000 and 4999 \(inclusive\)/
456
532
  ),
457
533
  socket.close({code: 1000}),
458
534
  ]));
@@ -468,10 +544,10 @@ describe('plugin-mercury', () => {
468
544
  it('accepts the logout reason', () =>
469
545
  socket
470
546
  .close({
471
- code: 1050,
547
+ code: 3050,
472
548
  reason: 'done (permanent)',
473
549
  })
474
- .then(() => assert.calledWith(mockWebSocket.close, 1050, 'done (permanent)')));
550
+ .then(() => assert.calledWith(mockWebSocket.close, 3050, 'done (permanent)')));
475
551
 
476
552
  it('can safely be called called multiple times', () => {
477
553
  const p1 = socket.close();
@@ -521,7 +597,7 @@ describe('plugin-mercury', () => {
521
597
  });
522
598
  });
523
599
 
524
- it('signals closure if no close frame is received within the specified window, but uses the initial options as 1050 if specified by options call', () => {
600
+ it('signals closure if no close frame is received within the specified window, but uses the initial options as 3050 if specified by options call', () => {
525
601
  const socket = new Socket();
526
602
  const promise = socket.open('ws://example.com', mockoptions);
527
603
 
@@ -546,21 +622,21 @@ describe('plugin-mercury', () => {
546
622
  });
547
623
  mockWebSocket.removeAllListeners('close');
548
624
 
549
- const promise = socket.close({code: 1050, reason: 'done (permanent)'});
625
+ const promise = socket.close({code: 3050, reason: 'done (permanent)'});
550
626
 
551
627
  clock.tick(mockoptions.forceCloseDelay);
552
628
 
553
629
  return promise.then(() => {
554
630
  assert.called(spy);
555
631
  assert.calledWith(spy, {
556
- code: 1050,
632
+ code: 3050,
557
633
  reason: 'done (permanent)',
558
634
  });
559
635
  });
560
636
  });
561
637
  });
562
638
 
563
- it('signals closure if no close frame is received within the specified window, and uses default options as 1000 if the code is not 1050', () => {
639
+ it('signals closure if no close frame is received within the specified window, and uses default options as 1000 if the code is not 3050', () => {
564
640
  const socket = new Socket();
565
641
  const promise = socket.open('ws://example.com', mockoptions);
566
642
 
@@ -593,7 +669,46 @@ describe('plugin-mercury', () => {
593
669
  assert.called(spy);
594
670
  assert.calledWith(spy, {
595
671
  code: 1000,
596
- reason: 'Done (forced)',
672
+ reason: 'test',
673
+ });
674
+ });
675
+ });
676
+ });
677
+
678
+ it('signals closure if no close frame is received within the specified window, and uses default options as 1000 if the code is not 3050', () => {
679
+ const socket = new Socket();
680
+ const promise = socket.open('ws://example.com', mockoptions);
681
+
682
+ mockWebSocket.readyState = 1;
683
+ mockWebSocket.emit('open');
684
+ mockWebSocket.emit('message', {
685
+ data: JSON.stringify({
686
+ id: uuid.v4(),
687
+ data: {
688
+ eventType: 'mercury.buffer_state',
689
+ },
690
+ }),
691
+ });
692
+
693
+ return promise.then(() => {
694
+ const spy = sinon.spy();
695
+
696
+ socket.on('close', spy);
697
+ mockWebSocket.close = () =>
698
+ new Promise(() => {
699
+ /* eslint no-inline-comments: [0] */
700
+ });
701
+ mockWebSocket.removeAllListeners('close');
702
+
703
+ const promise = socket.close({code: 1000});
704
+
705
+ clock.tick(mockoptions.forceCloseDelay);
706
+
707
+ return promise.then(() => {
708
+ assert.called(spy);
709
+ assert.calledWith(spy, {
710
+ code: 1000,
711
+ reason: 'Done (unknown)',
597
712
  });
598
713
  });
599
714
  });
@@ -705,9 +820,9 @@ describe('plugin-mercury', () => {
705
820
  );
706
821
  });
707
822
 
708
- describe('when it receives close code 1050', () => {
709
- it(`emits code 1050 for code 1050`, () => {
710
- const code = 1050;
823
+ describe('when it receives close code 3050', () => {
824
+ it(`emits code 3050 for code 3050`, () => {
825
+ const code = 3050;
711
826
  const reason = 'done (permanent)';
712
827
  const spy = sinon.spy();
713
828