"use strict";

var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");

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

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

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

var React = _interopRequireWildcard(require("react"));

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

var _TestUtils = require("../TestUtils");

var _WindowScroller = _interopRequireWildcard(require("./WindowScroller"));

function mockGetBoundingClientRectForHeader(_ref) {
  var _ref$documentOffset = _ref.documentOffset,
      documentOffset = _ref$documentOffset === void 0 ? 0 : _ref$documentOffset,
      height = _ref.height,
      width = _ref.width;
  // Mock the WindowScroller element and window separately
  // The only way to mock the former (before its created) is globally
  Element.prototype.getBoundingClientRect = jest.fn(function () {
    return {
      top: height,
      left: width
    };
  });
  document.documentElement.getBoundingClientRect = jest.fn(function () {
    return {
      top: documentOffset,
      left: documentOffset
    };
  });
}

function getMarkup() {
  var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
      headerElements = _ref2.headerElements,
      documentOffset = _ref2.documentOffset,
      renderFn = _ref2.renderFn,
      props = (0, _objectWithoutProperties2["default"])(_ref2, ["headerElements", "documentOffset", "renderFn"]);

  var windowScroller = React.createElement(_WindowScroller["default"], props, function (params) {
    return React.createElement("div", null, renderFn && renderFn(params));
  }); // JSDome doesn't implement a working getBoundingClientRect()
  // But WindowScroller requires it

  mockGetBoundingClientRectForHeader({
    documentOffset: documentOffset,
    height: headerElements ? headerElements.props.style.height : 0,
    width: headerElements ? headerElements.props.style.width : 0
  });

  if (headerElements) {
    return React.createElement("div", null, headerElements, windowScroller);
  } else {
    return windowScroller;
  }
}

function simulateWindowScroll(_ref3) {
  var _ref3$scrollX = _ref3.scrollX,
      scrollX = _ref3$scrollX === void 0 ? 0 : _ref3$scrollX,
      _ref3$scrollY = _ref3.scrollY,
      scrollY = _ref3$scrollY === void 0 ? 0 : _ref3$scrollY;
  document.body.style.height = '10000px';
  window.scrollX = scrollX;
  window.scrollY = scrollY;
  document.dispatchEvent(new window.Event('scroll', {
    bubbles: true
  }));
  document.body.style.height = '';
}

function simulateWindowResize(_ref4) {
  var _ref4$height = _ref4.height,
      height = _ref4$height === void 0 ? 0 : _ref4$height,
      _ref4$width = _ref4.width,
      width = _ref4$width === void 0 ? 0 : _ref4$width;
  window.innerHeight = height;
  window.innerWidth = width;
  document.dispatchEvent(new window.Event('resize', {
    bubbles: true
  }));
}

