import _Object$keys from 'babel-runtime/core-js/object/keys';
import _Object$getPrototypeOf from 'babel-runtime/core-js/object/get-prototype-of';
import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
import _createClass from 'babel-runtime/helpers/createClass';
import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
import _inherits from 'babel-runtime/helpers/inherits';
import _regeneratorRuntime from 'babel-runtime/regenerator';
import _Promise from 'babel-runtime/core-js/promise';
import _asyncToGenerator from 'babel-runtime/helpers/asyncToGenerator';
import _extends from 'babel-runtime/helpers/extends';

var _this = this;

import React from 'react';
import { findDOMNode } from 'react-dom';
import { Simulate } from 'react-dom/test-utils';
import { render } from '../TestUtils';
import Grid, { DEFAULT_SCROLLING_RESET_TIME_INTERVAL } from './Grid';
import { CellMeasurer, CellMeasurerCache } from '../CellMeasurer';
import { SCROLL_DIRECTION_BACKWARD, SCROLL_DIRECTION_FORWARD } from './defaultOverscanIndicesGetter';
import { DEFAULT_MAX_SCROLL_SIZE } from './utils/ScalingCellSizeAndPositionManager';

var DEFAULT_COLUMN_WIDTH = 50;
var DEFAULT_HEIGHT = 100;
var DEFAULT_ROW_HEIGHT = 20;
var DEFAULT_WIDTH = 200;
var NUM_ROWS = 100;
var NUM_COLUMNS = 50;

function getScrollbarSize0() {
  return 0;
}

function getScrollbarSize20() {
  return 20;
}

