"use strict";

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

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

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

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

var _reactDom = _interopRequireDefault(require("react-dom"));

var _testUtils = _interopRequireDefault(require("react-dom/test-utils"));

var _immutable = require("immutable");

var _mostSubject = require("most-subject");

var _Pagination = require("@instructure/ui-pagination/lib/components/Pagination");

var _Spinner = _interopRequireDefault(require("../../spinner/Spinner"));

var _presenter = _interopRequireDefault(require("../presenter"));

var defer = function defer() {
  var resolve;
  var promise = new Promise(function (_resolve) {
    return resolve = _resolve;
  });
  return {
    promise: promise,
    resolve: resolve
  };
};

describe('PaginatedCollection', function () {
  var getPageStub = sinon.stub().returns(Promise.resolve({
    records: [1, 2, 3],
    totalPages: 4
  }));
  var mergeStub = sinon.stub();
  var setStub = sinon.stub();
  var renderRecordsStub = sinon.stub().returns('Your Collection Here');
  var testbed = createTestbed(_presenter.default, {
    renderRecords: renderRecordsStub,
    getPage: getPageStub,
    totalPages: 4,
    currentPage: 1,
    contents: (0, _immutable.Map)({
      1: (0, _immutable.List)([1, 2, 3])
    }),
    stateKey: 'MY_KEY',
    merge: mergeStub,
    set: setStub
  });
  beforeEach(function () {
    getPageStub.resetHistory();
    mergeStub.resetHistory();
    renderRecordsStub.resetHistory();
  });
  it('does not fetch current page when present', function () {
    testbed.render();
    expect(getPageStub).not.to.have.been.called();
  });
  it('fetches current page on mount', function () {
    testbed.render({
      contents: (0, _immutable.Map)()
    });
    expect(getPageStub).to.have.been.calledOnce();
    expect(getPageStub.firstCall.args[0]).to.eql(1);
  });
  it('resets to first page when stateKey changes', function () {
    testbed.render();
    testbed.render({
      stateKey: 'OTHER_KEY'
    });
    expect(mergeStub).to.have.been.calledWith('OTHER_KEY', {
      current: 1
    });
  });
  it('clears all pages and resets to first page when params change', function () {
    testbed.render();
    testbed.render({
      params: (0, _immutable.fromJS)({
        abc: 123
      })
    });
    expect(setStub).to.have.been.calledWith('MY_KEY', (0, _immutable.fromJS)({
      current: 1
    }));
  });
  it('updates correct page after consecutive rerenders',
  /*#__PURE__*/
  (0, _asyncToGenerator2.default)(
  /*#__PURE__*/
  _regenerator.default.mark(function _callee() {
    var page, loadFirstKey, loadSecondKey, getPage, mergeCalls, merge, calls;
    return _regenerator.default.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            page = function page(records) {
              return {
                records: records,
                totalPages: 1
              };
            };

            loadFirstKey = defer();
            loadSecondKey = defer();
            getPage = sinon.stub().onCall(0).returns(loadFirstKey.promise).onCall(1).returns(loadSecondKey.promise); // componentDidUpdate is not called for the first render, and it's what
            // loads pages.

            testbed.render({
              stateKey: 'FIRST_KEY'
            });
            mergeCalls = (0, _mostSubject.sync)();
            merge = sinon.fake(function () {
              for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
                args[_key] = arguments[_key];
              }

              return mergeCalls.next(args);
            }); // We need to wait for all four calls to merge(), some of which happen
            // asynchronously in response to promise resolutions. Begin collecting calls,
            // and convert that stream into a promise of all expected calls.

            calls = mergeCalls.take(4).reduce(function (acc, cur) {
              return [].concat((0, _toConsumableArray2.default)(acc), [cur]);
            }, []); // Render pages and then resolve loads in reverse order. We shouldn't be
            // updating the old collection page cache with data from the new state key

            testbed.render({
              contents: (0, _immutable.Map)(),
              getPage: getPage,
              merge: merge
            });
            testbed.render({
              contents: (0, _immutable.Map)(),
              getPage: getPage,
              merge: merge,
              stateKey: 'OTHER_KEY'
            });
            loadSecondKey.resolve(page([4, 5, 6]));
            loadFirstKey.resolve(page([1, 2, 3]));
            _context.t0 = expect;
            _context.next = 15;
            return calls;

          case 15:
            _context.t1 = _context.sent;
            _context.t2 = [['MY_KEY', {
              current: 1
            }], ['OTHER_KEY', {
              current: 1
            }], ['OTHER_KEY', {
              contents: {
                1: [4, 5, 6]
              },
              total: 1
            }], ['MY_KEY', {
              contents: {
                1: [1, 2, 3]
              },
              total: 1
            }]];
            (0, _context.t0)(_context.t1).to.deep.equal(_context.t2);

          case 18:
          case "end":
            return _context.stop();
        }
      }
    }, _callee);
  })));
  it('ignores updates from old parameters',
  /*#__PURE__*/
  (0, _asyncToGenerator2.default)(
  /*#__PURE__*/
  _regenerator.default.mark(function _callee2() {
    var page, loadFirstParams, loadSecondParams, getPage, merged, merge, params;
    return _regenerator.default.wrap(function _callee2$(_context2) {
      while (1) {
        switch (_context2.prev = _context2.next) {
          case 0:
            page = function page(records) {
              return {
                records: records,
                totalPages: 1
              };
            };

            loadFirstParams = defer();
            loadSecondParams = defer();
            getPage = sinon.stub().onCall(0).returns(loadFirstParams.promise).onCall(1).returns(loadSecondParams.promise); // componentDidUpdate is not called for the first render, and it's what
            // loads pages.

            testbed.render({});
            merged = defer();
            merge = sinon.fake(function () {
              for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
                args[_key2] = arguments[_key2];
              }

              return merged.resolve(args);
            }); // Render pages and then resolve loads in reverse order. We shouldn't be
            // updating the collection with stale data from fetching with old parameters

            params = (0, _immutable.fromJS)({
              x: 1
            });
            testbed.render({
              contents: (0, _immutable.Map)(),
              getPage: getPage,
              merge: merge
            });
            testbed.render({
              contents: (0, _immutable.Map)(),
              getPage: getPage,
              merge: merge,
              params: params
            });
            loadFirstParams.resolve(page([1, 2, 3]));
            loadSecondParams.resolve(page([4, 5, 6]));
            _context2.t0 = expect;
            _context2.next = 15;
            return merged.promise;

          case 15:
            _context2.t1 = _context2.sent;
            _context2.t2 = ['MY_KEY', {
              contents: {
                1: [4, 5, 6]
              },
              total: 1
            }];
            (0, _context2.t0)(_context2.t1).to.deep.equal(_context2.t2);

          case 18:
          case "end":
            return _context2.stop();
        }
      }
    }, _callee2);
  })));
  it('updates state when a page is fetched', function (done) {
    testbed.render({
      contents: (0, _immutable.Map)()
    });
    setTimeout(function () {
      // wait for the getPage promise to resolve
      expect(mergeStub).to.have.been.calledWith('MY_KEY', {
        contents: {
          1: [1, 2, 3]
        },
        total: 4
      });
      done();
    }, 0);
  });
  it('updates current page on page change', function () {
    testbed.render();
    var buttons = testbed.findChildrenByType(_Pagination.PaginationButton);
    var element = testbed.findChildrenByTagName('button', buttons[1])[0];

    _testUtils.default.Simulate.click(element);

    expect(mergeStub).to.have.been.calledOnce();
    expect(mergeStub.firstCall.args).to.eql(['MY_KEY', {
      current: 2
    }]);
  });
  it('fetches page on update', function () {
    testbed.render();
    testbed.render({
      currentPage: 2
    });
    expect(getPageStub).to.have.been.calledOnce();
    expect(getPageStub.firstCall.args[0]).to.eql(2);
  });
  it('does not fetch page when present', function () {
    var contents = (0, _immutable.Map)({
      1: (0, _immutable.List)([1, 2, 3]),
      2: (0, _immutable.List)([4, 5, 6])
    });
    testbed.render({
      contents: contents
    });
    testbed.render({
      contents: contents,
      currentPage: 2
    });
    expect(getPageStub.called).to.be.false();
  });
  it('renders pagination controls', function () {
    testbed.render();
    var buttons = testbed.findChildrenByType(_Pagination.PaginationButton);
    expect(buttons.map(function (b) {
      return _reactDom.default.findDOMNode(b).innerText.trim();
    })).to.eql(['1', '2', '3', '4']);
  });
  it('renders collection even when no records are present', function () {
    testbed.render({
      contents: (0, _immutable.Map)({
        1: (0, _immutable.List)()
      })
    });
    expect(renderRecordsStub).to.have.been.calledOnce();
    expect(renderRecordsStub.firstCall.args[0]).to.eql([]);
  });
  it('renders empty state when provided', function () {
    testbed.render({
      contents: (0, _immutable.Map)({
        1: (0, _immutable.List)()
      }),
      empty: 'nothing here'
    });
    expect(testbed.getText()).to.include('nothing here');
  });
  it('renders collection with records in vanilla JS', function () {
    testbed.render({
      empty: 'nothing here'
    });
    expect(renderRecordsStub).to.have.been.calledOnce();
    expect(renderRecordsStub.firstCall.args[0]).to.eql([1, 2, 3]);
    expect(testbed.getText()).to.not.include('nothing here');
    expect(testbed.getText()).to.include('Your Collection Here');
  });
  it('renders loading state when records are not present', function () {
    testbed.render({
      contents: (0, _immutable.Map)(),
      empty: 'nothing here'
    });
    expect(testbed.getText()).to.not.include('nothing here');
    expect(testbed.findChildrenByType(_Spinner.default).length).to.eq(1);
  });
});