"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));

var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));

var _chai = require("chai");

var _Fetcher = _interopRequireDefault(require("../Fetcher"));

var _fakeFetch = _interopRequireDefault(require("../../../../tests/util/fakeFetch"));

var _jsonify = require("../jsonify");

var _constants = require("@instructure/quiz-common/lib/constants");

var _callHelpers = require("../callHelpers");

describe('Fetcher', function () {
  var SOME_CALL_TYPE = 'SOME_CALL_TYPE';
  var dispatchStub = sinon.stub();
  var getStateStub = sinon.stub();
  var userToken = 'aFakeToken';
  var apiEndpoint = 'blob:/';
  useFakeFetching({
    dispatchStub: dispatchStub,
    getStateStub: getStateStub,
    userToken: userToken,
    apiEndpoint: apiEndpoint
  });

  var makeFetcher = function makeFetcher() {
    var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    var fetchParams = Object.assign({
      callType: SOME_CALL_TYPE
    }, opts);
    return new _Fetcher.default(fetchParams);
  };

  describe('appendQueryParams', function () {
    it('does not append params when not present', function () {
      var fetcher = new _Fetcher.default();
      (0, _chai.expect)(fetcher.appendQueryParams('instructure.biz/path', {})).to.eq('instructure.biz/path');
    });
    it('concatenates decamelized query string', function () {
      var fetcher = new _Fetcher.default();
      (0, _chai.expect)(fetcher.appendQueryParams('instructure.ru/path', {
        oneFish: 2,
        redFish: 'blue'
      })).to.eq('instructure.ru/path?one_fish=2&red_fish=blue');
    });
    it('serializes array params to query string', function () {
      var fetcher = new _Fetcher.default();
      (0, _chai.expect)(fetcher.appendQueryParams('instructure.ru/path', {
        ids: [1, 2, 3]
      })).to.eq('instructure.ru/path?ids[]=1&ids[]=2&ids[]=3');
    });
  });

  var handlesHeaderAppropriately = function handlesHeaderAppropriately(methodName) {
    it('adds the accessToken header by default', function () {
      var response = (0, _jsonify.jsoniblePromise)({
        some: 'response'
      });
      _Fetcher.default.fakeFetch = sinon.spy((0, _fakeFetch.default)(response));
      var fetcher = makeFetcher();
      fetcher[methodName]('some_url.net', {});
      (0, _chai.expect)(_Fetcher.default.fakeFetch.getCall(0).args[1].headers.Authorization).to.include(userToken);
    });
    it('adds custom header if given', function () {
      var response = (0, _jsonify.jsoniblePromise)({
        some: 'response'
      });
      _Fetcher.default.fakeFetch = sinon.spy((0, _fakeFetch.default)(response));
      var customHeaders = {
        foo: 'bar'
      };
      var fetcher = makeFetcher({
        headers: customHeaders
      });
      fetcher[methodName]('some_url.net', {});
      (0, _chai.expect)(_Fetcher.default.fakeFetch.getCall(0).args[1].headers).to.be.equal(customHeaders);
    });
  };

  var shouldJsonifyTheResponse = function shouldJsonifyTheResponse(action) {
    it('should jsonify the response',
    /*#__PURE__*/
    (0, _asyncToGenerator2.default)(
    /*#__PURE__*/
    _regenerator.default.mark(function _callee() {
      var response, jsonifySpy, fetcher, fetchPromise;
      return _regenerator.default.wrap(function _callee$(_context) {
        while (1) {
          switch (_context.prev = _context.next) {
            case 0:
              response = (0, _jsonify.jsoniblePromise)({
                some: 'response'
              });
              jsonifySpy = sinon.spy(response, 'json');
              _Fetcher.default.fakeFetch = (0, _fakeFetch.default)(response);
              fetcher = makeFetcher();
              fetchPromise = fetcher[action]('some_url.net', {});
              _context.next = 7;
              return (0, _chai.expect)(fetchPromise).to.be.fulfilled();

            case 7:
              (0, _chai.expect)(jsonifySpy).to.have.been.called();

            case 8:
            case "end":
              return _context.stop();
          }
        }
      }, _callee);
    })));
  };

  var shouldTakeArbitraryResponseHandler = function shouldTakeArbitraryResponseHandler(action) {
    it('should handle the response',
    /*#__PURE__*/
    (0, _asyncToGenerator2.default)(
    /*#__PURE__*/
    _regenerator.default.mark(function _callee2() {
      var response, responseSpy, fetcher, fetchPromise;
      return _regenerator.default.wrap(function _callee2$(_context2) {
        while (1) {
          switch (_context2.prev = _context2.next) {
            case 0:
              response = (0, _jsonify.jsoniblePromise)({
                some: 'response'
              });
              responseSpy = sinon.spy(response, 'json');
              _Fetcher.default.fakeFetch = (0, _fakeFetch.default)(response);
              fetcher = makeFetcher({
                asyncResponseHandler: responseSpy
              });
              fetchPromise = fetcher[action]('some_url.net', {});
              _context2.next = 7;
              return (0, _chai.expect)(fetchPromise).to.be.fulfilled();

            case 7:
              (0, _chai.expect)(responseSpy).to.have.been.called();

            case 8:
            case "end":
              return _context2.stop();
          }
        }
      }, _callee2);
    })));
  };

  var shouldAlertProperly = function shouldAlertProperly(action) {
    it('should dispatch a success alert if successful and successMessage given',
    /*#__PURE__*/
    (0, _asyncToGenerator2.default)(
    /*#__PURE__*/
    _regenerator.default.mark(function _callee3() {
      var response, exampleSuccessMessage, fetcher, fetchPromise, actionDispatched;
      return _regenerator.default.wrap(function _callee3$(_context3) {
        while (1) {
          switch (_context3.prev = _context3.next) {
            case 0:
              response = (0, _jsonify.jsoniblePromise)({
                some: 'response'
              });
              _Fetcher.default.fakeFetch = (0, _fakeFetch.default)(response);
              exampleSuccessMessage = 'Success: API call works!';
              fetcher = makeFetcher({
                successMessage: exampleSuccessMessage
              });
              fetchPromise = fetcher[action]('some_url.net', {});
              _context3.next = 7;
              return (0, _chai.expect)(fetchPromise).to.be.fulfilled();

            case 7:
              actionDispatched = dispatchStub.getCall(1).args[0];
              (0, _chai.expect)(actionDispatched.type).to.be.equal(_constants.ADD_ALERT);
              (0, _chai.expect)(actionDispatched.payload.message).to.be.equal(exampleSuccessMessage);
              (0, _chai.expect)(actionDispatched.payload.variant).to.be.equal('success');

            case 11:
            case "end":
              return _context3.stop();
          }
        }
      }, _callee3);
    })));
    it('should dispatch an error alert if error and json',
    /*#__PURE__*/
    (0, _asyncToGenerator2.default)(
    /*#__PURE__*/
    _regenerator.default.mark(function _callee4() {
      var response, fetcher, fetchPromise, actionDispatched;
      return _regenerator.default.wrap(function _callee4$(_context4) {
        while (1) {
          switch (_context4.prev = _context4.next) {
            case 0:
              response = (0, _jsonify.jsoniblePromise)({
                error: 'error'
              }, {
                status: 400
              });
              _Fetcher.default.fakeFetch = (0, _fakeFetch.default)(response);
              fetcher = makeFetcher();
              fetchPromise = fetcher[action]('some_url.net');
              _context4.next = 6;
              return (0, _chai.expect)(fetchPromise).to.be.rejected();

            case 6:
              actionDispatched = dispatchStub.getCall(1).args[0];
              (0, _chai.expect)(actionDispatched.type).to.be.equal(_constants.ADD_ALERT);
              (0, _chai.expect)(actionDispatched.payload.message).to.include('400 Error');
              (0, _chai.expect)(actionDispatched.payload.variant).to.be.equal('error');

            case 10:
            case "end":
              return _context4.stop();
          }
        }
      }, _callee4);
    })));
    it('should dispatch an error alert if error and text/html',
    /*#__PURE__*/
    (0, _asyncToGenerator2.default)(
    /*#__PURE__*/
    _regenerator.default.mark(function _callee5() {
      var response, fetcher, fetchPromise, actionDispatched;
      return _regenerator.default.wrap(function _callee5$(_context5) {
        while (1) {
          switch (_context5.prev = _context5.next) {
            case 0:
              response = (0, _jsonify.jsoniblePromise)({
                error: 'error'
              }, {
                status: 500,
                contentType: 'text/html; charset=utf-8'
              });
              _Fetcher.default.fakeFetch = (0, _fakeFetch.default)(response);
              fetcher = makeFetcher();
              fetchPromise = fetcher[action]('some_url.net', {});
              _context5.next = 6;
              return (0, _chai.expect)(fetchPromise).to.be.rejected();

            case 6:
              actionDispatched = dispatchStub.getCall(1).args[0];
              (0, _chai.expect)(actionDispatched.type).to.be.equal(_constants.ADD_ALERT);
              (0, _chai.expect)(actionDispatched.payload.message).to.include('500 Error');
              (0, _chai.expect)(actionDispatched.payload.variant).to.be.equal('error');

            case 10:
            case "end":
              return _context5.stop();
          }
        }
      }, _callee5);
    })));
    context('with a custom error handler', function () {
      context('when there is a non-success response code', function () {
        it('should dispatch a custom ErrorHandler',
        /*#__PURE__*/
        (0, _asyncToGenerator2.default)(
        /*#__PURE__*/
        _regenerator.default.mark(function _callee6() {
          var onErrorStub, response, fetcher, fetchPromise;
          return _regenerator.default.wrap(function _callee6$(_context6) {
            while (1) {
              switch (_context6.prev = _context6.next) {
                case 0:
                  onErrorStub = sinon.stub().returns({});
                  response = (0, _jsonify.jsoniblePromise)({
                    error: 'error'
                  }, {
                    status: 500,
                    contentType: 'text/html; charset=utf-8'
                  });
                  _Fetcher.default.fakeFetch = (0, _fakeFetch.default)(response);
                  fetcher = makeFetcher({
                    onError: onErrorStub
                  });
                  fetchPromise = fetcher[action]('some_url.net', {});
                  _context6.next = 7;
                  return (0, _chai.expect)(fetchPromise).to.be.rejected();

                case 7:
                  (0, _chai.expect)(onErrorStub).to.have.been.called();

                case 8:
                case "end":
                  return _context6.stop();
              }
            }
          }, _callee6);
        })));
      });
      context('when there is a request failure', function () {
        it('should dispatch a custom ErrorHandler',
        /*#__PURE__*/
        (0, _asyncToGenerator2.default)(
        /*#__PURE__*/
        _regenerator.default.mark(function _callee7() {
          var errorAction, onErrorStub, fetcher, fetchPromise;
          return _regenerator.default.wrap(function _callee7$(_context7) {
            while (1) {
              switch (_context7.prev = _context7.next) {
                case 0:
                  errorAction = {
                    type: 'EXAMPLE'
                  };
                  onErrorStub = sinon.stub().returns(errorAction);

                  _Fetcher.default.fakeFetch = function (url, params) {
                    return Promise.reject({
                      statusText: 'Failed to fetch'
                    });
                  };

                  fetcher = makeFetcher({
                    onError: onErrorStub,
                    callType: null
                  });
                  fetchPromise = fetcher[action]('some_url.net', {});
                  _context7.next = 7;
                  return (0, _chai.expect)(fetchPromise).to.be.rejected();

                case 7:
                  (0, _chai.expect)(onErrorStub).to.have.been.called();
                  (0, _chai.expect)(dispatchStub.calledWith(errorAction)).to.be.true();

                case 9:
                case "end":
                  return _context7.stop();
              }
            }
          }, _callee7);
        })));
      });
      context("when the custom error handler doesn't return an action", function () {
        it('should not call dispatch',
        /*#__PURE__*/
        (0, _asyncToGenerator2.default)(
        /*#__PURE__*/
        _regenerator.default.mark(function _callee8() {
          var onErrorStub, fetcher, fetchPromise;
          return _regenerator.default.wrap(function _callee8$(_context8) {
            while (1) {
              switch (_context8.prev = _context8.next) {
                case 0:
                  onErrorStub = sinon.stub();

                  _Fetcher.default.fakeFetch = function (url, params) {
                    return Promise.reject({
                      statusText: 'Not Found'
                    });
                  };

                  fetcher = makeFetcher({
                    onError: onErrorStub,
                    callType: null
                  });
                  fetchPromise = fetcher[action]('some_url.net', {});
                  _context8.next = 6;
                  return (0, _chai.expect)(fetchPromise).to.be.rejected();

                case 6:
                  (0, _chai.expect)(onErrorStub).to.have.been.called();
                  (0, _chai.expect)(dispatchStub).not.to.have.been.called();

                case 8:
                case "end":
                  return _context8.stop();
              }
            }
          }, _callee8);
        })));
      });
    });
  };

  var shouldMarkCallsProperly = function shouldMarkCallsProperly(methodName) {
    it('should mark the call type as started', function () {
      var response = (0, _jsonify.jsoniblePromise)({
        some: 'response'
      });
      _Fetcher.default.fakeFetch = (0, _fakeFetch.default)(response);
      var fetcher = makeFetcher();
      fetcher[methodName]('some_url.net', {});
      (0, _chai.expect)(dispatchStub.calledWith((0, _callHelpers.startCallFor)(SOME_CALL_TYPE))).to.be.true();
    });
    it('should mark the call type as a success if response is success',
    /*#__PURE__*/
    (0, _asyncToGenerator2.default)(
    /*#__PURE__*/
    _regenerator.default.mark(function _callee9() {
      var response, fetcher, fetchPromise;
      return _regenerator.default.wrap(function _callee9$(_context9) {
        while (1) {
          switch (_context9.prev = _context9.next) {
            case 0:
              response = (0, _jsonify.jsoniblePromise)({
                some: 'response'
              });
              _Fetcher.default.fakeFetch = (0, _fakeFetch.default)(response);
              fetcher = makeFetcher();
              fetchPromise = fetcher[methodName]('some_url.net', {});
              _context9.next = 6;
              return (0, _chai.expect)(fetchPromise).to.be.fulfilled();

            case 6:
              (0, _chai.expect)(dispatchStub.calledWith((0, _callHelpers.successCallFor)(SOME_CALL_TYPE))).to.be.true();

            case 7:
            case "end":
              return _context9.stop();
          }
        }
      }, _callee9);
    })));
    it('should mark the call type as an error if there was a request failure',
    /*#__PURE__*/
    (0, _asyncToGenerator2.default)(
    /*#__PURE__*/
    _regenerator.default.mark(function _callee10() {
      var fetcher, fetchPromise;
      return _regenerator.default.wrap(function _callee10$(_context10) {
        while (1) {
          switch (_context10.prev = _context10.next) {
            case 0:
              _Fetcher.default.fakeFetch = function (url, params) {
                return Promise.reject({
                  statusText: 'Failed to fetch'
                });
              };

              fetcher = makeFetcher();
              fetchPromise = fetcher[methodName]('some_url.net', {});
              _context10.next = 5;
              return (0, _chai.expect)(fetchPromise).to.be.rejected();

            case 5:
              (0, _chai.expect)(dispatchStub.calledWith((0, _callHelpers.errorCallFor)(SOME_CALL_TYPE))).to.be.true();

            case 6:
            case "end":
              return _context10.stop();
          }
        }
      }, _callee10);
    })));
    it('should mark the call type as an error if response is an error',
    /*#__PURE__*/
    (0, _asyncToGenerator2.default)(
    /*#__PURE__*/
    _regenerator.default.mark(function _callee11() {
      var response, fetcher, fetchPromise;
      return _regenerator.default.wrap(function _callee11$(_context11) {
        while (1) {
          switch (_context11.prev = _context11.next) {
            case 0:
              response = (0, _jsonify.jsoniblePromise)({
                some: 'response'
              }, {
                status: 500
              });
              _Fetcher.default.fakeFetch = (0, _fakeFetch.default)(response);
              fetcher = makeFetcher();
              fetchPromise = fetcher[methodName]('some_url.net', {});
              _context11.next = 6;
              return (0, _chai.expect)(fetchPromise).to.be.rejected();

            case 6:
              (0, _chai.expect)(dispatchStub.calledWith((0, _callHelpers.errorCallFor)(SOME_CALL_TYPE))).to.be.true();

            case 7:
            case "end":
              return _context11.stop();
          }
        }
      }, _callee11);
    })));
    it('should mark the callbacks complete if markCallbacksComplete is called', function () {
      var fetcher = makeFetcher();
      fetcher.markCallbacksComplete();
      (0, _chai.expect)(dispatchStub.calledWith((0, _callHelpers.callbacksCompleteForCall)(SOME_CALL_TYPE))).to.be.true();
    });
  };

  var makesProperCallOfType = function makesProperCallOfType(actionType, methodName) {
    var params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    it("should make a proper ".concat(actionType, " call"), function () {
      var response = (0, _jsonify.jsoniblePromise)({
        some: 'response'
      });
      _Fetcher.default.fakeFetch = sinon.spy((0, _fakeFetch.default)(response));
      var fetcher = makeFetcher();
      fetcher[methodName]('some_url.net', params);
      (0, _chai.expect)(_Fetcher.default.fakeFetch.getCall(0).args[1].method).to.be.equal(actionType);

      if (params.body) {
        (0, _chai.expect)(_Fetcher.default.fakeFetch.getCall(0).args[1].body).to.be.equal(params.body);
      }
    });
  };

  var handlesCustomEndpoint = function handlesCustomEndpoint(action) {
    it('should use the correct base endpoint', function () {
      _Fetcher.default.fakeFetch = sinon.stub().returns(Promise.resolve());
      var fetcher = makeFetcher();
      fetcher[action]('/path', {});
      (0, _chai.expect)(_Fetcher.default.fakeFetch.firstCall.args[0]).to.equal('blob://path');
    });
    it('should not use the base endpoint if an absolute url is given', function () {
      _Fetcher.default.fakeFetch = sinon.stub().returns(Promise.resolve());
      var fetcher = makeFetcher();
      fetcher[action]('http://minio.docker/path');
      (0, _chai.expect)(_Fetcher.default.fakeFetch.firstCall.args[0]).to.equal('http://minio.docker/path');
    });
  };

  describe('#get', function () {
    shouldJsonifyTheResponse('get');
    shouldTakeArbitraryResponseHandler('get');
    shouldAlertProperly('get');
    makesProperCallOfType('GET', 'get', 'some_url.net');
    shouldMarkCallsProperly('get');
    handlesHeaderAppropriately('get');
    handlesCustomEndpoint('get');
    it('appends params to URL', function () {
      _Fetcher.default.fakeFetch = sinon.stub().returns(Promise.resolve());
      var fetcher = makeFetcher({
        state: {
          config: {
            apiEndpoint: 'blob:/'
          }
        }
      });
      fetcher.get('/path', {
        one: 'two'
      });
      (0, _chai.expect)(_Fetcher.default.fakeFetch.firstCall.args[0]).to.equal('blob://path?one=two');
    });
  });
  describe('#post', function () {
    shouldJsonifyTheResponse('post');
    shouldTakeArbitraryResponseHandler('post');
    shouldAlertProperly('post');
    makesProperCallOfType('POST', 'post', {
      body: 'exampleBody'
    });
    shouldMarkCallsProperly('post');
    handlesHeaderAppropriately('post');
    handlesCustomEndpoint('post');
  });
  describe('#patch', function () {
    shouldJsonifyTheResponse('patch');
    shouldTakeArbitraryResponseHandler('patch');
    shouldAlertProperly('patch');
    makesProperCallOfType('PATCH', 'patch', {
      body: 'exampleBody'
    });
    shouldMarkCallsProperly('patch');
    handlesHeaderAppropriately('patch');
    handlesCustomEndpoint('patch');
  });
  describe('#put', function () {
    shouldJsonifyTheResponse('put');
    shouldTakeArbitraryResponseHandler('put');
    shouldAlertProperly('put');
    makesProperCallOfType('PUT', 'put', {
      body: 'exampleBody'
    });
    shouldMarkCallsProperly('put');
    handlesHeaderAppropriately('put');
    handlesCustomEndpoint('put');
  });
  describe('#delete', function () {
    it('should not jsonify the response',
    /*#__PURE__*/
    (0, _asyncToGenerator2.default)(
    /*#__PURE__*/
    _regenerator.default.mark(function _callee12() {
      var response, jsonifySpy, fetcher, fetchPromise;
      return _regenerator.default.wrap(function _callee12$(_context12) {
        while (1) {
          switch (_context12.prev = _context12.next) {
            case 0:
              response = (0, _jsonify.jsoniblePromise)({
                some: 'response'
              });
              jsonifySpy = sinon.spy(response, 'json');
              _Fetcher.default.fakeFetch = (0, _fakeFetch.default)(response);
              fetcher = makeFetcher();
              fetchPromise = fetcher.delete('some_url.net');
              _context12.next = 7;
              return (0, _chai.expect)(fetchPromise).to.be.fulfilled();

            case 7:
              (0, _chai.expect)(jsonifySpy).not.to.have.been.called();

            case 8:
            case "end":
              return _context12.stop();
          }
        }
      }, _callee12);
    })));
    shouldAlertProperly('delete');
    makesProperCallOfType('DELETE', 'delete', 'some_url.net');
    shouldMarkCallsProperly('delete');
    handlesHeaderAppropriately('delete');
    handlesCustomEndpoint('delete');
  });
});