in spec/index.js [210:472]
function getTestCases(viewFactory) {
return function () {
beforeEach(doAsync(async () => {
listView = viewFactory({ size: 20000 });
await render();
listView.viewport.scrollTo({ y: 0 });
await sleep(redrawInterval);
}));
afterEach(doAsync(async () => {
listView.remove();
await sleep(redrawInterval);
}));
it('should create the ListView correctly', function () {
expect($('.test-container').get(0)).to.equal(listView.el);
expect($('.test-container > .internal-viewport > ul > li').length).to.be.above(0);
});
it('should fill up the viewport', function () {
const elLast = $('.test-container > .internal-viewport > ul > li').last().get(0);
const rectLast = elLast.getBoundingClientRect();
const height = viewportMetrics().outer.height;
expect(rectLast.bottom).to.be.at.least(height);
});
it('should fill up the viewport after jump scrolling', doAsync(async () => {
for (let scrollTop of [1000, 2000, 20000, 10000]) {
listView.viewport.scrollTo({ y: scrollTop });
await sleep(redrawInterval);
checkViewportFillup();
}
}));
it('should fill up the viewport while scrolling down continuously', doAsync(async () => {
for (let scrollTop = 1000; scrollTop < 1500; scrollTop += 100) {
listView.viewport.scrollTo({ y: scrollTop });
await sleep(redrawInterval);
checkViewportFillup();
}
}));
it('should fill up the viewport while scrolling up continuously', doAsync(async () => {
for (let scrollTop = 2000; scrollTop > 1500; scrollTop -= 100) {
listView.viewport.scrollTo({ y: scrollTop });
await sleep(redrawInterval);
checkViewportFillup();
}
}));
it('should be able to scroll an element to top', doAsync(async () => {
for (let index of [0, 1, 11, 111, 1111, 11111]) {
await scrollToItem(index, 'top');
checkItemLocation(index, 'top');
checkViewportFillup();
}
await scrollToItem(listView.options.items.length - 1, 'top');
checkScrolledToBottom();
checkViewportFillup();
}));
it('should be able to scroll an element to bottom', doAsync(async () => {
for (let index of [11111, 11110, 11100, 11000, 10000]) {
await scrollToItem(index, 'bottom');
checkItemLocation(index, 'bottom');
checkViewportFillup();
}
await scrollToItem(0, 'bottom');
checkScrolledToTop();
checkViewportFillup();
}));
it('should be able to scroll an element to middle', doAsync(async () => {
for (let index of [11111, 11110, 11100, 11000, 10000]) {
await scrollToItem(index, 'middle');
checkItemLocation(index, 'middle');
checkViewportFillup();
}
await scrollToItem(0, 'middle');
checkScrolledToTop();
checkViewportFillup();
await scrollToItem(listView.options.items.length - 1, 'middle');
checkScrolledToBottom();
checkViewportFillup();
}));
it('should be able to scroll an element to certain offset', doAsync(async () => {
const index = 1000;
const height = viewportMetrics().outer.height;
for (let pos of [0, 0.2, 0.5, 0.7, 0.9].map(rate => rate * height)) {
await scrollToItem(index, pos);
checkItemLocation(index, pos);
checkViewportFillup();
}
}));
it('should be scroll item to nearest visible location with "default" option', doAsync(async () => {
await scrollToItem(2000);
checkItemLocation(2000, 'bottom');
await scrollToItem(2001);
checkItemLocation(2001, 'bottom');
await scrollToItem(1000);
checkItemLocation(1000, 'top');
await scrollToItem(999);
checkItemLocation(999, 'top');
listView.scrollToItem(999);
await sleep(redrawInterval);
checkItemLocation(999, 'top');
const top = getElementRect(1000).top;
await scrollToItem(1000);
expect(Math.abs(getElementRect(1000).top - top)).to.be.below(1);
}));
it('should complain about wrong position opitons', function () {
_.each([
true,
'some-where',
{ foo: 'bar' },
['foo', 'bar'],
], pos => {
expect(() => listView.scrollToItem(0, pos)).to.throw('Invalid position');
});
});
it('should complain about the view is not rendered', function () {
const view = viewFactory({ size: 20000 });
const message = 'Cannot scroll before the view is rendered';
expect(() => view.scrollToItem(10)).to.throw(message);
});
it('should be able to reset the defaultItemHeight', doAsync(async () => {
const height = viewportMetrics().inner.height;
await set({ defaultItemHeight: 22 });
expect(viewportMetrics().inner.height).to.be.above(height);
}));
it('should be able to reset the items', doAsync(async () => {
const $ul = $('.test-container > .internal-viewport > ul');
const text = 'hello world!';
await set({ items: [{ text }] });
expect($ul.children().length).to.equal(3);
expect($ul.children().text()).to.equal(text);
await set({ items: [] });
expect($ul.length).to.equal(1);
expect($ul.children().length).to.equal(2);
}));
it('should be able to use duck typed array as items', doAsync(async () => {
const $ul = $('.test-container > .internal-viewport > ul');
const prefix = 'item';
await set({
items: {
length: 50000,
slice(start, stop) {
return _.map(_.range(start, stop), i => ({ text: `${prefix} ${i}` }));
},
},
});
expect($ul.children().first().next().text()).to.equal(`${prefix} 0`);
checkViewportFillup();
}));
it('should be able to reset the model and listTemplate', doAsync(async () => {
const title = 'New Template';
const model = { title };
const listTemplate = alternativeListTemplate;
await set({ model, listTemplate });
const $h2 = $('.test-container > h2');
expect($h2.length).to.equal(1);
expect($h2.text()).to.equal(title);
checkViewportFillup();
}));
it('should be able to reset the itemTemplate', doAsync(async () => {
const prefix = 'item';
const itemTemplate = ({ text }) => `<li>${prefix} - ${text}</li>`;
await set({ itemTemplate });
const $ul = $('.test-container > .internal-viewport > ul');
expect($ul.children().length).to.be.at.least(3);
expect($ul.children().first().next().text()).to.be.equal(`${prefix} - ${listView.itemAt(0).text}`);
checkViewportFillup();
}));
it('should be able to handle the DOM events', doAsync(async () => {
const spy = sinon.spy();
const events = { 'click li': spy };
await set({ events });
const $ul = $('.test-container > .internal-viewport > ul');
$ul.children().first().next().click();
expect(spy).to.be.calledOnce;
}));
it('should be able to handle the ListView events', doAsync(async () => {
const spyWillRedraw = sinon.spy();
const spyDidRedraw = sinon.spy();
const events = { willRedraw: spyWillRedraw, didRedraw: spyDidRedraw };
await set({ events });
expect(spyWillRedraw).have.been.calledOnce;
expect(spyDidRedraw).have.been.calledOnce;
await scrollToItem(1000);
expect(spyWillRedraw).have.been.calledTwice;
expect(spyDidRedraw).have.been.calledTwice;
await set({ events: {} });
expect(spyWillRedraw).have.been.calledTwice;
expect(spyDidRedraw).have.been.calledTwice;
await scrollToItem(0);
expect(spyWillRedraw).have.been.calledTwice;
expect(spyDidRedraw).have.been.calledTwice;
}));
it('should invoke the callback immediatedly if reset with no valid options', function () {
const spy = sinon.spy();
listView.set({ foo: 'bar' }, spy);
expect(spy).to.be.calledOnce;
});
it('should be able to invalidate the rendered items', doAsync(async () => {
const $ul = $('.test-container > .internal-viewport > ul');
const elFirst = $ul.children().get(1);
await new Promise(resolve => listView.invalidate(resolve));
const elFirstNew = $ul.children().get(1);
expect(elFirstNew).not.to.equal(elFirst);
}));
};
}