text: describe('WalkontableTable', () => {
    let $table;
    let $container;
    let $wrapper;
    const debug = false;
  
    beforeEach(() => {
      $wrapper = $('<div></div>').css({ overflow: 'hidden', position: 'relative' });
      $wrapper.width(100).height(201);
      $container = $('<div></div>');
      $table = $('<table></table>'); // create a table that is not attached to document
      $wrapper.append($container);
      $container.append($table);
      $wrapper.appendTo('body');
      createDataArray();
    });
  
    afterEach(() => {
      if (!debug) {
        $('.wtHolder').remove();
      }
  
      $wrapper.remove();
    });
  
    it('should create as many rows as fits in height', () => {
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns
      });
      wt.draw();
      expect($table.find('tbody tr').length).toBe(9);
    });
  
    it('should create as many rows as in `totalRows` if it is smaller than `height`', function() {
      this.data.splice(5, this.data.length - 5);
  
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns
      });
      wt.draw();
      expect($table.find('tbody tr').length).toBe(5);
    });
  
    it('first row should have as many columns as in THEAD', () => {
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        columnHeaders: [function(col, TH) {
          TH.innerHTML = col + 1;
        }]
      });
      wt.draw();
      expect($table.find('tbody tr:first td').length).toBe($table.find('thead th').length);
    });
  
    it('should put a blank cell in the corner if both rowHeaders and colHeaders are set', () => {
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        columnHeaders: [
          function(col, TH) {
            if (col > -1) {
              TH.innerHTML = 'Column';
            }
          }
        ],
        rowHeaders: [
          function(row, TH) {
            if (row > -1) {
              TH.innerHTML = 'Row';
            }
          }
        ]
      });
      wt.draw();
      expect($table.find('thead tr:first th').length).toBe(wt.wtTable.getRenderedColumnsCount() + 1); // 4 columns in THEAD + 1 empty cell in the corner
      expect($table.find('thead tr:first th:eq(0)')[0].innerHTML.replace(/&nbsp;/, '')).toBe(''); // corner row is empty (or contains only &nbsp;)
      expect($table.find('thead tr:first th:eq(1)')[0].innerHTML).toBe('Column');
      expect($table.find('tbody tr:first th:eq(0)')[0].innerHTML).toBe('Row');
    });
  
    it('getCell should only return cells from rendered rows and columns', function() {
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns
      });
      wt.draw();
  
      expect(wt.wtTable.getCell(new Walkontable.CellCoords(7, 0)) instanceof HTMLElement).toBe(true);
      expect($table.find('tr:eq(8) td:first-child').text()).toEqual(this.data[8][0].toString());
      expect(wt.wtTable.getCell(new Walkontable.CellCoords(20, 0))).toBe(-2); // exit code
      expect(wt.wtTable.getCell(new Walkontable.CellCoords(25, 0))).toBe(-2); // exit code
      expect(wt.wtTable.getCell(new Walkontable.CellCoords(7, 5))).toBe(-4); // exit code - after rendered column
  
      wt.scrollViewportHorizontally(getTotalColumns() - 1);
      wt.draw();
      expect(wt.wtTable.getCell(new Walkontable.CellCoords(7, 0))).toBe(-3); // exit code - before rendered column
    });
  
    it('getCoords should return coords of TD', () => {
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns
      });
      wt.draw();
  
      const $td2 = $table.find('tbody tr:eq(1) td:eq(1)');
      expect(wt.wtTable.getCoords($td2[0])).toEqual(new Walkontable.CellCoords(1, 1));
    });
  
    it('getCoords should return coords of TD (with row header)', () => {
  
      $wrapper.width(300);
  
      function plusOne(i) {
        return i + 1;
      }
  
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        rowHeaders: [function(row, TH) {
          TH.innerHTML = plusOne(row);
        }]
      });
      wt.draw();
  
      const $td2 = $table.find('tbody tr:eq(1) td:eq(1)');
      expect(wt.wtTable.getCoords($td2[0])).toEqual(new Walkontable.CellCoords(1, 1));
    });
  
    it('getCoords should return coords of TH', () => {
  
      $wrapper.width(300);
  
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        columnHeaders: [function(col, TH) {
          TH.innerHTML = col + 1;
        }]
      });
      wt.draw();
  
      const $th2 = $table.find('thead tr:first th:eq(1)');
      expect(wt.wtTable.getCoords($th2[0])).toEqual(new Walkontable.CellCoords(-1, 1));
    });
  
    it('getCoords should return coords of TD (with fixedColumnsLeft)', () => {
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        fixedColumnsLeft: 2,
        columnHeaders: [function(col, TH) {
          TH.innerHTML = col + 1;
        }]
      });
      wt.draw();
  
      const $cloneLeft = $('.ht_clone_left');
      const $td2 = $cloneLeft.find('tbody tr:eq(1) td:eq(1)');
      expect(wt.wtTable.getCoords($td2[0])).toEqual(new Walkontable.CellCoords(1, 1));
    });
  
    it('getStretchedColumnWidth should return valid column width when stretchH is set as \'all\'', () => {
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        rowHeaders: [function(row, TH) {
          TH.innerHTML = row + 1;
        }],
        stretchH: 'all'
      });
      wt.draw();
      wt.wtViewport.columnsRenderCalculator.refreshStretching(502);
  
      expect(wt.wtTable.getStretchedColumnWidth(0, 50)).toBe(125);
      expect(wt.wtTable.getStretchedColumnWidth(1, 50)).toBe(125);
      expect(wt.wtTable.getStretchedColumnWidth(2, 50)).toBe(125);
      expect(wt.wtTable.getStretchedColumnWidth(3, 50)).toBe(127);
    });
  
    it('getStretchedColumnWidth should return valid column width when stretchH is set as \'last\'', () => {
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        rowHeaders: [function(row, TH) {
          TH.innerHTML = row + 1;
        }],
        stretchH: 'last'
      });
      wt.draw();
      wt.wtViewport.columnsRenderCalculator.refreshStretching(502);
  
      expect(wt.wtTable.getStretchedColumnWidth(0, 50)).toBe(50);
      expect(wt.wtTable.getStretchedColumnWidth(1, 50)).toBe(50);
      expect(wt.wtTable.getStretchedColumnWidth(2, 50)).toBe(50);
      expect(wt.wtTable.getStretchedColumnWidth(3, 50)).toBe(352);
    });
  
    it('should use custom cell renderer if provided', () => {
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        cellRenderer(row, column, TD) {
          const cellData = getData(row, column);
  
          if (cellData === void 0) {
            TD.innerHTML = '';
          } else {
            TD.innerHTML = cellData;
          }
          TD.className = '';
          TD.style.backgroundColor = 'yellow';
        }
      });
      wt.draw();
      expect($table.find('td:first')[0].style.backgroundColor).toBe('yellow');
    });
  
    it('should remove rows if they were removed in data source', function() {
      this.data.splice(8, this.data.length - 8); // second param is required by IE8
  
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns
      });
      wt.draw();
      expect($table.find('tbody tr').length).toBe(8);
  
      this.data.splice(7, this.data.length - 7); // second param is required by IE8
      wt.draw();
      expect($table.find('tbody tr').length).toBe(7);
    });
  
    it('should render as much columns as the container width allows, if width is null', () => {
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        columnHeaders: [function(col, TH) {
          TH.innerHTML = col + 1;
        }]
      });
      wt.draw();
      expect($table.find('thead tr:first').children().length).toBe(2);
      expect($table.find('tbody tr:first').children().length).toBe(2);
  
      $wrapper.width(200);
      wt.draw();
      expect($table.find('thead tr:first').children().length).toBe(4);
      expect($table.find('tbody tr:first').children().length).toBe(4);
    });
  
    it('should render as much columns as the container width allows, if width is null (with row header)', () => {
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        rowHeaders: [function(row, TH) {
          TH.innerHTML = row + 1;
        }],
        columnHeaders: [function(col, TH) {
          TH.innerHTML = col + 1;
        }]
      });
      wt.draw();
      expect($table.find('thead tr:first').children().length).toBe(2);
      expect($table.find('tbody tr:first').children().length).toBe(2);
  
      $wrapper.width(200);
      wt.draw();
      expect($table.find('thead tr:first').children().length).toBe(4);
      expect($table.find('tbody tr:first').children().length).toBe(4);
    });
  
    it('should use column width function to get column width', () => {
  
      $wrapper.width(600);
  
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        rowHeaders: [function(row, TH) {
          TH.innerHTML = row + 1;
        }],
        columnHeaders: [function(col, TH) {
          TH.innerHTML = col + 1;
        }],
        columnWidth(column) {
          return (column + 1) * 50;
        }
      });
      wt.draw();
      expect($table.find('tbody tr:first td:eq(0)').outerWidth()).toBe(50);
      expect($table.find('tbody tr:first td:eq(1)').outerWidth()).toBe(100);
      expect($table.find('tbody tr:first td:eq(2)').outerWidth()).toBe(150);
      expect($table.find('tbody tr:first td:eq(3)').outerWidth()).toBe(200);
    });
  
    it('should use column width array to get column width', () => {
  
      $wrapper.width(600);
  
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        rowHeaders: [function(row, TH) {
          TH.innerHTML = row + 1;
        }],
        columnHeaders: [function(col, TH) {
          TH.innerHTML = col + 1;
        }],
        columnWidth: [50, 100, 150, 201]
      });
      wt.draw();
      expect($table.find('tbody tr:first td:eq(0)').outerWidth()).toBe(50);
      expect($table.find('tbody tr:first td:eq(1)').outerWidth()).toBe(100);
      expect($table.find('tbody tr:first td:eq(2)').outerWidth()).toBe(150);
      expect($table.find('tbody tr:first td:eq(3)').outerWidth()).toBe(201);
    });
  
    it('should use column width integer to get column width', () => {
  
      $wrapper.width(600);
  
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        rowHeaders: [function(row, TH) {
          TH.innerHTML = row + 1;
        }],
        columnHeaders: [function(col, TH) {
          TH.innerHTML = col + 1;
        }],
        columnWidth: 100
      });
      wt.draw();
      expect($table.find('tbody tr:first td:eq(0)').outerWidth()).toBe(100);
      expect($table.find('tbody tr:first td:eq(1)').outerWidth()).toBe(100);
      expect($table.find('tbody tr:first td:eq(2)').outerWidth()).toBe(100);
      expect($table.find('tbody tr:first td:eq(3)').outerWidth()).toBe(100);
    });
  
    it('should use column width also when there are no rows', function() {
      this.data.length = 0;
  
      $wrapper.width(600);
  
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: 4,
        rowHeaders: [function(row, TH) {
          TH.innerHTML = row + 1;
        }],
        columnHeaders: [function(col, TH) {
          TH.innerHTML = col + 1;
        }],
        columnWidth: 100
      });
      wt.draw();
      // start from eq(1) because eq(0) is corner header
      expect($table.find('thead tr:first th:eq(1)').outerWidth()).toBe(100);
      expect($table.find('thead tr:first th:eq(2)').outerWidth()).toBe(100);
      expect($table.find('thead tr:first th:eq(3)').outerWidth()).toBe(100);
      expect($table.find('thead tr:first th:eq(4)').outerWidth()).toBe(100);
    });
  
    it('should render a cell that is outside of the viewport horizontally', () => {
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns
      });
      wt.draw();
      $table.find('tbody td').html('');
      wt.draw();
      expect($table.find('tbody tr:first td').length).toBe(2);
    });
  
    it('should not render a cell when fastDraw == true', () => {
      let count = 0;
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        cellRenderer(row, column, TD) {
          count += 1;
          return wt.wtSettings.defaults.cellRenderer(row, column, TD);
        }
      });
      wt.draw();
      const oldCount = count;
      wt.draw(true);
      expect(count).toBe(oldCount);
    });
  
    it('should not ignore fastDraw == true when grid was scrolled by amount of rows that doesn\'t exceed endRow', () => {
      let count = 0;
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        cellRenderer(row, column, TD) {
          count += 1;
          return wt.wtSettings.defaults.cellRenderer(row, column, TD);
        },
        viewportRowCalculatorOverride(calc) {
          calc.endRow += 10;
        }
      });
      wt.draw();
      const oldCount = count;
  
      wt.scrollViewportVertically(8);
      wt.draw(true);
      expect(count).not.toBeGreaterThan(oldCount);
    });
  
    it('should ignore fastDraw == true when grid was scrolled by amount of rows that exceeds endRow', () => {
      let count = 0;
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        cellRenderer(row, column, TD) {
          count += 1;
          return wt.wtSettings.defaults.cellRenderer(row, column, TD);
        },
        viewportRowCalculatorOverride(calc) {
          calc.endRow += 10;
        }
      });
      wt.draw();
      const oldCount = count;
  
      wt.scrollViewportVertically(10);
      wt.draw(true);
      expect(count).not.toBeGreaterThan(oldCount);
  
      wt.scrollViewportVertically(getTotalRows() - 1);
      wt.draw(true);
      expect(count).toBeGreaterThan(oldCount);
    });
  
    it('should not ignore fastDraw == true when grid was scrolled by amount of columns that doesn\'t exceed endColumn', () => {
      createDataArray(50, 50);
      let count = 0;
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        cellRenderer(row, column, TD) {
          count += 1;
          return wt.wtSettings.defaults.cellRenderer(row, column, TD);
        },
        viewportColumnCalculatorOverride(calc) {
          calc.endColumn += 10;
        }
      });
      wt.draw();
      const oldCount = count;
  
      wt.scrollViewportHorizontally(8);
      wt.draw(true);
  
      expect(count).not.toBeGreaterThan(oldCount);
    });
  
    it('should ignore fastDraw == true when grid was scrolled by amount of columns that exceeds endColumn', () => {
      createDataArray(50, 50);
      let count = 0;
      const wt = new Walkontable.Core({
        table: $table[0],
        data: getData,
        totalRows: getTotalRows,
        totalColumns: getTotalColumns,
        cellRenderer(row, column, TD) {
          count += 1;
  
          return wt.wtSettings.defaults.cellRenderer(row, column, TD);
        },
        viewportColumnCalculatorOverride(calc) {
          calc.endColumn += 10;
        }
      });
      wt.draw();
      const oldCount = count;
  
      wt.scrollViewportHorizontally(10);
      wt.draw(true);
      expect(count).not.toBeGreaterThan(oldCount);
  
      wt.scrollViewportHorizontally(11);
      wt.draw(true);
      expect(count).toBeGreaterThan(oldCount);
    });
  
    describe('cell header border', () => {
      it('should be correct visible in fixedColumns and without row header', () => {
        createDataArray(50, 50);
        $wrapper.width(500).height(400);
  
        const wt = new Walkontable.Core({
          table: $table[0],
          data: getData,
          totalRows: getTotalRows,
          totalColumns: getTotalColumns,
          columnWidth: 70,
          fixedColumnsLeft: 2,
          columnHeaders: [function() {}]
        });
        wt.draw();
  
        expect($('.ht_clone_top_left_corner thead tr th').eq(0).css('border-left-width')).toBe('1px');
        expect($('.ht_clone_top_left_corner thead tr th').eq(0).css('border-right-width')).toBe('1px');
        expect($('.ht_clone_top_left_corner thead tr th').eq(1).css('border-left-width')).toBe('0px');
        expect($('.ht_clone_top_left_corner thead tr th').eq(1).css('border-right-width')).toBe('1px');
      });
    });
  
    describe('isLastRowFullyVisible', () => {
      it('should be false because it is only partially visible', () => {
        createDataArray(8, 4);
  
        $wrapper.width(185).height(175);
  
        const wt = new Walkontable.Core({
          table: $table[0],
          data: getData,
          totalRows: getTotalRows,
          totalColumns: getTotalColumns
        });
        wt.draw();
  
        expect(wt.wtTable.isLastRowFullyVisible()).toEqual(false);
      });
  
      it('should be true because it is fully visible', () => {
        createDataArray(8, 4);
  
        $wrapper.width(185).height(185);
  
        const wt = new Walkontable.Core({
          table: $table[0],
          data: getData,
          totalRows: getTotalRows,
          totalColumns: getTotalColumns
        });
        wt.draw();
        wt.scrollViewportVertically(7);
        wt.draw();
  
        expect(wt.wtTable.isLastRowFullyVisible()).toEqual(true);
      });
    });
  
    xdescribe('isLastColumnFullyVisible', () => {
      it('should be false because it is only partially visible', () => {
        createDataArray(18, 4);
  
        $wrapper.width(209).height(185);
  
        const wt = new Walkontable.Core({
          table: $table[0],
          data: getData,
          totalRows: getTotalRows,
          totalColumns: getTotalColumns
        });
        wt.draw();
  
        expect(wt.wtTable.isLastColumnFullyVisible()).toEqual(false); // few pixels are obstacled by scrollbar
      });
  
      it('should be true because it is fully visible', () => {
        createDataArray(18, 4);
  
        $wrapper.width(180).height(185);
  
        const wt = new Walkontable.Core({
          table: $table[0],
          data: getData,
          totalRows: getTotalRows,
          totalColumns: getTotalColumns
        });
        wt.draw();
        wt.scrollHorizontal(1);
  
        expect(wt.wtTable.isLastColumnFullyVisible()).toEqual(true);
      });
    });
  });
  