describe('WindowScroller', function () {
  // Set default window height and scroll position between tests
  beforeEach(function () {
    window.scrollY = 0;
    window.scrollX = 0;
    window.innerHeight = 500;
    window.innerWidth = 500;
  }); // Starts updating scrollTop only when the top position is reached

  it('should have correct top and left properties to be defined on :_positionFromTop and :_positionFromLeft', function () {
    var component = (0, _TestUtils.render)(getMarkup());
    var rendered = (0, _reactDom.findDOMNode)(component);

    var _rendered$getBounding = rendered.getBoundingClientRect(),
        top = _rendered$getBounding.top,
        left = _rendered$getBounding.left;

    expect(component._positionFromTop).toEqual(top);
    expect(component._positionFromLeft).toEqual(left);
  });
  it('should allow passing child element with registerChild of children function param', function () {
    var scrollElement = document.createElement('div');
    scrollElement.scrollTop = 100;
    scrollElement.scrollLeft = 150;

    scrollElement.getBoundingClientRect = function () {
      return {
        top: 200,
        left: 250
      };
    };

    var child = document.createElement('div');

    child.getBoundingClientRect = function () {
      return {
        top: 300,
        left: 350
      };
    };

    var renderFn = jest.fn();
    var component = (0, _TestUtils.render)(getMarkup({
      scrollElement: scrollElement,
      renderFn: renderFn
    }));
    renderFn.mock.calls[0][0].registerChild(child);
    expect(component._positionFromTop).toEqual(300 + 100 - 200);
    expect(component._positionFromLeft).toEqual(350 + 150 - 250);
  });
  it('should warn on passing non-element or not null', function () {
    var warnFn = jest.spyOn(console, 'warn');
    var renderFn = jest.fn();
    (0, _TestUtils.render)(getMarkup({
      renderFn: renderFn
    }));
    renderFn.mock.calls[0][0].registerChild(1);
    renderFn.mock.calls[0][0].registerChild(document.createElement('div'));
    renderFn.mock.calls[0][0].registerChild(null);
    expect(warnFn).toHaveBeenCalledTimes(1);
    warnFn.mockRestore();
  }); // Test edge-case reported in bvaughn/react-virtualized/pull/346

  it('should have correct top and left properties to be defined on :_positionFromTop and :_positionFromLeft if documentElement is scrolled', function () {
    _TestUtils.render.unmount(); // Simulate scrolled documentElement


    var component = (0, _TestUtils.render)(getMarkup({
      documentOffset: -100
    }));
    var rendered = (0, _reactDom.findDOMNode)(component);

    var _rendered$getBounding2 = rendered.getBoundingClientRect(),
        top = _rendered$getBounding2.top,
        left = _rendered$getBounding2.left;

    expect(component._positionFromTop).toEqual(top + 100);
    expect(component._positionFromLeft).toEqual(left + 100); // Reset override

    delete document.documentElement.getBoundingClientRect;
  });
  it('inherits the window height and passes it to child component', function () {
    var renderFn = jest.fn();
    var component = (0, _TestUtils.render)(getMarkup({
      renderFn: renderFn
    }));
    expect(component.state.height).toEqual(window.innerHeight);
    expect(component.state.height).toEqual(500);
    expect(renderFn).lastCalledWith(expect.objectContaining({
      height: 500
    }));
  });
  it('should restore pointerEvents on body after IS_SCROLLING_TIMEOUT', function _callee() {
    return _regenerator["default"].async(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            (0, _TestUtils.render)(getMarkup());
            document.body.style.pointerEvents = 'all';
            simulateWindowScroll({
              scrollY: 5000
            });
            expect(document.body.style.pointerEvents).toEqual('none');
            _context.next = 6;
            return _regenerator["default"].awrap(new Promise(function (resolve) {
              return setTimeout(resolve, _WindowScroller.IS_SCROLLING_TIMEOUT + 100);
            }));

          case 6:
            expect(document.body.style.pointerEvents).toEqual('all');

          case 7:
          case "end":
            return _context.stop();
        }
      }
    });
  });
  it('should restore pointerEvents on body after unmount', function () {
    (0, _TestUtils.render)(getMarkup());
    document.body.style.pointerEvents = 'all';
    simulateWindowScroll({
      scrollY: 5000
    });
    expect(document.body.style.pointerEvents).toEqual('none');

    _TestUtils.render.unmount();

    expect(document.body.style.pointerEvents).toEqual('all');
  });
  describe('onScroll', function () {
    it('should trigger callback when window scrolls', function _callee2() {
      var onScroll;
      return _regenerator["default"].async(function _callee2$(_context2) {
        while (1) {
          switch (_context2.prev = _context2.next) {
            case 0:
              onScroll = jest.fn();
              (0, _TestUtils.render)(getMarkup({
                onScroll: onScroll
              }));
              simulateWindowScroll({
                scrollY: 5000
              }); // Allow scrolling timeout to complete so that the component computes state

              _context2.next = 5;
              return _regenerator["default"].awrap(new Promise(function (resolve) {
                return setTimeout(resolve, 150);
              }));

            case 5:
              expect(onScroll).toHaveBeenCalledWith({
                scrollLeft: 0,
                scrollTop: 5000
              });
              simulateWindowScroll({
                scrollX: 2500,
                scrollY: 5000
              }); // Allow scrolling timeout to complete so that the component computes state

              _context2.next = 9;
              return _regenerator["default"].awrap(new Promise(function (resolve) {
                return setTimeout(resolve, 150);
              }));

            case 9:
              expect(onScroll).toHaveBeenCalledWith({
                scrollLeft: 2500,
                scrollTop: 5000
              });

            case 10:
            case "end":
              return _context2.stop();
          }
        }
      });
    });
    it('should update :scrollTop when window is scrolled', function _callee3() {
      var renderFn, component, componentScrollTop;
      return _regenerator["default"].async(function _callee3$(_context3) {
        while (1) {
          switch (_context3.prev = _context3.next) {
            case 0:
              renderFn = jest.fn();
              component = (0, _TestUtils.render)(getMarkup({
                renderFn: renderFn
              })); // Initial load of the component should have 0 scrollTop

              expect(renderFn).lastCalledWith(expect.objectContaining({
                scrollTop: 0
              }));
              simulateWindowScroll({
                scrollY: 5000
              }); // Allow scrolling timeout to complete so that the component computes state

              _context3.next = 6;
              return _regenerator["default"].awrap(new Promise(function (resolve) {
                return setTimeout(resolve, 150);
              }));

            case 6:
              componentScrollTop = window.scrollY - component._positionFromTop;
              expect(component.state.scrollTop).toEqual(componentScrollTop);
              expect(renderFn).lastCalledWith(expect.objectContaining({
                scrollTop: componentScrollTop
              }));

            case 9:
            case "end":
              return _context3.stop();
          }
        }
      });
    });
    it('should specify :isScrolling when scrolling and reset after scrolling', function _callee4() {
      var renderFn;
      return _regenerator["default"].async(function _callee4$(_context4) {
        while (1) {
          switch (_context4.prev = _context4.next) {
            case 0:
              renderFn = jest.fn();
              (0, _TestUtils.render)(getMarkup({
                renderFn: renderFn
              }));
              simulateWindowScroll({
                scrollY: 5000
              });
              expect(renderFn).lastCalledWith(expect.objectContaining({
                isScrolling: true
              }));
              _context4.next = 6;
              return _regenerator["default"].awrap(new Promise(function (resolve) {
                return setTimeout(resolve, 250);
              }));

            case 6:
              expect(renderFn).lastCalledWith(expect.objectContaining({
                isScrolling: false
              }));

            case 7:
            case "end":
              return _context4.stop();
          }
        }
      });
    });
    it('should support a custom :scrollingResetTimeInterval prop', function _callee5() {
      var renderFn;
      return _regenerator["default"].async(function _callee5$(_context5) {
        while (1) {
          switch (_context5.prev = _context5.next) {
            case 0:
              renderFn = jest.fn();
              (0, _TestUtils.render)(getMarkup({
                scrollingResetTimeInterval: 500,
                renderFn: renderFn
              }));
              expect(renderFn).lastCalledWith(expect.objectContaining({
                isScrolling: false
              }));
              simulateWindowScroll({
                scrollY: 5000
              });
              expect(renderFn).lastCalledWith(expect.objectContaining({
                isScrolling: true
              }));
              _context5.next = 7;
              return _regenerator["default"].awrap(new Promise(function (resolve) {
                return setTimeout(resolve, 100);
              }));

            case 7:
              expect(renderFn).lastCalledWith(expect.objectContaining({
                isScrolling: true
              }));
              _context5.next = 10;
              return _regenerator["default"].awrap(new Promise(function (resolve) {
                return setTimeout(resolve, 100);
              }));

            case 10:
              expect(renderFn).lastCalledWith(expect.objectContaining({
                isScrolling: true
              }));
              _context5.next = 13;
              return _regenerator["default"].awrap(new Promise(function (resolve) {
                return setTimeout(resolve, 400);
              }));

            case 13:
              expect(renderFn).lastCalledWith(expect.objectContaining({
                isScrolling: false
              }));

            case 14:
            case "end":
              return _context5.stop();
          }
        }
      });
    });
  });
  describe('onResize', function () {
    it('should trigger callback on init and when window resizes', function () {
      var resizeFn = jest.fn();
      (0, _TestUtils.render)(getMarkup({
        onResize: resizeFn
      }));
      simulateWindowResize({
        height: 1000,
        width: 1024
      });
      expect(resizeFn).toHaveBeenCalledTimes(1);
      expect(resizeFn).lastCalledWith({
        height: 1000,
        width: 1024
      });
    });
    it('should update height when window resizes', function () {
      var renderFn = jest.fn();
      var component = (0, _TestUtils.render)(getMarkup({
        renderFn: renderFn
      })); // Initial load of the component should have the same window height = 500

      expect(component.state.height).toEqual(window.innerHeight);
      expect(component.state.height).toEqual(500);
      expect(renderFn).lastCalledWith(expect.objectContaining({
        height: 500
      }));
      simulateWindowResize({
        height: 1000
      });
      expect(component.state.height).toEqual(window.innerHeight);
      expect(component.state.height).toEqual(1000);
      expect(renderFn).lastCalledWith(expect.objectContaining({
        height: 1000
      }));
    });
  });
  describe('updatePosition', function () {
    it('should calculate the initial offset from the top of the page when mounted', function () {
      var windowScroller;
      (0, _TestUtils.render)(getMarkup({
        headerElements: React.createElement("div", {
          style: {
            height: 100
          }
        }),
        ref: function ref(_ref5) {
          windowScroller = _ref5;
        }
      }));
      expect(windowScroller._positionFromTop).toBe(100);
    });
    it('should recalculate the offset from the top when the window resizes', function () {
      var windowScroller;
      (0, _TestUtils.render)(getMarkup({
        headerElements: React.createElement("div", {
          id: "header",
          style: {
            height: 100,
            width: 150
          }
        }),
        ref: function ref(_ref6) {
          windowScroller = _ref6;
        }
      }));
      expect(windowScroller._positionFromTop).toBe(100);
      expect(windowScroller._positionFromLeft).toBe(150);
      mockGetBoundingClientRectForHeader({
        height: 200,
        width: 300
      });
      expect(windowScroller._positionFromTop).toBe(100);
      expect(windowScroller._positionFromLeft).toBe(150);
      simulateWindowResize({
        height: 1000,
        width: 1000
      });
      expect(windowScroller._positionFromTop).toBe(200);
      expect(windowScroller._positionFromLeft).toBe(300);
    });
    it('should recalculate the offset from the top if called externally', function () {
      var windowScroller;
      (0, _TestUtils.render)(getMarkup({
        headerElements: React.createElement("div", {
          id: "header",
          style: {
            height: 100,
            width: 150
          }
        }),
        ref: function ref(_ref7) {
          windowScroller = _ref7;
        }
      }));
      expect(windowScroller._positionFromTop).toBe(100);
      expect(windowScroller._positionFromLeft).toBe(150);
      mockGetBoundingClientRectForHeader({
        height: 200,
        width: 300
      });
      windowScroller.updatePosition();
      expect(windowScroller._positionFromTop).toBe(200);
      expect(windowScroller._positionFromLeft).toBe(300);
    });
  });
  describe('when child scrolls', function () {
    var originalScrollTo;
    beforeEach(function () {
      originalScrollTo = window.scrollTo;

      window.scrollTo = function (scrollX, scrollY) {
        return simulateWindowScroll({
          scrollX: scrollX,
          scrollY: scrollY
        });
      };
    });
    afterEach(function () {
      window.scrollTo = originalScrollTo;

      _TestUtils.render.unmount();
    });
    it('should scroll the scrollElement (when it is window) the desired amount', function () {
      var renderFn = jest.fn();
      var windowScroller;
      (0, _TestUtils.render)(getMarkup({
        ref: function ref(_ref8) {
          windowScroller = _ref8;
        },
        renderFn: renderFn
      }));
      renderFn.mock.calls[0][0].onChildScroll({
        scrollTop: 200
      });
      expect(window.scrollY).toEqual(200 + windowScroller._positionFromTop);
    });
    it('should not scroll the scrollElement if trying to scroll to where we already are', function () {
      var renderFn = jest.fn();
      (0, _TestUtils.render)(getMarkup({
        renderFn: renderFn
      }));
      simulateWindowScroll({
        scrollY: 200
      });
      window.scrollTo = jest.fn();
      renderFn.mock.calls[0][0].onChildScroll({
        scrollTop: 200
      });
      expect(window.scrollTo).not.toHaveBeenCalled();
    });
    it('should scroll the scrollElement (when it is an element) the desired amount', function () {
      var windowScroller;
      var renderFn = jest.fn();
      var divEl = document.createElement('div');
      (0, _TestUtils.render)(getMarkup({
        ref: function ref(_ref9) {
          windowScroller = _ref9;
        },
        renderFn: renderFn,
        scrollElement: divEl
      }));
      renderFn.mock.calls[0][0].onChildScroll({
        scrollTop: 200
      });
      expect(divEl.scrollTop).toEqual(200 + windowScroller._positionFromTop);
    });
    it('should update own scrollTop', function () {
      var renderFn = jest.fn();
      (0, _TestUtils.render)(getMarkup({
        renderFn: renderFn
      }));
      renderFn.mock.calls[0][0].onChildScroll({
        scrollTop: 200
      });
      expect(renderFn).lastCalledWith(expect.objectContaining({
        scrollTop: 200
      }));
    });
  });
});