Files
2016-04-05 14:50:05 -05:00

570 lines
17 KiB
TypeScript

import {VirtualScroll} from '../../../../ionic/components/virtual-scroll/virtual-scroll';
import {VirtualCell, VirtualData, VirtualNode} from '../../../../ionic/components/virtual-scroll/virtual-util';
import {processRecords, populateNodeData, initReadNodes, getVirtualHeight, adjustRendered} from '../../../../ionic/components/virtual-scroll/virtual-util';
export function run() {
describe('VirtualScroll', () => {
beforeEach(() => {
records = [0,1,2,3,4,5,6,7,8,9];
cells = [];
nodes = [];
headerFn = null;
footerFn = null;
var viewportWidth = 300;
data = {
viewWidth: viewportWidth,
viewHeight: 600,
itmWidth: viewportWidth,
itmHeight: HEIGHT_ITEM,
hdrWidth: viewportWidth,
hdrHeight: HEIGHT_HEADER,
ftrWidth: viewportWidth,
ftrHeight: HEIGHT_FOOTER
};
window.getComputedStyle = function(element) {
var styles: any = {
marginTop: '0px',
marginRight: '0px',
marginBottom: '0px',
marginLeft: '0px'
}
return styles;
};
});
describe('processRecords', () => {
it('should load data for 100% width items', () => {
records = [0,1,2,3,4]
let stopAtHeight = 200;
processRecords(stopAtHeight, records, cells,
headerFn, footerFn, data);
expect(cells.length).toBe(4);
expect(cells[0].record).toBe(0);
expect(cells[0].row).toBe(0);
expect(cells[0].top).toBe(HEIGHT_ITEM * 0);
expect(cells[0].height).toBe(HEIGHT_ITEM);
expect(cells[0].data).toBeUndefined();
expect(cells[0].tmpl).toBe(TEMPLATE_ITEM);
expect(cells[1].row).toBe(1);
expect(cells[1].top).toBe(HEIGHT_ITEM * 1);
expect(cells[1].height).toBe(HEIGHT_ITEM);
expect(cells[2].row).toBe(2);
expect(cells[2].top).toBe(HEIGHT_ITEM * 2);
expect(cells[2].height).toBe(HEIGHT_ITEM);
expect(cells[3].row).toBe(3);
expect(cells[3].top).toBe(HEIGHT_ITEM * 3);
expect(cells[3].height).toBe(HEIGHT_ITEM);
});
it('should load data for 30% width items', () => {
records = [0,1,2,3,4];
let stopAtHeight = 1000;
data.viewWidth = 300;
data.itmWidth = 90; // 30%, 3 per row
data.hdrWidth = data.viewWidth; // 100%, 1 per row
data.ftrWidth = data.viewWidth; // 100%, 1 per row
headerFn = function(record) {
return (record === 0) ? 'Header' : null;
};
footerFn = function(record) {
return (record === 4) ? 'Footer' : null;
};
processRecords(stopAtHeight, records, cells,
headerFn, footerFn, data);
expect(cells.length).toBe(7);
expect(cells[0].row).toBe(0);
expect(cells[0].width).toBe(data.viewWidth);
expect(cells[0].height).toBe(HEIGHT_HEADER);
expect(cells[0].top).toBe(0);
expect(cells[0].left).toBe(0);
expect(cells[0].tmpl).toBe(TEMPLATE_HEADER);
expect(cells[0].data).toBe('Header');
expect(cells[0].record).toBe(0);
expect(cells[1].row).toBe(1);
expect(cells[1].width).toBe(data.itmWidth);
expect(cells[1].height).toBe(HEIGHT_ITEM);
expect(cells[1].top).toBe(HEIGHT_HEADER);
expect(cells[1].left).toBe(data.itmWidth * 0);
expect(cells[1].tmpl).toBe(TEMPLATE_ITEM);
expect(cells[1].data).toBeUndefined();
expect(cells[1].record).toBe(0);
expect(cells[2].row).toBe(1);
expect(cells[2].width).toBe(data.itmWidth);
expect(cells[2].height).toBe(HEIGHT_ITEM);
expect(cells[2].top).toBe(HEIGHT_HEADER);
expect(cells[2].left).toBe(data.itmWidth * 1);
expect(cells[2].tmpl).toBe(TEMPLATE_ITEM);
expect(cells[2].data).toBeUndefined();
expect(cells[2].record).toBe(1);
expect(cells[3].row).toBe(1);
expect(cells[3].width).toBe(data.itmWidth);
expect(cells[3].height).toBe(HEIGHT_ITEM);
expect(cells[3].top).toBe(HEIGHT_HEADER);
expect(cells[3].left).toBe(data.itmWidth * 2);
expect(cells[3].tmpl).toBe(TEMPLATE_ITEM);
expect(cells[3].data).toBeUndefined();
expect(cells[3].record).toBe(2);
expect(cells[4].row).toBe(2);
expect(cells[4].width).toBe(data.itmWidth);
expect(cells[4].height).toBe(HEIGHT_ITEM);
expect(cells[4].top).toBe(HEIGHT_HEADER + HEIGHT_ITEM);
expect(cells[4].left).toBe(data.itmWidth * 0);
expect(cells[4].tmpl).toBe(TEMPLATE_ITEM);
expect(cells[4].data).toBeUndefined();
expect(cells[4].record).toBe(3);
expect(cells[5].row).toBe(2);
expect(cells[5].width).toBe(data.itmWidth);
expect(cells[5].height).toBe(HEIGHT_ITEM);
expect(cells[5].top).toBe(HEIGHT_HEADER + HEIGHT_ITEM);
expect(cells[5].left).toBe(data.itmWidth * 1);
expect(cells[5].tmpl).toBe(TEMPLATE_ITEM);
expect(cells[5].data).toBeUndefined();
expect(cells[5].record).toBe(4);
expect(cells[6].row).toBe(3);
expect(cells[6].width).toBe(data.ftrWidth);
expect(cells[6].height).toBe(HEIGHT_FOOTER);
expect(cells[6].top).toBe(HEIGHT_HEADER + HEIGHT_ITEM + HEIGHT_ITEM);
expect(cells[6].left).toBe(0);
expect(cells[6].tmpl).toBe(TEMPLATE_FOOTER);
expect(cells[6].data).toBe('Footer');
expect(cells[6].record).toBe(4);
});
it('should process more data', () => {
records = [0,1,2,3,4,5,6,7,8,9];
let stopAtHeight = 100;
data.viewWidth = 200;
data.itmWidth = 90; // 2 per row
data.hdrWidth = data.viewWidth; // 100%, 1 per row
headerFn = function(record) {
return (record === 0) ? 'Header' : null;
};
processRecords(stopAtHeight, records, cells,
headerFn, footerFn, data);
expect(cells.length).toBe(5);
expect(cells[0].row).toBe(0);
expect(cells[0].top).toBe(0);
expect(cells[0].left).toBe(0);
expect(cells[0].tmpl).toBe(TEMPLATE_HEADER);
expect(cells[0].record).toBe(0);
expect(cells[1].row).toBe(1);
expect(cells[1].top).toBe(HEIGHT_HEADER);
expect(cells[1].left).toBe(data.itmWidth * 0);
expect(cells[1].tmpl).toBe(TEMPLATE_ITEM);
expect(cells[1].record).toBe(0);
stopAtHeight = 150;
processRecords(stopAtHeight, records, cells,
headerFn, footerFn, data);
expect(cells[2].row).toBe(1);
expect(cells[2].top).toBe(HEIGHT_HEADER);
expect(cells[2].left).toBe(data.itmWidth * 1);
expect(cells[2].tmpl).toBe(TEMPLATE_ITEM);
expect(cells[2].record).toBe(1);
expect(cells.length).toBe(9);
expect(cells[3].row).toBe(2);
expect(cells[3].top).toBe(HEIGHT_HEADER + HEIGHT_ITEM);
expect(cells[3].left).toBe(data.itmWidth * 0);
expect(cells[3].tmpl).toBe(TEMPLATE_ITEM);
expect(cells[3].record).toBe(2);
stopAtHeight = 20000;
processRecords(stopAtHeight, records, cells,
headerFn, footerFn, data);
expect(cells.length).toBe(11);
expect(cells[5].row).toBe(3);
expect(cells[5].top).toBe(HEIGHT_HEADER + HEIGHT_ITEM + HEIGHT_ITEM);
expect(cells[5].left).toBe(data.itmWidth * 0);
expect(cells[5].tmpl).toBe(TEMPLATE_ITEM);
expect(cells[5].record).toBe(4);
});
});
describe('populateNodeData', () => {
it('should skip already rendered, and create nodes', () => {
cells = [
{row: 0, tmpl: TEMPLATE_ITEM},
{row: 1, tmpl: TEMPLATE_ITEM},
{row: 2, tmpl: TEMPLATE_HEADER},
{row: 3, tmpl: TEMPLATE_ITEM},
{row: 4, tmpl: TEMPLATE_FOOTER},
{row: 5, tmpl: TEMPLATE_ITEM},
{row: 6, tmpl: TEMPLATE_ITEM, reads: 0}
];
nodes = [
{cell: 0, tmpl: TEMPLATE_ITEM, view: getView()},
{cell: 1, tmpl: TEMPLATE_ITEM, view: getView()},
{cell: 2, tmpl: TEMPLATE_HEADER, view: getView()},
{cell: 3, tmpl: TEMPLATE_ITEM, view: getView()},
];
let startCellIndex = 2;
let endCellIndex = 5;
populateNodeData(startCellIndex, endCellIndex, data.viewWidth, true,
cells, records, nodes, viewContainer,
itmTmp, hdrTmp, ftrTmp, false);
expect(nodes.length).toBe(5);
// first stays unchanged
expect(nodes[0].cell).toBe(0);
expect(nodes[1].cell).toBe(5);
expect(nodes[2].cell).toBe(2);
expect(nodes[3].cell).toBe(3);
expect(nodes[4].cell).toBe(4);
});
it('should create nodes', () => {
cells = [
{row: 0, tmpl: TEMPLATE_ITEM},
{row: 1, tmpl: TEMPLATE_ITEM},
{row: 2, tmpl: TEMPLATE_HEADER},
{row: 3, tmpl: TEMPLATE_ITEM},
{row: 4, tmpl: TEMPLATE_FOOTER},
{row: 5, tmpl: TEMPLATE_ITEM},
{row: 6, tmpl: TEMPLATE_ITEM}
];
let startCellIndex = 2;
let endCellIndex = 4;
populateNodeData(startCellIndex, endCellIndex, data.viewWidth, true,
cells, records, nodes, viewContainer,
itmTmp, hdrTmp, ftrTmp, true);
expect(nodes.length).toBe(3);
expect(nodes[0].cell).toBe(2);
expect(nodes[1].cell).toBe(3);
expect(nodes[2].cell).toBe(4);
expect(nodes[0].tmpl).toBe(TEMPLATE_HEADER);
expect(nodes[1].tmpl).toBe(TEMPLATE_ITEM);
expect(nodes[2].tmpl).toBe(TEMPLATE_FOOTER);
});
});
describe('initReadNodes', () => {
it('should get all the row heights w/ 30% width rows', () => {
let firstTop = 3;
nodes = [
{cell: 0, tmpl: TEMPLATE_HEADER, view: getView(data.viewWidth, HEIGHT_HEADER, firstTop, 0)},
{cell: 1, tmpl: TEMPLATE_ITEM, view: getView(90, HEIGHT_ITEM, HEIGHT_HEADER + firstTop, 0)},
{cell: 2, tmpl: TEMPLATE_ITEM, view: getView(90, HEIGHT_ITEM, HEIGHT_HEADER + firstTop, 90)},
{cell: 3, tmpl: TEMPLATE_ITEM, view: getView(90, HEIGHT_ITEM, HEIGHT_HEADER + firstTop, 180)},
{cell: 4, tmpl: TEMPLATE_ITEM, view: getView(90, HEIGHT_ITEM, HEIGHT_HEADER + HEIGHT_ITEM + firstTop, 0)},
{cell: 5, tmpl: TEMPLATE_FOOTER, view: getView(data.viewWidth, HEIGHT_FOOTER, HEIGHT_HEADER + HEIGHT_ITEM + HEIGHT_ITEM + firstTop, 0)},
];
cells = [
{row: 0, tmpl: TEMPLATE_HEADER, reads: 0},
{row: 0, tmpl: TEMPLATE_ITEM, reads: 0},
{row: 0, tmpl: TEMPLATE_ITEM, reads: 0},
{row: 0, tmpl: TEMPLATE_ITEM, reads: 0},
{row: 0, tmpl: TEMPLATE_ITEM, reads: 0},
{row: 0, tmpl: TEMPLATE_FOOTER, reads: 0},
];
initReadNodes(nodes, cells, data);
expect(cells[0].top).toBe(firstTop);
expect(cells[0].left).toBe(0);
expect(cells[0].width).toBe(data.viewWidth);
expect(cells[0].height).toBe(HEIGHT_HEADER);
expect(cells[1].top).toBe(firstTop + HEIGHT_HEADER);
expect(cells[1].left).toBe(0);
expect(cells[1].width).toBe(90);
expect(cells[1].height).toBe(HEIGHT_ITEM);
expect(cells[2].top).toBe(firstTop + HEIGHT_HEADER);
expect(cells[2].left).toBe(data.itmWidth);
expect(cells[2].width).toBe(90);
expect(cells[2].height).toBe(HEIGHT_ITEM);
expect(cells[3].top).toBe(firstTop + HEIGHT_HEADER);
expect(cells[3].left).toBe(data.itmWidth * 2);
expect(cells[3].width).toBe(90);
expect(cells[3].height).toBe(HEIGHT_ITEM);
expect(cells[4].top).toBe(firstTop + HEIGHT_HEADER + HEIGHT_ITEM);
expect(cells[4].left).toBe(0);
expect(cells[4].width).toBe(90);
expect(cells[4].height).toBe(HEIGHT_ITEM);
expect(cells[5].top).toBe(firstTop + HEIGHT_HEADER + HEIGHT_ITEM + HEIGHT_ITEM);
expect(cells[5].left).toBe(0);
expect(cells[5].width).toBe(data.viewWidth);
expect(cells[5].height).toBe(HEIGHT_FOOTER);
});
it('should get all the row heights w/ all 100% width rows', () => {
nodes = [
{cell: 0, tmpl: TEMPLATE_HEADER, view: getView(data.viewWidth, HEIGHT_HEADER, 0, 0)},
{cell: 1, tmpl: TEMPLATE_ITEM, view: getView(data.viewWidth, HEIGHT_ITEM, HEIGHT_HEADER, 0)},
{cell: 2, tmpl: TEMPLATE_ITEM, view: getView(data.viewWidth, HEIGHT_ITEM, HEIGHT_HEADER + HEIGHT_ITEM, 0)},
{cell: 3, tmpl: TEMPLATE_ITEM, view: getView(data.viewWidth, HEIGHT_ITEM, HEIGHT_HEADER + HEIGHT_ITEM + HEIGHT_ITEM, 0)},
{cell: 4, tmpl: TEMPLATE_FOOTER, view: getView(data.viewWidth, HEIGHT_FOOTER, HEIGHT_HEADER + HEIGHT_ITEM + HEIGHT_ITEM + HEIGHT_ITEM, 0)},
];
cells = [
{row: 0, tmpl: TEMPLATE_HEADER, reads: 0},
{row: 1, tmpl: TEMPLATE_ITEM, reads: 0},
{row: 2, tmpl: TEMPLATE_ITEM, reads: 0},
{row: 3, tmpl: TEMPLATE_ITEM, reads: 0},
{row: 4, tmpl: TEMPLATE_FOOTER, reads: 0},
];
initReadNodes(nodes, cells, data);
expect(cells[0].top).toBe(0);
expect(cells[0].height).toBe(HEIGHT_HEADER);
expect(cells[0].reads).toBe(1);
expect(cells[1].top).toBe(HEIGHT_HEADER);
expect(cells[1].height).toBe(HEIGHT_ITEM);
expect(cells[2].top).toBe(HEIGHT_HEADER + HEIGHT_ITEM);
expect(cells[2].height).toBe(HEIGHT_ITEM);
expect(cells[3].top).toBe(HEIGHT_HEADER + HEIGHT_ITEM + HEIGHT_ITEM);
expect(cells[3].height).toBe(HEIGHT_ITEM);
expect(cells[4].top).toBe(HEIGHT_HEADER + HEIGHT_ITEM + HEIGHT_ITEM + HEIGHT_ITEM);
expect(cells[4].height).toBe(HEIGHT_FOOTER);
});
});
describe('adjustRendered', () => {
it('should adjust when all the way to the top, scrolling down', () => {
for (var i = 0; i < 100; i++) {
cells.push({top: i, height: 1, row: i});
}
data.scrollDiff = 1;
data.viewHeight = 20;
data.itmHeight = 1;
data.renderHeight = 40;
data.topViewCell = 0;
data.bottomViewCell = 19;
adjustRendered(cells, data);
expect(data.topCell).toBe(0);
expect(data.bottomCell).toBe(38);
});
it('should adjust when in the middle, scrolling down', () => {
for (var i = 0; i < 100; i++) {
cells.push({top: i, height: 1, row: i});
}
data.scrollDiff = 1;
data.viewHeight = 20;
data.itmHeight = 1;
data.renderHeight = 40;
data.topViewCell = 30;
data.bottomViewCell = 49;
adjustRendered(cells, data);
expect(data.topCell).toBe(27);
expect(data.bottomCell).toBe(65);
});
it('should adjust when all the way to the bottom, scrolling down', () => {
for (var i = 0; i < 100; i++) {
cells.push({top: i, height: 1, row: i});
}
data.scrollDiff = 1;
data.viewHeight = 20;
data.itmHeight = 1;
data.renderHeight = 40;
data.topViewCell = 80;
data.bottomViewCell = 99;
adjustRendered(cells, data);
expect(data.topCell).toBe(77);
expect(data.bottomCell).toBe(99);
});
it('should adjust when all the way to the bottom, scrolling up', () => {
for (var i = 0; i < 100; i++) {
cells.push({top: i, height: 1, row: i});
}
data.scrollDiff = -1;
data.viewHeight = 20;
data.itmHeight = 1;
data.renderHeight = 40;
data.topViewCell = 80;
data.bottomViewCell = 99;
adjustRendered(cells, data);
expect(data.topCell).toBe(61);
expect(data.bottomCell).toBe(99);
});
});
describe('getVirtualHeight', () => {
it('should return known height from last cell/record', () => {
let totalRecords = 1000;
let lastCell: VirtualCell = {
record: 999,
tmpl: TEMPLATE_ITEM,
row: 800,
top: 900,
height: 45
};
let virtualHeight = getVirtualHeight(totalRecords, lastCell);
expect(virtualHeight).toBe(945);
});
it('should guess the height from 1 known cell', () => {
let totalRecords = 100;
let lastCell: VirtualCell = {
record: 0,
tmpl: TEMPLATE_ITEM,
row: 0,
top: 0,
height: 50
};
let virtualHeight = getVirtualHeight(totalRecords, lastCell);
expect(virtualHeight).toBe(5000);
});
it('should guess the height from 1/2 known cells', () => {
let totalRecords = 100;
let lastCell: VirtualCell = {
record: 49,
tmpl: TEMPLATE_ITEM,
row: 0,
top: 2450,
height: 50
};
let virtualHeight = getVirtualHeight(totalRecords, lastCell);
expect(virtualHeight).toBe(5000);
});
it('should guess the height from 99/100 known cells', () => {
let totalRecords = 100;
let lastCell: VirtualCell = {
record: 98,
tmpl: TEMPLATE_ITEM,
row: 0,
top: 4900,
height: 50
};
let virtualHeight = getVirtualHeight(totalRecords, lastCell);
expect(virtualHeight).toBe(5000);
});
});
let records: any[];
let cells: VirtualCell[];
let nodes: VirtualNode[];
let headerFn: Function;
let footerFn: Function;
let data: VirtualData;
let itmTmp = null;
let hdrTmp = null;
let ftrTmp = null;
let viewContainer: any = {
createEmbeddedView: function() {
return getView();
},
indexOf: function() {
return 0;
}
};
function getView(width?:number, height?: number, top?: number, left?: number): any {
return {
setLocal: function(){},
rootNodes: [{
nodeType: 1,
offsetWidth: width,
offsetHeight: height,
offsetTop: top,
offsetLeft: left,
style: {
top: '',
left: ''
},
classList: {
add: function(){},
remove: function(){}
},
setAttribute: function(){},
innerHTML: '',
}]
}
}
});
}
const TEMPLATE_ITEM = 0;
const TEMPLATE_HEADER = 1;
const TEMPLATE_FOOTER = 2;
const HEIGHT_HEADER = 50;
const HEIGHT_ITEM = 45;
const HEIGHT_FOOTER = 32;