"use strict";

var _Object$keys = require("@babel/runtime-corejs2/core-js/object/keys");
var _Object$getOwnPropertySymbols = require("@babel/runtime-corejs2/core-js/object/get-own-property-symbols");
var _Object$getOwnPropertyDescriptor = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptor");
var _Object$getOwnPropertyDescriptors = require("@babel/runtime-corejs2/core-js/object/get-own-property-descriptors");
var _Object$defineProperties = require("@babel/runtime-corejs2/core-js/object/define-properties");
var _Object$defineProperty2 = require("@babel/runtime-corejs2/core-js/object/define-property");
var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
var _regenerator = _interopRequireDefault(require("@babel/runtime-corejs2/regenerator"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/object/define-property"));
var _now = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/date/now"));
var _stringify = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/json/stringify"));
var _apply = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/reflect/apply"));
var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
var _defineProperty3 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty"));
var _construct2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/construct"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/asyncToGenerator"));
var _wrap2 = _interopRequireDefault(require("lodash/wrap"));
var _crypto = require("crypto");
var _sinon = _interopRequireDefault(require("sinon"));
var _testHelperChai = require("@webex/test-helper-chai");
var _testHelperMockWebex = _interopRequireDefault(require("@webex/test-helper-mock-webex"));
var _testHelperMockWebSocket = _interopRequireDefault(require("@webex/test-helper-mock-web-socket"));
var _index = _interopRequireDefault(require("./index"));
var _config = _interopRequireDefault(require("./config"));
var _socket = _interopRequireDefault(require("./socket"));
var _constants = require("./socket/constants");
var _promiseTick = _interopRequireDefault(require("./test/promise-tick"));
function ownKeys(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty3.default)(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { _Object$defineProperty2(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; } /*!
 * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
 */
if (!crypto.randomUUID) {
  (0, _defineProperty2.default)(crypto, 'randomUUID', {
    value: _crypto.randomUUID,
    configurable: true
  });
}
describe('plugin-mobiusSocket', function () {
  var createUuid = function createUuid() {
    return crypto.randomUUID();
  };
  describe('MobiusSocket', function () {
    describe('Events', function () {
      var mobiusSocket;
      var mockWebSocket;
      var originalSendSpy;
      var socketOpenStub;
      var usingFakeTimers;
      var webex;
      var fakeTestMessage = {
        id: createUuid(),
        data: {
          eventType: 'fake.test'
        },
        timestamp: (0, _now.default)(),
        trackingId: "suffix_".concat(createUuid(), "_").concat((0, _now.default)())
      };
      var statusStartTypingMessage = {
        id: createUuid(),
        data: {
          eventType: 'status.start_typing',
          actor: {
            id: 'actorId'
          },
          conversationId: createUuid()
        },
        timestamp: (0, _now.default)(),
        trackingId: "suffix_".concat(createUuid(), "_").concat((0, _now.default)())
      };
      var emitAuthResponse = function emitAuthResponse() {
        var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
          _ref$statusCode = _ref.statusCode,
          statusCode = _ref$statusCode === void 0 ? 200 : _ref$statusCode,
          _ref$statusMessage = _ref.statusMessage,
          statusMessage = _ref$statusMessage === void 0 ? 'OK' : _ref$statusMessage;
        var sendSpy = mockWebSocket.send.lastCall ? mockWebSocket.send : originalSendSpy;
        var authRequest = JSON.parse(sendSpy.lastCall.args[0]);
        mockWebSocket.emit('message', {
          data: (0, _stringify.default)({
            type: 'response_event',
            subtype: _constants.MESSAGE_TYPES.AUTH,
            trackingId: authRequest.trackingId,
            statusCode: statusCode,
            statusMessage: statusMessage
          })
        });
      };
      beforeEach(function () {
        jest.useFakeTimers({
          doNotFake: ['nextTick']
        });
        usingFakeTimers = true;
      });
      afterEach(/*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee() {
        var _t;
        return _regenerator.default.wrap(function (_context) {
          while (1) switch (_context.prev = _context.next) {
            case 0:
              if (usingFakeTimers) {
                jest.useRealTimers();
                usingFakeTimers = false;
              }
              if (!mobiusSocket) {
                _context.next = 4;
                break;
              }
              _context.prev = 1;
              _context.next = 2;
              return mobiusSocket.disconnect();
            case 2:
              _context.next = 4;
              break;
            case 3:
              _context.prev = 3;
              _t = _context["catch"](1);
            case 4:
              if (mockWebSocket && typeof mockWebSocket.close === 'function') {
                try {
                  mockWebSocket.close();
                } catch (e) {
                  // Ignore cleanup errors in tests.
                }
              }
              // Restore stubs
              if (_socket.default.getWebSocketConstructor.restore) {
                _socket.default.getWebSocketConstructor.restore();
              }
              if (socketOpenStub && socketOpenStub.restore) {
                socketOpenStub.restore();
              }
            case 5:
            case "end":
              return _context.stop();
          }
        }, _callee, null, [[1, 3]]);
      })));
      beforeEach(function () {
        webex = new _testHelperMockWebex.default();
        webex.internal.device.registered = true;
        webex.internal.metrics.submitClientMetrics = _sinon.default.stub();
        webex.trackingId = 'fakeTrackingId';
        webex.logger = console;
        _sinon.default.stub(_socket.default, 'getWebSocketConstructor').callsFake(function () {
          return function () {
            for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
              args[_key] = arguments[_key];
            }
            mockWebSocket = (0, _construct2.default)(_testHelperMockWebSocket.default, args);
            originalSendSpy = mockWebSocket.send;
            return mockWebSocket;
          };
        });
        var origOpen = _socket.default.prototype.open;
        socketOpenStub = _sinon.default.stub(_socket.default.prototype, 'open').callsFake(function () {
          for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
            args[_key2] = arguments[_key2];
          }
          var promise = (0, _apply.default)(origOpen, this, args);
          process.nextTick(function () {
            mockWebSocket.open();
            // Simulate Mobius auth response after socket open
            process.nextTick(function () {
              emitAuthResponse();
            });
          });
          return promise;
        });
        mobiusSocket = new _index.default(webex, _objectSpread({}, _config.default.mobiusSocket));
        mobiusSocket.logger = {
          debug: jest.fn(),
          error: jest.fn(),
          info: jest.fn(),
          log: jest.fn(),
          warn: jest.fn()
        };
      });
      it('removes all listeners for an event when off() is called without a listener', function () {
        var firstListener = _sinon.default.stub();
        var secondListener = _sinon.default.stub();
        mobiusSocket.on('event:fake.test', firstListener);
        mobiusSocket.on('event:fake.test', secondListener);
        mobiusSocket.off('event:fake.test');
        mobiusSocket.emit('event:fake.test', fakeTestMessage);
        _testHelperChai.assert.notCalled(firstListener);
        _testHelperChai.assert.notCalled(secondListener);
      });
      afterEach(function () {
        if (socketOpenStub) {
          socketOpenStub.restore();
        }
        if (_socket.default.getWebSocketConstructor.restore) {
          _socket.default.getWebSocketConstructor.restore();
        }
      });
      describe('when connected', function () {
        it('emits the `online` event', function () {
          var spy = _sinon.default.spy();
          mobiusSocket.on('online', spy);
          var promise = mobiusSocket.connect();
          return promise.then(function () {
            return _testHelperChai.assert.called(spy);
          });
        });
      });
      describe('when disconnected', function () {
        it('emits the `offline` event', function () {
          var spy = _sinon.default.spy();
          mobiusSocket.on('offline', spy);
          var promise = mobiusSocket.connect();
          mockWebSocket.open();
          return promise.then(function () {
            var disconnectPromise = mobiusSocket.disconnect();
            mockWebSocket.emit('close', {
              code: 1000,
              reason: 'Done'
            });
            return disconnectPromise;
          }).then(function () {
            return _testHelperChai.assert.calledOnce(spy);
          });
        });
        describe('when reconnected', function () {
          it('emits the `online` event', function () {
            var spy = _sinon.default.spy();
            mobiusSocket.on('online', spy);
            var promise = mobiusSocket.connect();
            mockWebSocket.open();
            return promise.then(function () {
              return _testHelperChai.assert.calledOnce(spy);
            }).then(function () {
              return mockWebSocket.emit('close', {
                code: 1000,
                reason: 'Idle'
              });
            }).then(function () {
              return mobiusSocket.connect();
            }).then(function () {
              return _testHelperChai.assert.calledTwice(spy);
            });
          });
        });
      });
      describe('when `mercury.buffer_state` is received', function () {
        // This test is here because the buffer states message may arrive before
        // the mobiusSocket Promise resolves.
        it('gets emitted', function () {
          var sendSpy;
          var resolveTest;
          var rejectTest;
          var bufferStateSpy = _sinon.default.spy();
          var onlineSpy = _sinon.default.spy();
          mobiusSocket.on('event:mercury.buffer_state', bufferStateSpy);
          mobiusSocket.on('online', onlineSpy);
          _socket.default.getWebSocketConstructor.callsFake(function () {
            return function () {
              for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
                args[_key3] = arguments[_key3];
              }
              mockWebSocket = (0, _construct2.default)(_testHelperMockWebSocket.default, args);
              sendSpy = mockWebSocket.send;
              mockWebSocket.send = (0, _wrap2.default)(mockWebSocket.send, function (fn) {
                var _this = this;
                for (var _len4 = arguments.length, sendArgs = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
                  sendArgs[_key4 - 1] = arguments[_key4];
                }
                process.nextTick(function () {
                  (0, _apply.default)(fn, _this, sendArgs);
                });
              });
              process.nextTick(function () {
                _testHelperChai.assert.isTrue(mobiusSocket.connecting, 'MobiusSocket is still connecting');
                _testHelperChai.assert.isFalse(mobiusSocket.connected, 'MobiusSocket has not yet connected');
                _testHelperChai.assert.notCalled(onlineSpy);
                _testHelperChai.assert.lengthOf(sendSpy.args, 0, 'The client has not yet sent the auth message');
                // set websocket readystate to 1 to allow a successful send message
                mockWebSocket.readyState = 1;
                mockWebSocket.emit('open');
                mockWebSocket.emit('message', {
                  data: (0, _stringify.default)({
                    id: createUuid(),
                    data: {
                      eventType: 'mercury.buffer_state'
                    }
                  })
                });
                // using lengthOf because notCalled doesn't allow the helpful
                // string assertion
                _testHelperChai.assert.lengthOf(sendSpy.args, 0, 'The client has not acked the buffer_state message');
                (0, _promiseTick.default)(1).then(function () {
                  _testHelperChai.assert.called(bufferStateSpy);
                  resolveTest();
                }).catch(rejectTest);
              });
              return mockWebSocket;
            };
          });
          return new _promise.default(function (resolve, reject) {
            resolveTest = resolve;
            rejectTest = reject;
            mobiusSocket.connect().catch(function () {});
          });
        });
      });
      describe('when a CloseEvent is received', function () {
        var events = [{
          code: 1000,
          reason: 'idle',
          action: 'close'
        }, {
          code: 1000,
          reason: 'done (forced)',
          action: 'close'
        }, {
          code: 1000,
          reason: 'pong not received',
          action: 'close'
        }, {
          code: 1000,
          reason: 'pong mismatch',
          action: 'close'
        }, {
          code: 1000,
          action: 'close'
        }, {
          code: 1003,
          action: 'close'
        }, {
          code: 1001,
          action: 'close'
        }, {
          code: 1005,
          action: 'reconnect'
        }, {
          code: 1006,
          action: 'reconnect'
        }, {
          code: 1011,
          action: 'reconnect'
        }, {
          code: 4000,
          action: 'replace'
        }, {
          action: 'close'
        }];
        events.forEach(function (def) {
          var action = def.action,
            reason = def.reason,
            code = def.code;
          var description;
          if (code && reason) {
            description = "with code `".concat(code, "` and reason `").concat(reason, "`");
          } else if (code) {
            description = "with code `".concat(code, "`");
          } else if (reason) {
            description = "with reason `".concat(reason, "`");
          }
          describe("when an event ".concat(description, " is received"), function () {
            it("takes the ".concat(action, " action"), function () {
              if (mobiusSocket.reconnect.restore) {
                mobiusSocket.reconnect.restore();
              }
              _sinon.default.spy(mobiusSocket, 'connect');
              var offlineSpy = _sinon.default.spy();
              var permanentSpy = _sinon.default.spy();
              var transientSpy = _sinon.default.spy();
              var replacedSpy = _sinon.default.spy();
              mobiusSocket.on('offline', offlineSpy);
              mobiusSocket.on('offline.permanent', permanentSpy);
              mobiusSocket.on('offline.transient', transientSpy);
              mobiusSocket.on('offline.replaced', replacedSpy);
              var promise = mobiusSocket.connect();
              mockWebSocket.open();
              return promise.then(function () {
                // Make sure mobiusSocket.connect has a call count of zero
                mobiusSocket.connect.resetHistory();
                mockWebSocket.emit('close', {
                  code: code,
                  reason: reason
                });
                return (0, _promiseTick.default)(1);
              }).then(function () {
                _testHelperChai.assert.called(offlineSpy);
                _testHelperChai.assert.calledWith(offlineSpy, {
                  code: code,
                  reason: reason
                });
                switch (action) {
                  case 'close':
                    _testHelperChai.assert.called(permanentSpy);
                    _testHelperChai.assert.notCalled(transientSpy);
                    _testHelperChai.assert.notCalled(replacedSpy);
                    break;
                  case 'reconnect':
                    _testHelperChai.assert.notCalled(permanentSpy);
                    _testHelperChai.assert.called(transientSpy);
                    _testHelperChai.assert.notCalled(replacedSpy);
                    break;
                  case 'replace':
                    _testHelperChai.assert.notCalled(permanentSpy);
                    _testHelperChai.assert.notCalled(transientSpy);
                    _testHelperChai.assert.called(replacedSpy);
                    break;
                  default:
                    (0, _testHelperChai.assert)(false, 'unreachable code reached');
                }
                _testHelperChai.assert.isFalse(mobiusSocket.connected, 'MobiusSocket is not connected');
                if (action === 'reconnect') {
                  _testHelperChai.assert.called(mobiusSocket.connect);
                  _testHelperChai.assert.calledWith(mobiusSocket.connect, mockWebSocket.url);
                  _testHelperChai.assert.isTrue(mobiusSocket.connecting, 'MobiusSocket is connecting');

                  // Block until reconnect completes so logs don't overlap
                  return mobiusSocket.connect();
                }
                _testHelperChai.assert.notCalled(mobiusSocket.connect);
                _testHelperChai.assert.isFalse(mobiusSocket.connecting, 'MobiusSocket is not connecting');
                return _promise.default.resolve();
              });
            });
          });
        });
      });
      describe('when a MessageEvent is received', function () {
        it('emits the MobiusSocket envelope', function () {
          var startSpy = _sinon.default.spy();
          var stopSpy = _sinon.default.spy();
          mobiusSocket.on('event:status.start_typing', startSpy);
          mobiusSocket.on('event:status.stop_typing', stopSpy);
          var promise = mobiusSocket.connect();
          mockWebSocket.open();
          return promise.then(function () {
            mockWebSocket.emit('message', {
              data: (0, _stringify.default)(statusStartTypingMessage)
            });
            return (0, _promiseTick.default)(1);
          }).then(function () {
            _testHelperChai.assert.calledOnce(startSpy);
            _testHelperChai.assert.notCalled(stopSpy);
            _testHelperChai.assert.calledWith(startSpy, statusStartTypingMessage);
          });
        });
        it("emits the MobiusSocket envelope named by the MobiusSocket event's eventType", function () {
          var startSpy = _sinon.default.spy();
          var stopSpy = _sinon.default.spy();
          mobiusSocket.on('event:status.start_typing', startSpy);
          mobiusSocket.on('event:status.stop_typing', stopSpy);
          var promise = mobiusSocket.connect();
          mockWebSocket.open();
          return promise.then(function () {
            mockWebSocket.emit('message', {
              data: (0, _stringify.default)(statusStartTypingMessage)
            });
            return (0, _promiseTick.default)(1);
          }).then(function () {
            _testHelperChai.assert.calledOnce(startSpy);
            _testHelperChai.assert.notCalled(stopSpy);
            _testHelperChai.assert.calledWith(startSpy, statusStartTypingMessage);
          });
        });
      });

      // Mobius does not use sequence numbers, so sequence-mismatch tests are not applicable
    });
  });

  /*
  // On mobiusSocket:
  online
  offline
  offline.transient
  offline.permanent
  offline.replaced
  event
  event:locus.participant_joined
  mockWebSocket.connection-failed
  mockWebSocket.sequence-mismatch
   // On webex:
  mobiusSocket.online
  mobiusSocket.offline
  mobiusSocket.offline.transient
  mobiusSocket.offline.permanent
  mobiusSocket.offline.replaced
  mobiusSocket.event
  mobiusSocket.event:locus.participant_joined
  mobiusSocket.mockWebSocket.connection-failed
  mobiusSocket.mockWebSocket.sequence-mismatch
   // TODO go through all it(`emits...`) and make sure corresponding tests are here
  */
});
//# sourceMappingURL=mobius-socket-events.test.js.map