describe('Grid', function () {
  function defaultCellRenderer(_ref) {
    var columnIndex = _ref.columnIndex,
        key = _ref.key,
        rowIndex = _ref.rowIndex,
        style = _ref.style;

    return React.createElement(
      'div',
      {
        className: 'gridItem',
        key: key,
        style: style
      },
      'row:' + rowIndex + ', column:' + columnIndex
    );
  }

  function simulateScroll(_ref2) {
    var grid = _ref2.grid,
        _ref2$scrollLeft = _ref2.scrollLeft,
        scrollLeft = _ref2$scrollLeft === undefined ? 0 : _ref2$scrollLeft,
        _ref2$scrollTop = _ref2.scrollTop,
        scrollTop = _ref2$scrollTop === undefined ? 0 : _ref2$scrollTop;

    var target = { scrollLeft: scrollLeft, scrollTop: scrollTop };
    grid._scrollingContainer = target; // HACK to work around _onScroll target check
    Simulate.scroll(findDOMNode(grid), { target: target });
  }

  function getMarkup() {
    var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

    return React.createElement(Grid, _extends({
      cellRenderer: defaultCellRenderer,
      columnCount: NUM_COLUMNS,
      columnWidth: DEFAULT_COLUMN_WIDTH,
      getScrollbarSize: getScrollbarSize0,
      height: DEFAULT_HEIGHT,
      overscanColumnCount: 0,
      overscanRowCount: 0,
      autoHeight: false,
      rowHeight: DEFAULT_ROW_HEIGHT,
      rowCount: NUM_ROWS,
      width: DEFAULT_WIDTH
    }, props));
  }

  describe('number of rendered children', function () {
    it('should render enough children to fill the available area', function () {
      var rendered = findDOMNode(render(getMarkup()));
      expect(rendered.querySelectorAll('.gridItem').length).toEqual(20); // 5 rows x 4 columns
    });

    it('should not render more rows than available if the area is not filled', function () {
      var rendered = findDOMNode(render(getMarkup({ rowCount: 2 })));
      expect(rendered.querySelectorAll('.gridItem').length).toEqual(8); // 2 rows x 4 columns
    });

    it('should not render more columns than available if the area is not filled', function () {
      var rendered = findDOMNode(render(getMarkup({ columnCount: 2 })));
      expect(rendered.querySelectorAll('.gridItem').length).toEqual(10); // 5 rows x 2 columns
    });

    // Small performance tweak added in 5.5.6
    it('should not render/parent cells that are null or false', function () {
      function cellRenderer(_ref3) {
        var columnIndex = _ref3.columnIndex,
            key = _ref3.key,
            rowIndex = _ref3.rowIndex,
            style = _ref3.style;

        if (columnIndex === 0) {
          return null;
        } else if (rowIndex === 0) {
          return false;
        } else {
          return React.createElement(
            'div',
            {
              className: 'cell',
              key: key,
              style: style
            },
            'row:' + rowIndex + ', column:' + columnIndex
          );
        }
      }
      var rendered = findDOMNode(render(getMarkup({
        columnCount: 3,
        overscanColumnCount: 0,
        overscanRowCount: 0,
        rowCount: 3,
        cellRenderer: cellRenderer
      })));
      expect(rendered.querySelectorAll('.cell').length).toEqual(4); // [1,1], [1,2], [2,1], and [2,2]
      expect(rendered.textContent).not.toContain('column:0');
      expect(rendered.textContent).not.toContain('row:0');
    });
  });

  describe('shows and hides scrollbars based on rendered content', function () {
    it('should set overflowX:hidden if columns fit within the available width and y-axis has no scrollbar', function () {
      var rendered = findDOMNode(render(getMarkup({
        columnCount: 4,
        getScrollbarSize: getScrollbarSize20,
        rowCount: 5
      })));
      expect(rendered.style.overflowX).toEqual('hidden');
    });

    it('should set overflowX:hidden if columns and y-axis scrollbar fit within the available width', function () {
      var rendered = findDOMNode(render(getMarkup({
        columnCount: 4,
        getScrollbarSize: getScrollbarSize20,
        width: 200 + getScrollbarSize20()
      })));
      expect(rendered.style.overflowX).toEqual('hidden');
    });

    it('should leave overflowX:auto if columns require more than the available width', function () {
      var rendered = findDOMNode(render(getMarkup({
        columnCount: 4,
        getScrollbarSize: getScrollbarSize20,
        width: 200 - 1,
        rowCount: 5
      })));
      expect(rendered.style.overflowX).not.toEqual('hidden');
    });

    it('should leave overflowX:auto if columns and y-axis scrollbar require more than the available width', function () {
      var rendered = findDOMNode(render(getMarkup({
        columnCount: 4,
        getScrollbarSize: getScrollbarSize20,
        width: 200 + getScrollbarSize20() - 1
      })));
      expect(rendered.style.overflowX).not.toEqual('hidden');
    });

    it('should set overflowY:hidden if rows fit within the available width and xaxis has no scrollbar', function () {
      var rendered = findDOMNode(render(getMarkup({
        getScrollbarSize: getScrollbarSize20,
        rowCount: 5,
        columnCount: 4
      })));
      expect(rendered.style.overflowY).toEqual('hidden');
    });

    it('should set overflowY:hidden if rows and x-axis scrollbar fit within the available width', function () {
      var rendered = findDOMNode(render(getMarkup({
        getScrollbarSize: getScrollbarSize20,
        rowCount: 5,
        height: 100 + getScrollbarSize20()
      })));
      expect(rendered.style.overflowY).toEqual('hidden');
    });

    it('should leave overflowY:auto if rows require more than the available width', function () {
      var rendered = findDOMNode(render(getMarkup({
        getScrollbarSize: getScrollbarSize20,
        rowCount: 5,
        height: 100 - 1,
        columnCount: 4
      })));
      expect(rendered.style.overflowY).not.toEqual('hidden');
    });

    it('should leave overflowY:auto if rows and x-axis scrollbar require more than the available width', function () {
      var rendered = findDOMNode(render(getMarkup({
        getScrollbarSize: getScrollbarSize20,
        rowCount: 5,
        height: 100 + getScrollbarSize20() - 1
      })));
      expect(rendered.style.overflowY).not.toEqual('hidden');
    });

    it('should accept styles that overwrite calculated ones', function () {
      var rendered = findDOMNode(render(getMarkup({
        columnCount: 1,
        getScrollbarSize: getScrollbarSize20,
        height: 1,
        rowCount: 1,
        style: {
          overflowY: 'visible',
          overflowX: 'visible'
        },
        width: 1
      })));
      expect(rendered.style.overflowY).toEqual('visible');
      expect(rendered.style.overflowX).toEqual('visible');
    });
  });

  /** Tests scrolling via initial props */
  describe(':scrollToColumn and :scrollToRow', function () {
    it('should scroll to the left', function () {
      var grid = render(getMarkup({ scrollToColumn: 0 }));
      expect(grid.state.scrollLeft).toEqual(0);
    });

    it('should scroll over to the middle', function () {
      var grid = render(getMarkup({ scrollToColumn: 24 }));
      // 100 columns * 50 item width = 5,000 total item width
      // 4 columns can be visible at a time and :scrollLeft is initially 0,
      // So the minimum amount of scrolling leaves the 25th item at the right (just scrolled into view).
      expect(grid.state.scrollLeft).toEqual(1050);
    });

    it('should scroll to the far right', function () {
      var grid = render(getMarkup({ scrollToColumn: 49 }));
      // 100 columns * 50 item width = 5,000 total item width
      // Target offset for the last item then is 5,000 - 200
      expect(grid.state.scrollLeft).toEqual(2300);
    });

    it('should scroll to the top', function () {
      var grid = render(getMarkup({ scrollToRow: 0 }));
      expect(grid.state.scrollTop).toEqual(0);
    });

    it('should scroll down to the middle', function () {
      var grid = render(getMarkup({ scrollToRow: 49 }));
      // 100 rows * 20 item height = 2,000 total item height
      // 5 rows can be visible at a time and :scrollTop is initially 0,
      // So the minimum amount of scrolling leaves the 50th item at the bottom (just scrolled into view).
      expect(grid.state.scrollTop).toEqual(900);
    });

    it('should scroll to the bottom', function () {
      var grid = render(getMarkup({ scrollToRow: 99 }));
      // 100 rows * 20 item height = 2,000 total item height
      // Target offset for the last item then is 2,000 - 100
      expect(grid.state.scrollTop).toEqual(1900);
    });

    it('should scroll to a row and column just added', function () {
      var grid = render(getMarkup());
      expect(grid.state.scrollLeft).toEqual(0);
      expect(grid.state.scrollTop).toEqual(0);
      grid = render(getMarkup({
        columnCount: NUM_COLUMNS + 1,
        rowCount: NUM_ROWS + 1,
        scrollToColumn: NUM_COLUMNS,
        scrollToRow: NUM_ROWS
      }));
      expect(grid.state.scrollLeft).toEqual(2350);
      expect(grid.state.scrollTop).toEqual(1920);
    });

    it('should scroll back to a newly-added cell without a change in prop', function () {
      var grid = render(getMarkup({
        columnCount: NUM_COLUMNS,
        rowCount: NUM_ROWS,
        scrollToColumn: NUM_COLUMNS,
        scrollToRow: NUM_ROWS
      }));
      grid = render(getMarkup({
        columnCount: NUM_COLUMNS + 1,
        rowCount: NUM_ROWS + 1,
        scrollToColumn: NUM_COLUMNS,
        scrollToRow: NUM_ROWS
      }));
      expect(grid.state.scrollLeft).toEqual(2350);
      expect(grid.state.scrollTop).toEqual(1920);
    });

    it('should scroll to the correct position for :scrollToAlignment "start"', function () {
      var grid = render(getMarkup({
        scrollToAlignment: 'start',
        scrollToColumn: 24,
        scrollToRow: 49
      }));
      // 100 columns * 50 item width = 5,000 total item width
      // 100 rows * 20 item height = 2,000 total item height
      // 4 columns and 5 rows can be visible at a time.
      // The minimum amount of scrolling leaves the specified cell in the bottom/right corner (just scrolled into view).
      // Since alignment is set to "start" we should scroll past this point until the cell is aligned top/left.
      expect(grid.state.scrollLeft).toEqual(1200);
      expect(grid.state.scrollTop).toEqual(980);
    });

    it('should scroll to the correct position for :scrollToAlignment "end"', function () {
      render(getMarkup({
        scrollToColumn: 99,
        scrollToRow: 99
      }));
      var grid = render(getMarkup({
        scrollToAlignment: 'end',
        scrollToColumn: 24,
        scrollToRow: 49
      }));
      // 100 columns * 50 item width = 5,000 total item width
      // 100 rows * 20 item height = 2,000 total item height
      // We first scroll past the specified cell and then back.
      // The minimum amount of scrolling then should leave the specified cell in the top/left corner (just scrolled into view).
      // Since alignment is set to "end" we should scroll past this point until the cell is aligned bottom/right.
      expect(grid.state.scrollLeft).toEqual(1050);
      expect(grid.state.scrollTop).toEqual(900);
    });

    it('should scroll to the correct position for :scrollToAlignment "center"', function () {
      render(getMarkup({
        scrollToColumn: 99,
        scrollToRow: 99
      }));
      var grid = render(getMarkup({
        scrollToAlignment: 'center',
        scrollToColumn: 24,
        scrollToRow: 49
      }));
      // 100 columns * 50 item width = 5,000 total item width
      // Viewport width is 200
      // Column 24 starts at 1,200, center point at 1,225, so...
      expect(grid.state.scrollLeft).toEqual(1125);
      // 100 rows * 20 item height = 2,000 total item height
      // Viewport height is 100
      // Row 49 starts at 980, center point at 990, so...
      expect(grid.state.scrollTop).toEqual(940);
    });

    // Tests issue #691
    it('should set the correct :scrollLeft after height increases from 0', function () {
      render.unmount();
      expect(findDOMNode(render(getMarkup({
        height: 0,
        scrollToColumn: 24
      }))).scrollLeft || 0).toEqual(0);
      expect(findDOMNode(render(getMarkup({
        height: 100,
        scrollToColumn: 24
      }))).scrollLeft).toEqual(1050);
    });

    // Tests issue #691
    it('should set the correct :scrollTop after width increases from 0', function () {
      render.unmount();
      expect(findDOMNode(render(getMarkup({
        scrollToRow: 49,
        width: 0
      }))).scrollTop || 0).toEqual(0);
      expect(findDOMNode(render(getMarkup({
        scrollToRow: 49,
        width: 100
      }))).scrollTop).toEqual(900);
    });

    // Tests issue #218
    it('should set the correct :scrollTop after row and column counts increase from 0', function () {
      var expectedScrollTop = 100 * DEFAULT_ROW_HEIGHT - DEFAULT_HEIGHT + DEFAULT_ROW_HEIGHT;
      render(getMarkup({
        columnCount: 0,
        rowCount: 150,
        scrollToRow: 100
      }));
      expect(findDOMNode(render(getMarkup({
        columnCount: 150,
        rowCount: 150,
        scrollToRow: 100
      }))).scrollTop).toEqual(expectedScrollTop);
    });

    it('should support scrollToCell() public method', function () {
      var grid = render(getMarkup());
      expect(grid.state.scrollLeft).toEqual(0);
      expect(grid.state.scrollTop).toEqual(0);
      grid.scrollToCell({
        columnIndex: 24,
        rowIndex: 49
      });
      // 100 columns * 50 item width = 5,000 total item width
      // 4 columns can be visible at a time and :scrollLeft is initially 0,
      // So the minimum amount of scrolling leaves the 25th item at the right (just scrolled into view).
      expect(grid.state.scrollLeft).toEqual(1050);
      // 100 rows * 20 item height = 2,000 total item height
      // 5 rows can be visible at a time and :scrollTop is initially 0,
      // So the minimum amount of scrolling leaves the 50th item at the bottom (just scrolled into view).
      expect(grid.state.scrollTop).toEqual(900);

      // Change column without affecting row
      grid.scrollToCell({
        columnIndex: 49
      });
      expect(grid.state.scrollLeft).toEqual(2300);
      expect(grid.state.scrollTop).toEqual(900);

      // Change row without affecting column
      grid.scrollToCell({
        rowIndex: 99
      });
      expect(grid.state.scrollLeft).toEqual(2300);
      expect(grid.state.scrollTop).toEqual(1900);
    });

    it('should support scrollToPosition() public method', function () {
      var grid = render(getMarkup());
      expect(grid.state.scrollLeft).toEqual(0);
      expect(grid.state.scrollTop).toEqual(0);

      grid.scrollToPosition({
        scrollLeft: 50,
        scrollTop: 100
      });
      expect(grid.state.scrollLeft).toEqual(50);
      expect(grid.state.scrollTop).toEqual(100);

      // Change column without affecting row
      grid.scrollToPosition({
        scrollLeft: 25
      });
      expect(grid.state.scrollLeft).toEqual(25);
      expect(grid.state.scrollTop).toEqual(100);

      // Change row without affecting column
      grid.scrollToPosition({
        scrollTop: 50
      });
      expect(grid.state.scrollLeft).toEqual(25);
      expect(grid.state.scrollTop).toEqual(50);
    });

    it('should support handleScrollEvent() public method', function () {
      var grid = render(getMarkup());
      expect(grid.state.scrollLeft).toEqual(0);
      expect(grid.state.scrollTop).toEqual(0);

      grid.handleScrollEvent({
        scrollLeft: 50,
        scrollTop: 100
      });
      expect(grid.state.isScrolling).toEqual(true);
      expect(grid.state.scrollLeft).toEqual(50);
      expect(grid.state.scrollTop).toEqual(100);
    });

    it('should support getOffsetForCell() public method', function () {
      var grid = render(getMarkup());

      var _grid$getOffsetForCel = grid.getOffsetForCell({
        columnIndex: 24,
        rowIndex: 49
      }),
          scrollLeft = _grid$getOffsetForCel.scrollLeft,
          scrollTop = _grid$getOffsetForCel.scrollTop;
      // 100 columns * 50 item width = 5,000 total item width
      // 4 columns can be visible at a time and :scrollLeft is initially 0,
      // So the minimum amount of scrolling leaves the 25th item at the right (just scrolled into view).


      expect(scrollLeft).toEqual(1050);
      // 100 rows * 20 item height = 2,000 total item height
      // 5 rows can be visible at a time and :scrollTop is initially 0,
      // So the minimum amount of scrolling leaves the 50th item at the bottom (just scrolled into view).
      expect(scrollTop).toEqual(900);
    });

    // See issue #565
    it('should update scroll position to account for changed cell sizes within a function prop wrapper', function () {
      var _rowHeight = 20;

      var props = {
        height: 100,
        rowCount: 100,
        rowHeight: function rowHeight(_ref4) {
          var index = _ref4.index;
          return index === 99 ? _rowHeight : 20;
        },
        scrollToRow: 99
      };

      var grid = render(getMarkup(props));
      var node = findDOMNode(grid);

      expect(node.scrollTop).toBe(1900);

      _rowHeight = 40;
      grid.recomputeGridSize({
        rowIndex: 99
      });

      expect(node.scrollTop).toBe(1920);
    });

    it('should restore scroll offset for column when row count increases from 0 (and vice versa)', function () {
      var props = {
        columnWidth: 50,
        columnCount: 100,
        height: 100,
        rowCount: 100,
        rowHeight: 20,
        scrollToColumn: 50,
        scrollToRow: 50,
        width: 100
      };
      var grid = render(getMarkup(props));
      expect(grid.state.scrollLeft).toEqual(2450);
      expect(grid.state.scrollTop).toEqual(920);
      render(getMarkup(_extends({}, props, {
        columnCount: 0
      })));
      expect(grid.state.scrollLeft).toEqual(0);
      expect(grid.state.scrollTop).toEqual(0);
      render(getMarkup(props));
      expect(grid.state.scrollLeft).toEqual(2450);
      expect(grid.state.scrollTop).toEqual(920);
      render(getMarkup(_extends({}, props, {
        rowCount: 0
      })));
      expect(grid.state.scrollLeft).toEqual(0);
      expect(grid.state.scrollTop).toEqual(0);
      render(getMarkup(props));
      expect(grid.state.scrollLeft).toEqual(2450);
      expect(grid.state.scrollTop).toEqual(920);
    });

    it('should take scrollbar size into account when aligning cells', function () {
      var grid = render(getMarkup({
        columnWidth: 50,
        columnCount: 100,
        getScrollbarSize: getScrollbarSize20,
        height: 100,
        rowCount: 100,
        rowHeight: 20,
        scrollToColumn: 50,
        scrollToRow: 50,
        width: 100
      }));

      expect(grid.state.scrollLeft).toEqual(2450 + getScrollbarSize20());
      expect(grid.state.scrollTop).toEqual(920 + getScrollbarSize20());
    });
  });

  describe('property updates', function () {
    it('should update :scrollToColumn position when :columnWidth changes', function () {
      var grid = findDOMNode(render(getMarkup({ scrollToColumn: 25 })));
      expect(grid.textContent).toContain('column:25');
      // Making columns taller pushes name off/beyond the scrolled area
      grid = findDOMNode(render(getMarkup({ scrollToColumn: 25, columnWidth: 20 })));
      expect(grid.textContent).toContain('column:25');
    });

    it('should update :scrollToRow position when :rowHeight changes', function () {
      var grid = findDOMNode(render(getMarkup({ scrollToRow: 50 })));
      expect(grid.textContent).toContain('row:50');
      // Making rows taller pushes name off/beyond the scrolled area
      grid = findDOMNode(render(getMarkup({ scrollToRow: 50, rowHeight: 20 })));
      expect(grid.textContent).toContain('row:50');
    });

    it('should update :scrollToColumn position when :width changes', function () {
      var grid = findDOMNode(render(getMarkup({ scrollToColumn: 25 })));
      expect(grid.textContent).toContain('column:25');
      // Making the grid narrower leaves only room for 1 item
      grid = findDOMNode(render(getMarkup({ scrollToColumn: 25, width: 50 })));
      expect(grid.textContent).toContain('column:25');
    });

    it('should update :scrollToRow position when :height changes', function () {
      var grid = findDOMNode(render(getMarkup({ scrollToRow: 50 })));
      expect(grid.textContent).toContain('row:50');
      // Making the grid shorter leaves only room for 1 item
      grid = findDOMNode(render(getMarkup({ scrollToRow: 50, height: 20 })));
      expect(grid.textContent).toContain('row:50');
    });

    it('should update :scrollToColumn position when :scrollToColumn changes', function () {
      var grid = findDOMNode(render(getMarkup()));
      expect(grid.textContent).not.toContain('column:25');
      grid = findDOMNode(render(getMarkup({ scrollToColumn: 25 })));
      expect(grid.textContent).toContain('column:25');
    });

    it('should update :scrollToRow position when :scrollToRow changes', function () {
      var grid = findDOMNode(render(getMarkup()));
      expect(grid.textContent).not.toContain('row:50');
      grid = findDOMNode(render(getMarkup({ scrollToRow: 50 })));
      expect(grid.textContent).toContain('row:50');
    });

    it('should update scroll position if size shrinks smaller than the current scroll', function () {
      var grid = findDOMNode(render(getMarkup({ scrollToColumn: 250 })));
      grid = findDOMNode(render(getMarkup()));
      grid = findDOMNode(render(getMarkup({ scrollToColumn: 250, columnCount: 10 })));
      expect(grid.textContent).toContain('column:9');
    });

    it('should update scroll position if size shrinks smaller than the current scroll', function () {
      var grid = findDOMNode(render(getMarkup({ scrollToRow: 500 })));
      grid = findDOMNode(render(getMarkup()));
      grid = findDOMNode(render(getMarkup({ scrollToRow: 500, rowCount: 10 })));
      expect(grid.textContent).toContain('row:9');
    });
  });

  describe('noContentRenderer', function () {
    it('should call :noContentRenderer if :columnCount is 0', function () {
      var list = findDOMNode(render(getMarkup({
        noContentRenderer: function noContentRenderer() {
          return React.createElement(
            'div',
            null,
            'No data'
          );
        },
        columnCount: 0
      })));
      expect(list.textContent).toEqual('No data');
    });

    it('should call :noContentRenderer if :rowCount is 0', function () {
      var list = findDOMNode(render(getMarkup({
        noContentRenderer: function noContentRenderer() {
          return React.createElement(
            'div',
            null,
            'No data'
          );
        },
        rowCount: 0
      })));
      expect(list.textContent).toEqual('No data');
    });

    // Sanity check for bvaughn/react-virtualized/pull/348
    it('should render an empty body if :rowCount or :columnCount changes to 0', function () {
      function noContentRenderer() {
        return React.createElement(
          'div',
          null,
          'No data'
        );
      }

      var list = findDOMNode(render(getMarkup({
        noContentRenderer: noContentRenderer
      })));
      expect(list.textContent).not.toEqual('No data');

      list = findDOMNode(render(getMarkup({
        noContentRenderer: noContentRenderer,
        rowCount: 0
      })));
      expect(list.textContent).toEqual('No data');

      list = findDOMNode(render(getMarkup({
        noContentRenderer: noContentRenderer
      })));
      expect(list.textContent).not.toEqual('No data');

      list = findDOMNode(render(getMarkup({
        columnCount: 0,
        noContentRenderer: noContentRenderer
      })));
      expect(list.textContent).toEqual('No data');
    });

    it('should render an empty body if :columnCount is 0 and there is no :noContentRenderer', function () {
      var list = findDOMNode(render(getMarkup({
        columnCount: 0
      })));
      expect(list.textContent).toEqual('');
    });

    it('should render an empty body if :rowCount is 0 and there is no :noContentRenderer', function () {
      var list = findDOMNode(render(getMarkup({
        rowCount: 0
      })));
      expect(list.textContent).toEqual('');
    });

    it('should render an empty body there is a :noContentRenderer but :height or :width are 0', function () {
      var list = findDOMNode(render(getMarkup({
        height: 0,
        noContentRenderer: function noContentRenderer() {
          return React.createElement(
            'div',
            null,
            'No data'
          );
        }
      })));
      expect(list.textContent).toEqual('');
      list = findDOMNode(render(getMarkup({
        noContentRenderer: function noContentRenderer() {
          return React.createElement(
            'div',
            null,
            'No data'
          );
        },
        width: 0
      })));
      expect(list.textContent).toEqual('');
    });
  });

  describe('onSectionRendered', function () {
    it('should call :onSectionRendered if at least one cell is rendered', function () {
      var columnStartIndex = void 0,
          columnStopIndex = void 0,
          rowStartIndex = void 0,
          rowStopIndex = void 0;
      render(getMarkup({
        onSectionRendered: function onSectionRendered(params) {
          var _params;

          return _params = params, columnStartIndex = _params.columnStartIndex, columnStopIndex = _params.columnStopIndex, rowStartIndex = _params.rowStartIndex, rowStopIndex = _params.rowStopIndex, _params;
        }
      }));
      expect(columnStartIndex).toEqual(0);
      expect(columnStopIndex).toEqual(3);
      expect(rowStartIndex).toEqual(0);
      expect(rowStopIndex).toEqual(4);
    });

    it('should not call :onSectionRendered unless the column or row start or stop indices have changed', function () {
      var numCalls = 0;
      var columnStartIndex = void 0,
          columnStopIndex = void 0,
          rowStartIndex = void 0,
          rowStopIndex = void 0;
      var onSectionRendered = function onSectionRendered(params) {
        columnStartIndex = params.columnStartIndex;
        columnStopIndex = params.columnStopIndex;
        rowStartIndex = params.rowStartIndex;
        rowStopIndex = params.rowStopIndex;
        numCalls++;
      };
      render(getMarkup({ onSectionRendered: onSectionRendered }));
      expect(numCalls).toEqual(1);
      expect(columnStartIndex).toEqual(0);
      expect(columnStopIndex).toEqual(3);
      expect(rowStartIndex).toEqual(0);
      expect(rowStopIndex).toEqual(4);
      render(getMarkup({ onSectionRendered: onSectionRendered }));
      expect(numCalls).toEqual(1);
      expect(columnStartIndex).toEqual(0);
      expect(columnStopIndex).toEqual(3);
      expect(rowStartIndex).toEqual(0);
      expect(rowStopIndex).toEqual(4);
    });

    it('should call :onSectionRendered if the row or column start or stop indices have changed', function () {
      var numCalls = 0;
      var columnStartIndex = void 0,
          columnStopIndex = void 0,
          rowStartIndex = void 0,
          rowStopIndex = void 0;
      var onSectionRendered = function onSectionRendered(params) {
        columnStartIndex = params.columnStartIndex;
        columnStopIndex = params.columnStopIndex;
        rowStartIndex = params.rowStartIndex;
        rowStopIndex = params.rowStopIndex;
        numCalls++;
      };
      render(getMarkup({ onSectionRendered: onSectionRendered }));
      expect(columnStartIndex).toEqual(0);
      expect(columnStopIndex).toEqual(3);
      expect(rowStartIndex).toEqual(0);
      expect(rowStopIndex).toEqual(4);
      render(getMarkup({
        height: 50,
        onSectionRendered: onSectionRendered
      }));
      expect(numCalls).toEqual(2);
      expect(columnStartIndex).toEqual(0);
      expect(columnStopIndex).toEqual(3);
      expect(rowStartIndex).toEqual(0);
      expect(rowStopIndex).toEqual(2);
      render(getMarkup({
        height: 50,
        onSectionRendered: onSectionRendered,
        width: 100
      }));
      expect(numCalls).toEqual(3);
      expect(columnStartIndex).toEqual(0);
      expect(columnStopIndex).toEqual(1);
      expect(rowStartIndex).toEqual(0);
      expect(rowStopIndex).toEqual(2);
    });

    it('should not call :onSectionRendered if no cells are rendered', function () {
      var numCalls = 0;
      render(getMarkup({
        height: 0,
        onSectionRendered: function onSectionRendered(params) {
          return numCalls++;
        }
      }));
      expect(numCalls).toEqual(0);
    });
  });

  describe(':scrollLeft and :scrollTop properties', function () {
    it('should render correctly when an initial :scrollLeft and :scrollTop properties are specified', function () {
      var columnStartIndex = void 0,
          columnStopIndex = void 0,
          rowStartIndex = void 0,
          rowStopIndex = void 0;
      findDOMNode(render(getMarkup({
        onSectionRendered: function onSectionRendered(params) {
          var _params2;

          return _params2 = params, columnStartIndex = _params2.columnStartIndex, columnStopIndex = _params2.columnStopIndex, rowStartIndex = _params2.rowStartIndex, rowStopIndex = _params2.rowStopIndex, _params2;
        },
        scrollLeft: 250,
        scrollTop: 100
      })));
      expect(rowStartIndex).toEqual(5);
      expect(rowStopIndex).toEqual(9);
      expect(columnStartIndex).toEqual(5);
      expect(columnStopIndex).toEqual(8);
    });

    it('should render correctly when :scrollLeft and :scrollTop properties are updated', function () {
      var columnStartIndex = void 0,
          columnStopIndex = void 0,
          rowStartIndex = void 0,
          rowStopIndex = void 0;

      render(getMarkup({
        onSectionRendered: function onSectionRendered(params) {
          var _params3;

          return _params3 = params, columnStartIndex = _params3.columnStartIndex, columnStopIndex = _params3.columnStopIndex, rowStartIndex = _params3.rowStartIndex, rowStopIndex = _params3.rowStopIndex, _params3;
        }
      }));
      expect(rowStartIndex).toEqual(0);
      expect(rowStopIndex).toEqual(4);
      expect(columnStartIndex).toEqual(0);
      expect(columnStopIndex).toEqual(3);

      render(getMarkup({
        onSectionRendered: function onSectionRendered(params) {
          var _params4;

          return _params4 = params, columnStartIndex = _params4.columnStartIndex, columnStopIndex = _params4.columnStopIndex, rowStartIndex = _params4.rowStartIndex, rowStopIndex = _params4.rowStopIndex, _params4;
        },
        scrollLeft: 250,
        scrollTop: 100
      }));
      expect(rowStartIndex).toEqual(5);
      expect(rowStopIndex).toEqual(9);
      expect(columnStartIndex).toEqual(5);
      expect(columnStopIndex).toEqual(8);
    });
  });

  describe('styles, classNames, and ids', function () {
    it('should use the expected global CSS classNames', function () {
      var rendered = findDOMNode(render(getMarkup()));
      expect(rendered.className).toEqual('ReactVirtualized__Grid');
    });

    it('should use a custom :className if specified', function () {
      var rendered = findDOMNode(render(getMarkup({ className: 'foo' })));
      expect(rendered.className).toContain('foo');
    });

    it('should use a custom :id if specified', function () {
      var rendered = findDOMNode(render(getMarkup({ id: 'bar' })));
      expect(rendered.getAttribute('id')).toEqual('bar');
    });

    it('should use a custom :style if specified', function () {
      var style = { backgroundColor: 'red' };
      var rendered = findDOMNode(render(getMarkup({ style: style })));
      expect(rendered.style.backgroundColor).toEqual('red');
    });

    it('should use a custom :containerStyle if specified', function () {
      var containerStyle = { backgroundColor: 'red' };
      var rendered = findDOMNode(render(getMarkup({ containerStyle: containerStyle })));
      expect(rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer').style.backgroundColor).toEqual('red');
    });
  });

  describe('onScroll', function () {
    it('should trigger callback when component is mounted', function () {
      var onScrollCalls = [];
      render(getMarkup({
        onScroll: function onScroll(params) {
          return onScrollCalls.push(params);
        },
        scrollLeft: 50,
        scrollTop: 100
      }));
      expect(onScrollCalls).toEqual([{
        clientHeight: 100,
        clientWidth: 200,
        scrollHeight: 2000,
        scrollLeft: 50,
        scrollTop: 100,
        scrollWidth: 2500
      }]);
    });

    it('should trigger callback when component scrolls horizontally', function () {
      var onScrollCalls = [];
      var grid = render(getMarkup({
        onScroll: function onScroll(params) {
          return onScrollCalls.push(params);
        }
      }));
      simulateScroll({
        grid: grid,
        scrollLeft: 100,
        scrollTop: 0
      });
      expect(onScrollCalls.length).toEqual(2);
      expect(onScrollCalls[1]).toEqual({
        clientHeight: 100,
        clientWidth: 200,
        scrollHeight: 2000,
        scrollLeft: 100,
        scrollTop: 0,
        scrollWidth: 2500
      });
    });

    it('should trigger callback when component scrolls vertically', function () {
      var onScrollCalls = [];
      var grid = render(getMarkup({
        onScroll: function onScroll(params) {
          return onScrollCalls.push(params);
        }
      }));
      simulateScroll({
        grid: grid,
        scrollLeft: 0,
        scrollTop: 100
      });
      expect(onScrollCalls.length).toEqual(2);
      expect(onScrollCalls[1]).toEqual({
        clientHeight: 100,
        clientWidth: 200,
        scrollHeight: 2000,
        scrollLeft: 0,
        scrollTop: 100,
        scrollWidth: 2500
      });
    });

    it('should trigger callback with scrollLeft of 0 when total columns width is less than width', function () {
      var onScrollCalls = [];
      var grid = render(getMarkup({
        columnCount: 1,
        columnWidth: 50,
        onScroll: function onScroll(params) {
          return onScrollCalls.push(params);
        },
        scrollLeft: 0,
        scrollTop: 10,
        width: 200
      }));
      simulateScroll({
        grid: grid,
        scrollLeft: 0,
        scrollTop: 0
      });
      expect(onScrollCalls.length).toEqual(2);
      expect(onScrollCalls[1]).toEqual({
        clientHeight: 100,
        clientWidth: 200,
        scrollHeight: 2000,
        scrollLeft: 0,
        scrollTop: 0,
        scrollWidth: 50
      });
    });

    it('should trigger callback with scrollTop of 0 when total rows height is less than height', function () {
      var onScrollCalls = [];
      var grid = render(getMarkup({
        rowCount: 1,
        rowHeight: 50,
        onScroll: function onScroll(params) {
          return onScrollCalls.push(params);
        },
        scrollLeft: 0,
        scrollTop: 10,
        height: 200
      }));
      simulateScroll({
        grid: grid,
        scrollLeft: 0,
        scrollTop: 0
      });
      expect(onScrollCalls.length).toEqual(2);
      expect(onScrollCalls[1]).toEqual({
        clientHeight: 200,
        clientWidth: 200,
        scrollHeight: 50,
        scrollLeft: 0,
        scrollTop: 0,
        scrollWidth: 2500
      });
    });

    // Support use-cases like WindowScroller; enable them to stay in sync with scroll-to-cell changes.
    it('should trigger when :scrollToColumn or :scrollToRow are changed via props', function () {
      var onScrollCalls = [];
      render(getMarkup());
      render(getMarkup({
        onScroll: function onScroll(params) {
          return onScrollCalls.push(params);
        },
        scrollToColumn: 24,
        scrollToRow: 49
      }));
      expect(onScrollCalls).toEqual([{
        clientHeight: 100,
        clientWidth: 200,
        scrollHeight: 2000,
        scrollLeft: 1050,
        scrollTop: 900,
        scrollWidth: 2500
      }]);
    });
  });

  describe('overscanColumnCount & overscanRowCount', function () {
    function createHelper() {
      var _columnOverscanStartIndex = void 0,
          _columnOverscanStopIndex = void 0,
          _columnStartIndex = void 0,
          _columnStopIndex = void 0,
          _rowOverscanStartIndex = void 0,
          _rowOverscanStopIndex = void 0,
          _rowStartIndex = void 0,
          _rowStopIndex = void 0;

      function onSectionRendered(params) {
        _columnOverscanStartIndex = params.columnOverscanStartIndex;
        _columnOverscanStopIndex = params.columnOverscanStopIndex;
        _columnStartIndex = params.columnStartIndex;
        _columnStopIndex = params.columnStopIndex;
        _rowOverscanStartIndex = params.rowOverscanStartIndex;
        _rowOverscanStopIndex = params.rowOverscanStopIndex;
        _rowStartIndex = params.rowStartIndex;
        _rowStopIndex = params.rowStopIndex;
      }

      return {
        columnOverscanStartIndex: function columnOverscanStartIndex() {
          return _columnOverscanStartIndex;
        },
        columnOverscanStopIndex: function columnOverscanStopIndex() {
          return _columnOverscanStopIndex;
        },
        columnStartIndex: function columnStartIndex() {
          return _columnStartIndex;
        },
        columnStopIndex: function columnStopIndex() {
          return _columnStopIndex;
        },
        onSectionRendered: onSectionRendered,
        rowOverscanStartIndex: function rowOverscanStartIndex() {
          return _rowOverscanStartIndex;
        },
        rowOverscanStopIndex: function rowOverscanStopIndex() {
          return _rowOverscanStopIndex;
        },
        rowStartIndex: function rowStartIndex() {
          return _rowStartIndex;
        },
        rowStopIndex: function rowStopIndex() {
          return _rowStopIndex;
        }
      };
    }

    it('should not overscan if disabled', function () {
      var helper = createHelper();
      render(getMarkup({
        onSectionRendered: helper.onSectionRendered
      }));
      expect(helper.columnOverscanStartIndex()).toEqual(helper.columnStartIndex());
      expect(helper.columnOverscanStopIndex()).toEqual(helper.columnStopIndex());
      expect(helper.rowOverscanStartIndex()).toEqual(helper.rowStartIndex());
      expect(helper.rowOverscanStopIndex()).toEqual(helper.rowStopIndex());
    });

    it('should overscan the specified amount', function () {
      var helper = createHelper();
      render(getMarkup({
        onSectionRendered: helper.onSectionRendered,
        overscanColumnCount: 2,
        overscanRowCount: 5,
        scrollToColumn: 25,
        scrollToRow: 50
      }));
      expect(helper.columnOverscanStartIndex()).toEqual(22);
      expect(helper.columnOverscanStopIndex()).toEqual(27);
      expect(helper.columnStartIndex()).toEqual(22);
      expect(helper.columnStopIndex()).toEqual(25);
      expect(helper.rowOverscanStartIndex()).toEqual(46);
      expect(helper.rowOverscanStopIndex()).toEqual(55);
      expect(helper.rowStartIndex()).toEqual(46);
      expect(helper.rowStopIndex()).toEqual(50);
    });

    it('should not overscan beyond the bounds of the grid', function () {
      var helper = createHelper();
      render(getMarkup({
        onSectionRendered: helper.onSectionRendered,
        columnCount: 6,
        overscanColumnCount: 10,
        overscanRowCount: 10,
        rowCount: 5
      }));
      expect(helper.columnOverscanStartIndex()).toEqual(0);
      expect(helper.columnOverscanStopIndex()).toEqual(5);
      expect(helper.columnStartIndex()).toEqual(0);
      expect(helper.columnStopIndex()).toEqual(3);
      expect(helper.rowOverscanStartIndex()).toEqual(0);
      expect(helper.rowOverscanStopIndex()).toEqual(4);
      expect(helper.rowStartIndex()).toEqual(0);
      expect(helper.rowStopIndex()).toEqual(4);
    });

    it('should set the correct scroll direction', function () {
      // Do not pass in the initial state as props, otherwise the internal state is forbidden from updating itself
      var grid = render(getMarkup());

      // Simulate a scroll to set the initial internal state
      simulateScroll({
        grid: grid,
        scrollLeft: 50,
        scrollTop: 50
      });

      expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_FORWARD);
      expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_FORWARD);

      simulateScroll({
        grid: grid,
        scrollLeft: 0,
        scrollTop: 0
      });

      expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_BACKWARD);
      expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_BACKWARD);

      simulateScroll({
        grid: grid,
        scrollLeft: 100,
        scrollTop: 100
      });

      expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_FORWARD);
      expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_FORWARD);
    });

    it('should set the correct scroll direction when scroll position is updated from props', function () {
      var grid = render(getMarkup({
        scrollLeft: 50,
        scrollTop: 50
      }));

      expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_FORWARD);
      expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_FORWARD);

      grid = render(getMarkup({
        scrollLeft: 0,
        scrollTop: 0
      }));

      expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_BACKWARD);
      expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_BACKWARD);

      grid = render(getMarkup({
        scrollLeft: 100,
        scrollTop: 100
      }));

      expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_FORWARD);
      expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_FORWARD);
    });

    it('should not reset scroll direction for one axis when scrolled in another', function () {
      // Do not pass in the initial state as props, otherwise the internal state is forbidden from updating itself
      var grid = render(getMarkup());

      // Simulate a scroll to set the initial internal state
      simulateScroll({
        grid: grid,
        scrollLeft: 0,
        scrollTop: 5
      });

      expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_FORWARD);
      expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_FORWARD);

      simulateScroll({
        grid: grid,
        scrollLeft: 5,
        scrollTop: 5
      });

      expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_FORWARD);
      expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_FORWARD);

      simulateScroll({
        grid: grid,
        scrollLeft: 5,
        scrollTop: 0
      });

      expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_FORWARD);
      expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_BACKWARD);

      simulateScroll({
        grid: grid,
        scrollLeft: 0,
        scrollTop: 0
      });

      expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_BACKWARD);
      expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_BACKWARD);
    });

    it('should overscan in the direction being scrolled', function () {
      var _ref5 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(done) {
        var helper, onSectionRenderedResolve, onSectionRendered, grid, onSectionRenderedPromise;
        return _regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                onSectionRendered = function onSectionRendered(params) {
                  helper.onSectionRendered(params);

                  if (onSectionRenderedResolve) {
                    onSectionRenderedResolve();
                  }
                };

                helper = createHelper();
                onSectionRenderedResolve = void 0;
                grid = render(getMarkup({
                  onSectionRendered: onSectionRendered,
                  overscanColumnCount: 2,
                  overscanRowCount: 5
                }));

                // Wait until the onSectionRendered handler / debouncer has processed

                onSectionRenderedPromise = new _Promise(function (resolve) {
                  onSectionRenderedResolve = resolve;
                });


                simulateScroll({
                  grid: grid,
                  scrollLeft: 200,
                  scrollTop: 200
                });

                _context.next = 8;
                return onSectionRenderedPromise;

              case 8:

                // It should overscan in the direction being scrolled while scroll is in progress
                expect(helper.columnOverscanStartIndex()).toEqual(4);
                expect(helper.columnOverscanStopIndex()).toEqual(9);
                expect(helper.columnStartIndex()).toEqual(4);
                expect(helper.columnStopIndex()).toEqual(7);
                expect(helper.rowOverscanStartIndex()).toEqual(10);
                expect(helper.rowOverscanStopIndex()).toEqual(19);
                expect(helper.rowStartIndex()).toEqual(10);
                expect(helper.rowStopIndex()).toEqual(14);

                // Wait until the onSectionRendered handler / debouncer has processed
                onSectionRenderedPromise = new _Promise(function (resolve) {
                  onSectionRenderedResolve = resolve;
                });

                simulateScroll({
                  grid: grid,
                  scrollLeft: 100,
                  scrollTop: 100
                });

                _context.next = 20;
                return onSectionRenderedPromise;

              case 20:

                // It reset overscan once scrolling has finished
                expect(helper.columnOverscanStartIndex()).toEqual(0);
                expect(helper.columnOverscanStopIndex()).toEqual(5);
                expect(helper.columnStartIndex()).toEqual(2);
                expect(helper.columnStopIndex()).toEqual(5);
                expect(helper.rowOverscanStartIndex()).toEqual(0);
                expect(helper.rowOverscanStopIndex()).toEqual(9);
                expect(helper.rowStartIndex()).toEqual(5);
                expect(helper.rowStopIndex()).toEqual(9);

                done();

              case 29:
              case 'end':
                return _context.stop();
            }
          }
        }, _callee, _this);
      }));

      return function (_x2) {
        return _ref5.apply(this, arguments);
      };
    }());
  });

  describe('cellRangeRenderer', function () {
    it('should use a custom :cellRangeRenderer if specified', function () {
      var cellRangeRendererCalled = 0;
      var cellRangeRendererParams = void 0;
      var rendered = findDOMNode(render(getMarkup({
        cellRangeRenderer: function cellRangeRenderer(params) {
          cellRangeRendererParams = params;
          cellRangeRendererCalled++;

          return [React.createElement(
            'div',
            { key: '0' },
            'Fake content'
          )];
        }
      })));
      expect(cellRangeRendererCalled).toEqual(1);
      expect(cellRangeRendererParams.columnStartIndex).toEqual(0);
      expect(cellRangeRendererParams.columnStopIndex).toEqual(3);
      expect(cellRangeRendererParams.rowStartIndex).toEqual(0);
      expect(cellRangeRendererParams.rowStopIndex).toEqual(4);
      expect(rendered.textContent).toContain('Fake content');
    });
  });

  describe('estimated row and column sizes', function () {
    it('should not estimate sizes if actual sizes are numbers', function () {
      var grid = render(getMarkup({
        columnWidth: 100,
        estimatedColumnSize: 150,
        estimatedRowSize: 15,
        rowHeight: 20
      }));
      expect(grid._getEstimatedColumnSize(grid.props)).toEqual(100);
      expect(grid._getEstimatedRowSize(grid.props)).toEqual(20);
    });

    it('should estimate row and column sizes if actual sizes are functions', function () {
      var grid = render(getMarkup({
        columnWidth: function columnWidth() {
          return 100;
        },
        estimatedColumnSize: 150,
        estimatedRowSize: 15,
        rowHeight: function rowHeight() {
          return 20;
        }
      }));
      expect(grid._getEstimatedColumnSize(grid.props)).toEqual(150);
      expect(grid._getEstimatedRowSize(grid.props)).toEqual(15);
    });
  });

  it('should pass the cellRenderer an :isScrolling flag when scrolling is in progress', function () {
    var _ref6 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee2(done) {
      var cellRendererCalls, cellRenderer, grid;
      return _regeneratorRuntime.wrap(function _callee2$(_context2) {
        while (1) {
          switch (_context2.prev = _context2.next) {
            case 0:
              cellRenderer = function cellRenderer(_ref7) {
                var columnIndex = _ref7.columnIndex,
                    isScrolling = _ref7.isScrolling,
                    key = _ref7.key,
                    rowIndex = _ref7.rowIndex,
                    style = _ref7.style;

                cellRendererCalls.push(isScrolling);
                return defaultCellRenderer({ columnIndex: columnIndex, key: key, rowIndex: rowIndex, style: style });
              };

              cellRendererCalls = [];
              grid = render(getMarkup({
                cellRenderer: cellRenderer
              }));

              expect(cellRendererCalls[0]).toEqual(false);
              cellRendererCalls.splice(0);

              // Give React time to process the queued setState()
              _context2.next = 7;
              return new _Promise(function (resolve) {
                return setTimeout(resolve, 1);
              });

            case 7:

              simulateScroll({ grid: grid, scrollTop: 100 });
              expect(cellRendererCalls[0]).toEqual(true);

              done();

            case 10:
            case 'end':
              return _context2.stop();
          }
        }
      }, _callee2, _this);
    }));

    return function (_x3) {
      return _ref6.apply(this, arguments);
    };
  }());

  it('should pass the cellRenderer an :isScrolling flag based on props override', function () {
    var cellRenderer = jest.fn();
    cellRenderer.mockImplementation(function (_ref8) {
      var key = _ref8.key,
          style = _ref8.style;
      return React.createElement('div', { key: key, style: style });
    });
    render(getMarkup({
      cellRenderer: cellRenderer,
      isScrolling: true
    }));
    expect(cellRenderer).toHaveBeenCalled();
    expect(cellRenderer.mock.calls[0][0].isScrolling).toBe(true);
    cellRenderer.mockReset();
    render(getMarkup({
      cellRenderer: cellRenderer,
      isScrolling: false,
      width: DEFAULT_WIDTH + 1
    }));
    expect(cellRenderer).toHaveBeenCalled();
    expect(cellRenderer.mock.calls[0][0].isScrolling).toBe(false);
  });

  it('should pass the cellRenderer an :isVisible flag', function () {
    var cellRendererCalls = [];
    function cellRenderer(props) {
      cellRendererCalls.push(props);
      return defaultCellRenderer(props);
    }
    render(getMarkup({
      cellRenderer: cellRenderer,
      height: DEFAULT_ROW_HEIGHT,
      overscanColumnCount: 1,
      overscanRowCount: 1,
      width: DEFAULT_COLUMN_WIDTH
    }));
    cellRendererCalls.forEach(function (props) {
      expect(props.isVisible).toEqual(props.columnIndex === 0 && props.rowIndex === 0); // Only the first cell is visible
    });
  });

  describe('cell caching', function () {
    it('should not cache cells if the Grid is not scrolling', function () {
      var cellRendererCalls = [];
      function cellRenderer(_ref9) {
        var columnIndex = _ref9.columnIndex,
            key = _ref9.key,
            rowIndex = _ref9.rowIndex,
            style = _ref9.style;

        cellRendererCalls.push({ columnIndex: columnIndex, rowIndex: rowIndex });
        return defaultCellRenderer({ columnIndex: columnIndex, key: key, rowIndex: rowIndex, style: style });
      }
      var props = {
        cellRenderer: cellRenderer,
        columnWidth: 100,
        height: 40,
        rowHeight: 20,
        scrollToRow: 0,
        width: 100
      };

      render(getMarkup(_extends({}, props, {
        scrollToRow: 0
      })));
      expect(cellRendererCalls).toEqual([{ columnIndex: 0, rowIndex: 0 }, { columnIndex: 0, rowIndex: 1 }]);

      cellRendererCalls.splice(0);

      render(getMarkup(_extends({}, props, {
        scrollToRow: 1
      })));
      expect(cellRendererCalls).toEqual([{ columnIndex: 0, rowIndex: 0 }, { columnIndex: 0, rowIndex: 1 }]);
    });

    it('should cache a cell once it has been rendered while scrolling', function () {
      var cellRendererCalls = [];
      function cellRenderer(_ref10) {
        var columnIndex = _ref10.columnIndex,
            key = _ref10.key,
            rowIndex = _ref10.rowIndex,
            style = _ref10.style;

        cellRendererCalls.push({ columnIndex: columnIndex, rowIndex: rowIndex });
        return defaultCellRenderer({ columnIndex: columnIndex, key: key, rowIndex: rowIndex, style: style });
      }
      var props = {
        cellRenderer: cellRenderer,
        columnWidth: 100,
        height: 40,
        rowHeight: 20,
        width: 100
      };

      var grid = render(getMarkup(_extends({}, props, {
        scrollToRow: 0
      })));
      expect(cellRendererCalls).toEqual([{ columnIndex: 0, rowIndex: 0 }, { columnIndex: 0, rowIndex: 1 }]);

      simulateScroll({ grid: grid, scrollTop: 1 });

      cellRendererCalls.splice(0);

      // Rows 0-2 have already rendered but row 3 is not yet visible
      // This means that only row 3 should be newly-created
      // The others should come from the cache
      render(getMarkup(_extends({}, props, {
        scrollToRow: 3
      })));
      expect(cellRendererCalls).toEqual([{ columnIndex: 0, rowIndex: 3 }]);
    });

    it('should clear cache once :isScrolling is false', function () {
      var _ref11 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee3(done) {
        var cellRendererCalls, cellRenderer, props, grid;
        return _regeneratorRuntime.wrap(function _callee3$(_context3) {
          while (1) {
            switch (_context3.prev = _context3.next) {
              case 0:
                cellRenderer = function cellRenderer(_ref12) {
                  var columnIndex = _ref12.columnIndex,
                      key = _ref12.key,
                      rowIndex = _ref12.rowIndex,
                      style = _ref12.style;

                  cellRendererCalls.push({ columnIndex: columnIndex, rowIndex: rowIndex });
                  return defaultCellRenderer({ columnIndex: columnIndex, key: key, rowIndex: rowIndex, style: style });
                };

                cellRendererCalls = [];
                props = {
                  cellRenderer: cellRenderer,
                  columnWidth: 100,
                  height: 40,
                  rowHeight: 20,
                  scrollToRow: 0,
                  width: 100
                };
                grid = render(getMarkup(props));

                expect(cellRendererCalls).toEqual([{ columnIndex: 0, rowIndex: 0 }, { columnIndex: 0, rowIndex: 1 }]);

                simulateScroll({ grid: grid, scrollTop: 1 });

                // Allow scrolling timeout to complete so that cell cache is reset
                _context3.next = 8;
                return new _Promise(function (resolve) {
                  return setTimeout(resolve, DEFAULT_SCROLLING_RESET_TIME_INTERVAL * 2);
                });

              case 8:

                cellRendererCalls.splice(0);

                render(getMarkup(_extends({}, props, {
                  scrollToRow: 1
                })));
                expect(cellRendererCalls.length).not.toEqual(0);

                done();

              case 12:
              case 'end':
                return _context3.stop();
            }
          }
        }, _callee3, _this);
      }));

      return function (_x4) {
        return _ref11.apply(this, arguments);
      };
    }());

    it('should clear cache once :isScrolling via props is false', _asyncToGenerator(_regeneratorRuntime.mark(function _callee4() {
      var cellRenderer, props, scrollingStyle;
      return _regeneratorRuntime.wrap(function _callee4$(_context4) {
        while (1) {
          switch (_context4.prev = _context4.next) {
            case 0:
              cellRenderer = jest.fn();

              cellRenderer.mockImplementation(function (params) {
                return React.createElement('div', { key: params.key, style: params.style });
              });

              props = {
                autoHeight: true,
                cellRenderer: cellRenderer,
                columnCount: 1,
                isScrolling: true,
                rowCount: 1
              };


              render(getMarkup(props));
              render(getMarkup(props));
              expect(cellRenderer).toHaveBeenCalledTimes(1); // Due to cell cache

              scrollingStyle = cellRenderer.mock.calls[0][0].style;


              cellRenderer.mockReset();

              render(getMarkup(_extends({}, props, {
                isScrolling: false
              })));

              expect(cellRenderer.mock.calls[0][0].style).toBe(scrollingStyle);
              expect(cellRenderer).toHaveBeenCalledTimes(1); // Reset cache

              cellRenderer.mockReset();

              render(getMarkup(_extends({}, props, {
                isScrolling: true
              })));

              expect(cellRenderer.mock.calls[0][0].style).toBe(scrollingStyle);
              expect(cellRenderer).toHaveBeenCalledTimes(1); // Only cached when scrolling

            case 15:
            case 'end':
              return _context4.stop();
          }
        }
      }, _callee4, _this);
    })));

    it('should clear cache if :recomputeGridSize is called', function () {
      var cellRendererCalls = [];
      function cellRenderer(_ref14) {
        var columnIndex = _ref14.columnIndex,
            key = _ref14.key,
            rowIndex = _ref14.rowIndex,
            style = _ref14.style;

        cellRendererCalls.push({ columnIndex: columnIndex, rowIndex: rowIndex });
        return defaultCellRenderer({ columnIndex: columnIndex, key: key, rowIndex: rowIndex, style: style });
      }
      var props = {
        cellRenderer: cellRenderer,
        columnWidth: 100,
        height: 40,
        rowHeight: 20,
        scrollTop: 0,
        width: 100
      };

      var grid = render(getMarkup(props));
      expect(cellRendererCalls).toEqual([{ columnIndex: 0, rowIndex: 0 }, { columnIndex: 0, rowIndex: 1 }]);

      simulateScroll({ grid: grid, scrollTop: 1 });

      cellRendererCalls.splice(0);

      grid.recomputeGridSize();

      expect(cellRendererCalls.length).not.toEqual(0);
    });

    it('should support a custom :scrollingResetTimeInterval prop', function () {
      var _ref15 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee5(done) {
        var cellRendererCalls, scrollingResetTimeInterval, cellRenderer, props, grid;
        return _regeneratorRuntime.wrap(function _callee5$(_context5) {
          while (1) {
            switch (_context5.prev = _context5.next) {
              case 0:
                cellRenderer = function cellRenderer(_ref16) {
                  var columnIndex = _ref16.columnIndex,
                      key = _ref16.key,
                      rowIndex = _ref16.rowIndex,
                      style = _ref16.style;

                  cellRendererCalls.push({ columnIndex: columnIndex, rowIndex: rowIndex });
                  return defaultCellRenderer({ columnIndex: columnIndex, key: key, rowIndex: rowIndex, style: style });
                };

                cellRendererCalls = [];
                scrollingResetTimeInterval = DEFAULT_SCROLLING_RESET_TIME_INTERVAL * 2;
                props = {
                  cellRenderer: cellRenderer,
                  scrollingResetTimeInterval: scrollingResetTimeInterval
                };
                grid = render(getMarkup(props));

                expect(cellRendererCalls.length > 0).toEqual(true);

                simulateScroll({ grid: grid, scrollTop: 1 });

                _context5.next = 9;
                return new _Promise(function (resolve) {
                  return setTimeout(resolve, DEFAULT_SCROLLING_RESET_TIME_INTERVAL);
                });

              case 9:

                cellRendererCalls.splice(0);
                render(getMarkup(_extends({}, props, {
                  className: 'foo'
                })));
                expect(cellRendererCalls.length).toEqual(0);

                _context5.next = 14;
                return new _Promise(function (resolve) {
                  return setTimeout(resolve, DEFAULT_SCROLLING_RESET_TIME_INTERVAL * 2);
                });

              case 14:

                cellRendererCalls.splice(0);
                render(getMarkup(_extends({}, props, {
                  className: 'bar'
                })));
                expect(cellRendererCalls.length).not.toEqual(0);

                done();

              case 18:
              case 'end':
                return _context5.stop();
            }
          }
        }, _callee5, _this);
      }));

      return function (_x5) {
        return _ref15.apply(this, arguments);
      };
    }());
  });

  describe('measureAllCells', function () {
    it('should measure any unmeasured columns and rows', function () {
      var grid = render(getMarkup({
        columnCount: 10,
        columnWidth: function columnWidth() {
          return 100;
        },
        estimatedColumnSize: 150,
        estimatedRowSize: 15,
        height: 0,
        rowCount: 10,
        rowHeight: function rowHeight() {
          return 20;
        },
        width: 0
      }));
      expect(grid._columnSizeAndPositionManager.getTotalSize()).toEqual(1500);
      expect(grid._rowSizeAndPositionManager.getTotalSize()).toEqual(150);
      grid.measureAllCells();
      expect(grid._columnSizeAndPositionManager.getTotalSize()).toEqual(1000);
      expect(grid._rowSizeAndPositionManager.getTotalSize()).toEqual(200);
    });
  });

  describe('recomputeGridSize', function () {
    it('should recompute cell sizes and other values when called', function () {
      var columnIndices = [];
      var rowIndices = [];
      function columnWidth(_ref17) {
        var index = _ref17.index;

        columnIndices.push(index);
        return 10;
      }
      function rowHeight(_ref18) {
        var index = _ref18.index;

        rowIndices.push(index);
        return 10;
      }
      var props = {
        columnCount: 50,
        columnWidth: columnWidth,
        height: 50,
        rowHeight: rowHeight,
        rowCount: 50,
        width: 100
      };
      var component = render(getMarkup(props));

      columnIndices.splice(0);
      rowIndices.splice(0);

      component.recomputeGridSize();

      // Only the rows required to fill the current viewport will be rendered
      expect(columnIndices[0]).toEqual(0);
      expect(columnIndices[columnIndices.length - 1]).toEqual(9);
      expect(rowIndices[0]).toEqual(0);
      expect(rowIndices[rowIndices.length - 1]).toEqual(4);

      columnIndices.splice(0);
      rowIndices.splice(0);

      component.recomputeGridSize({
        columnIndex: 4,
        rowIndex: 2
      });

      // Only the rows required to fill the current viewport will be rendered
      expect(columnIndices[0]).toEqual(4);
      expect(columnIndices[columnIndices.length - 1]).toEqual(9);
      expect(rowIndices[0]).toEqual(2);
      expect(rowIndices[rowIndices.length - 1]).toEqual(4);
    });
  });

  describe('autoContainerWidth', function () {
    it('should set the innerScrollContainer width to auto to better support single-column HOCs', function () {
      var props = {
        autoContainerWidth: true
      };
      var rendered = findDOMNode(render(getMarkup(props)));
      expect(rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer').style.width).toEqual('auto');
    });

    it('should set the innerScrollContainer width to :totalColumnsWidth unless :autoContainerWidth', function () {
      var props = {
        autoContainerWidth: false
      };
      var rendered = findDOMNode(render(getMarkup(props)));
      expect(rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer').style.width).toEqual('2500px'); // 50 columns x 50px
    });
  });

  describe('autoHeight', function () {
    it('should set the container height to auto to adjust to innerScrollContainer height', function () {
      var props = {
        autoHeight: true
      };
      var rendered = findDOMNode(render(getMarkup(props)));
      expect(rendered.style.height).toEqual('auto');
    });

    it('should have container height still affecting number of rows rendered', function () {
      var props = {
        height: 500,
        autoHeight: true
      };
      var rendered = findDOMNode(render(getMarkup(props)));
      expect(rendered.querySelectorAll('.gridItem').length).toEqual(100); // 25 rows x 4 columns
    });

    it('should have innerScrollContainer height to be equal number of rows * rowHeight', function () {
      var props = {
        autoHeight: true
      };
      var grid = render(getMarkup(props));
      var rendered = findDOMNode(grid);
      expect(rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer').style.height).toEqual('2000px'); // 100 rows * 20px rowHeight
      expect(grid._rowSizeAndPositionManager.getTotalSize()).toEqual(2000);
    });
  });

  describe('autoWidth', function () {
    it('should set the container width to auto to adjust to innerScrollContainer width', function () {
      var props = {
        autoWidth: true
      };
      var rendered = findDOMNode(render(getMarkup(props)));
      expect(rendered.style.width).toEqual('auto');
    });

    it('should have container width still affecting number of columns rendered', function () {
      var props = {
        width: 500,
        autoWidth: true
      };
      var rendered = findDOMNode(render(getMarkup(props)));
      expect(rendered.querySelectorAll('.gridItem').length).toEqual(50); // 5 rows x 10 columns
    });

    it('should have innerScrollContainer width to be equal number of columns * columnWidth', function () {
      var props = {
        autoWidth: true
      };
      var grid = render(getMarkup(props));
      var rendered = findDOMNode(grid);
      expect(rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer').style.width).toEqual('2500px'); // 50 columns * 50px columnWidth
      expect(grid._columnSizeAndPositionManager.getTotalSize()).toEqual(2500);
    });
  });

  describe('tabIndex', function () {
    it('should be focusable by default', function () {
      var rendered = findDOMNode(render(getMarkup()));
      expect(rendered.tabIndex).toEqual(0);
    });

    it('should allow tabIndex to be overridden', function () {
      var rendered = findDOMNode(render(getMarkup({
        tabIndex: -1
      })));
      expect(rendered.tabIndex).toEqual(-1);
    });
  });

  describe('role', function () {
    it('should have grid role by default', function () {
      var rendered = findDOMNode(render(getMarkup()));
      expect(rendered.getAttribute('role')).toEqual('grid');
    });

    it('should allow role to be overridden', function () {
      var role = null;
      var rendered = findDOMNode(render(getMarkup({ role: role })));
      expect(rendered.getAttribute('role')).toEqual(role);
    });
  });

  describe('pure', function () {
    it('should not re-render unless props have changed', function () {
      var cellRendererCalled = false;
      function cellRenderer(_ref19) {
        var key = _ref19.key,
            style = _ref19.style;

        cellRendererCalled = true;
        return React.createElement('div', { key: key, style: style });
      }
      var markup = getMarkup({ cellRenderer: cellRenderer });
      render(markup);
      expect(cellRendererCalled).toEqual(true);
      cellRendererCalled = false;
      render(markup);
      expect(cellRendererCalled).toEqual(false);
    });

    it('should not re-render grid components if they extend PureComponent', function () {
      var componentUpdates = 0;

      var GridComponent = function (_React$PureComponent) {
        _inherits(GridComponent, _React$PureComponent);

        function GridComponent() {
          _classCallCheck(this, GridComponent);

          return _possibleConstructorReturn(this, (GridComponent.__proto__ || _Object$getPrototypeOf(GridComponent)).apply(this, arguments));
        }

        _createClass(GridComponent, [{
          key: 'componentDidUpdate',
          value: function componentDidUpdate() {
            componentUpdates++;
          }
        }, {
          key: 'render',
          value: function render() {
            var _props = this.props,
                columnIndex = _props.columnIndex,
                rowIndex = _props.rowIndex,
                style = _props.style;

            return React.createElement(
              'div',
              {
                className: 'gridItem',
                style: style
              },
              'row:' + rowIndex + ', column:' + columnIndex
            );
          }
        }]);

        return GridComponent;
      }(React.PureComponent);

      function cellRenderer(_ref20) {
        var columnIndex = _ref20.columnIndex,
            key = _ref20.key,
            rowIndex = _ref20.rowIndex,
            style = _ref20.style;

        return React.createElement(GridComponent, {
          key: key,
          columnIndex: columnIndex,
          rowIndex: rowIndex,
          style: style
        });
      }

      var props = {
        cellRenderer: cellRenderer,
        columnWidth: 100,
        height: 40,
        rowHeight: 20,
        scrollTop: 0,
        width: 100
      };

      var grid = render(getMarkup(props));
      simulateScroll({ grid: grid, scrollTop: 1 });

      expect(componentUpdates).toEqual(0);
    });

    it('should clear all but the visible rows from the style cache once :isScrolling is false', function () {
      var _ref21 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee6(done) {
        var props, grid;
        return _regeneratorRuntime.wrap(function _callee6$(_context6) {
          while (1) {
            switch (_context6.prev = _context6.next) {
              case 0:
                props = {
                  columnWidth: 50,
                  height: 100,
                  overscanColumnCount: 0,
                  overscanRowCount: 0,
                  rowHeight: 50,
                  width: 100
                };
                grid = render(getMarkup(props));


                expect(_Object$keys(grid._styleCache).length).toBe(4);

                simulateScroll({ grid: grid, scrollTop: 50 });

                expect(_Object$keys(grid._styleCache).length).toBe(6);

                // Allow scrolling timeout to complete so that cell cache is reset
                _context6.next = 7;
                return new _Promise(function (resolve) {
                  return setTimeout(resolve, DEFAULT_SCROLLING_RESET_TIME_INTERVAL * 2);
                });

              case 7:

                expect(_Object$keys(grid._styleCache).length).toBe(4);

                done();

              case 9:
              case 'end':
                return _context6.stop();
            }
          }
        }, _callee6, _this);
      }));

      return function (_x6) {
        return _ref21.apply(this, arguments);
      };
    }());

    it('should clear style cache if :recomputeGridSize is called', function () {
      var props = {
        columnWidth: 50,
        height: 100,
        overscanColumnCount: 0,
        overscanRowCount: 0,
        rowHeight: 50,
        width: 100
      };

      var grid = render(getMarkup(props));

      expect(_Object$keys(grid._styleCache).length).toBe(4);

      render(getMarkup(_extends({}, props, {
        scrollTop: 50
      })));

      expect(_Object$keys(grid._styleCache).length).toBe(6);

      grid.recomputeGridSize();

      expect(_Object$keys(grid._styleCache).length).toBe(4);
    });

    it('should clear style cache if cell sizes change', function () {
      var cellRendererCalls = [];
      function cellRenderer(params) {
        cellRendererCalls.push(params);
        return React.createElement('div', { key: params.key, style: params.style });
      }

      var props = {
        cellRenderer: cellRenderer,
        columnWidth: 100,
        height: 100,
        overscanColumnCount: 0,
        overscanRowCount: 0,
        rowHeight: 100,
        width: 100
      };

      render(getMarkup(props));

      expect(cellRendererCalls.length).toEqual(1);
      expect(cellRendererCalls[0].style.width).toEqual(100);

      render(getMarkup(_extends({}, props, {
        columnWidth: 50,
        width: 50
      })));

      expect(cellRendererCalls.length).toEqual(2);
      expect(cellRendererCalls[1].style.width).toEqual(50);
    });
  });

  it('should not pull from the style cache while scrolling if there is an offset adjustment', function () {
    var cellRendererCalls = [];
    function cellRenderer(params) {
      cellRendererCalls.push(params);
      return React.createElement('div', { key: params.key, style: params.style });
    }

    var grid = render(getMarkup({
      cellRenderer: cellRenderer,
      width: 100,
      height: 100,
      rowHeight: 100,
      columnWidth: 100,
      rowCount: DEFAULT_MAX_SCROLL_SIZE * 2 / 100, // lots of offset
      scrollTop: 2000
    }));

    simulateScroll({
      grid: grid,
      scrollTop: 2100
    });

    // cellRendererCalls[0] is the element at rowIndex 0
    var firstProps = cellRendererCalls[1];
    var secondProps = cellRendererCalls[2];

    expect(cellRendererCalls.length).toEqual(3);
    expect(firstProps.style).not.toBe(secondProps.style);
  });

  it('should only cache styles when a :deferredMeasurementCache is provided if the cell has already been measured', function () {
    var cache = new CellMeasurerCache({
      fixedWidth: true
    });
    cache.set(0, 0, 100, 100);
    cache.set(1, 1, 100, 100);

    var grid = render(getMarkup({
      columnCount: 2,
      deferredMeasurementCache: cache,
      rowCount: 2
    }));

    var keys = _Object$keys(grid._styleCache);

    expect(keys).toEqual(['0-0', '1-1']);
  });

  describe('DEV warnings', function () {
    it('should warn about cells that forget to include the :style property', function () {
      spyOn(console, 'warn');

      function cellRenderer(params) {
        return React.createElement('div', { key: params.key });
      }

      render(getMarkup({
        cellRenderer: cellRenderer
      }));

      expect(console.warn).toHaveBeenCalledWith('Rendered cell should include style property for positioning.');
      expect(console.warn).toHaveBeenCalledTimes(1);
    });

    it('should warn about CellMeasurer measured cells that forget to include the :style property', function () {
      spyOn(console, 'warn');

      var cache = new CellMeasurerCache({
        fixedWidth: true
      });

      var cellRenderer = jest.fn();
      cellRenderer.mockImplementation(function (params) {
        return React.createElement(
          CellMeasurer,
          {
            cache: cache,
            columnIndex: params.columnIndex,
            key: params.key,
            parent: params.parent,
            rowIndex: params.rowIndex,
            style: params.style
          },
          React.createElement('div', null)
        );
      });

      render(getMarkup({
        cellRenderer: cellRenderer,
        columnCount: 1,
        deferredMeasurementCache: cache,
        rowCount: 1
      }));

      expect(console.warn).toHaveBeenCalledWith('Rendered cell should include style property for positioning.');
      expect(console.warn).toHaveBeenCalledTimes(1);
    });
  });

  describe('deferredMeasurementCache', function () {
    it('invalidateCellSizeAfterRender should invalidate cache and refresh displayed cells after mount', function () {
      var cache = new CellMeasurerCache({
        fixedWidth: true
      });

      var invalidateCellSizeAfterRender = true;

      var cellRenderer = jest.fn();
      cellRenderer.mockImplementation(function (params) {
        // Don't get stuck in a loop
        if (invalidateCellSizeAfterRender) {
          invalidateCellSizeAfterRender = false;

          params.parent.invalidateCellSizeAfterRender({
            columnIndex: 1,
            rowIndex: 0
          });
        }
        return React.createElement('div', { key: params.key, style: params.style });
      });

      var props = {
        cellRenderer: cellRenderer,
        columnCount: 2,
        deferredMeasurementCache: cache,
        rowCount: 2
      };

      render(getMarkup(props));

      // 4 times for initial render + 4 once cellCache was cleared
      expect(cellRenderer).toHaveBeenCalledTimes(8);
    });

    it('should invalidate cache and refresh displayed cells after update', function () {
      var cache = new CellMeasurerCache({
        fixedWidth: true
      });

      var cellRenderer = jest.fn();
      cellRenderer.mockImplementation(function (params) {
        return React.createElement('div', { key: params.key, style: params.style });
      });

      var props = {
        cellRenderer: cellRenderer,
        columnCount: 2,
        deferredMeasurementCache: cache,
        rowCount: 2
      };

      var grid = render(getMarkup(props));

      expect(cellRenderer).toHaveBeenCalledTimes(4);

      var invalidateCellSizeAfterRender = false;

      cellRenderer.mockReset();
      cellRenderer.mockImplementation(function (params) {
        // Don't get stuck in a loop
        if (invalidateCellSizeAfterRender) {
          invalidateCellSizeAfterRender = false;
          params.parent.invalidateCellSizeAfterRender({
            columnIndex: 1,
            rowIndex: 0
          });
        }
        return React.createElement('div', { key: params.key, style: params.style });
      });

      invalidateCellSizeAfterRender = true;
      grid.recomputeGridSize();

      // 4 times for initial render + 4 once cellCache was cleared
      expect(cellRenderer).toHaveBeenCalledTimes(8);
    });

    it('should not cache cells until they have been measured by CellMeasurer', function () {
      var cache = new CellMeasurerCache({
        fixedWidth: true
      });

      // Fake measure cell 0,0 but not cell 0,1
      cache.set(0, 0, 100, 30);

      var cellRenderer = jest.fn();
      cellRenderer.mockImplementation(function (params) {
        return React.createElement('div', { key: params.key, style: params.style });
      });

      var props = {
        cellRenderer: cellRenderer,
        columnCount: 2,
        deferredMeasurementCache: cache,
        rowCount: 1
      };

      // Trigger 2 renders
      // The second render should re-use the style for cell 0,0
      // But should not re-use the style for cell 0,1 since it was not measured
      var grid = render(getMarkup(props));
      grid.forceUpdate();

      // 0,0 - 0,1 - 0,0 - 0,1
      expect(cellRenderer).toHaveBeenCalledTimes(4);
      var style00A = cellRenderer.mock.calls[0][0].style;
      var style01A = cellRenderer.mock.calls[1][0].style;
      var style00B = cellRenderer.mock.calls[2][0].style;
      var style01B = cellRenderer.mock.calls[3][0].style;
      expect(style00A).toBe(style00B);
      expect(style01A).not.toBe(style01B);
    });
  });
});