test/basic.test.js
/* eslint-disable max-lines */
import {
assert, fixture, html, oneEvent, waitUntil
} from '@open-wc/testing';
import sinon from 'sinon';
import {
visibilityFixture, defaultsFixture, flushRenderQueue, getItems, setupFixture
} from './helpers/utils.js';
import '../cosmoz-data-nav.js';
import './helpers/cosmoz-data-nav-test-view.js';
sinon.assert.expose(assert, { prefix: '' });
suite('defaults', () => {
let nav;
suiteSetup(async () => {
nav = await setupFixture();
});
test('creates buffer elements', () => {
assert.lengthOf(nav._elements, nav.elementsBuffer);
});
test('has template for elements', () => {
const template = nav.$.templatesSlot.querySelector('template');
assert.equal(template, nav._elementsTemplate);
});
test('buffer elements have incomplete content', () => {
nav._elements.forEach(element => assert.isNotNull(element.__incomplete));
});
test('buffer elements are not templatized without complete data', () => {
nav._elements.forEach(element => assert.isUndefined(element.__instance));
});
test('selects first element', () => {
assert.equal(nav.selected, 0);
const selectedEl = nav._getElement(nav.selected);
assert.include(nav._elements, selectedEl);
});
});
suite('properties check', () => {
let nav;
suiteSetup(async () => {
nav = await setupFixture();
});
test('selected property is updated', () => {
nav.select(1);
assert.equal(nav.selected, 1, 'Expected the index of selected item to be 1');
nav.select(3);
assert.equal(nav.selected, 3, 'Expected the index of selected item to be 3');
nav.select(7);
assert.equal(nav.selected, 7, 'Expected the index of selected item to be 7');
nav.select(2);
assert.equal(nav.selected, 2, 'Expected the index of selected item to be 2');
});
test('selectedNext is updated', () => {
nav.select(13);
assert.equal(nav.selectedNext, 14);
nav.select(2);
assert.equal(nav.selectedNext, 3);
nav.select(7);
assert.equal(nav.selectedNext, 8);
nav.select(0);
assert.equal(nav.selectedNext, 1);
});
test('queueLength returns the length of items array', () => {
assert.equal(nav.queueLength, 20);
});
test('reverse property is working', () => {
nav.select(5);
assert.isFalse(nav.reverse);
nav.select(15);
assert.isFalse(nav.reverse);
nav.select(5);
assert.isTrue(nav.reverse);
nav.select(2);
assert.isTrue(nav.reverse);
nav.select(3);
assert.isFalse(nav.reverse);
});
});
suite('duplicate ids', () => {
let nav,
warnSpy;
suiteSetup(async () => {
nav = await fixture(defaultsFixture);
nav._templatesObserver.flush();
const items = getItems();
items[0] = '0';
items[1] = '0';
nav.items = items;
warnSpy = sinon.spy(console, 'warn');
});
suiteTeardown(() => {
warnSpy.restore();
});
test('setItemById handlers duplicate ids', done => {
const data = { id: 0 },
cache = nav.haunted.cache;
nav.setItemById('0', data);
sinon.assert.calledWith(warnSpy,
'Multiple replaceable items matches idPath',
'id',
'with id',
'0',
'in the item list', nav.items,
'to replace with item', { id: 0 });
assert.equal(cache.get('0').id, data.id);
assert.equal(nav.items[0].id, data.id);
assert.equal(nav.items[1].id, data.id);
done();
});
});
suite('lacks template', () => {
let warnSpy;
suiteSetup(async () => {
warnSpy = sinon.spy(console, 'warn');
await fixture(html`<cosmoz-data-nav><b></b></cosmoz-data-nav>`);
});
test('data-nav warns about missing template', () => {
sinon.assert.calledWith(warnSpy, 'cosmoz-data-nav requires a template');
});
suiteTeardown(() => {
warnSpy.restore();
});
});
suite('cache', () => {
let nav;
suiteSetup(async () => {
nav = await fixture(defaultsFixture);
nav._templatesObserver.flush();
});
setup(() => {
nav.items = getItems();
});
teardown(() => {
nav.haunted.cache.clear();
});
test('cache stores one item', () => {
const cache = nav.haunted.cache;
nav.setItemById('1', { id: 88 });
assert.equal(cache.get('1').id, 88);
});
test('cache stores two items', () => {
const cache = nav.haunted.cache;
nav.setItemById('1', { id: 88 });
nav.setItemById('2', { id: 99 });
assert.equal(cache.get('1').id, 88);
assert.equal(cache.get('2').id, 99);
});
test('haunted.cache.clear method works', () => {
const cache = nav.haunted.cache;
nav.setItemById('1', { id: 1 });
nav.setItemById('2', { id: 2 });
assert.equal(cache.get('1').id, 1);
assert.equal(cache.get('2').id, 2);
nav.haunted.cache.clear();
assert.isUndefined(cache.get('1'));
assert.isUndefined(cache.get('2'));
});
test('haunted.cache.dropItem method works', () => {
const cache = nav.haunted.cache;
nav.setItemById('1', { id: 88 });
nav.setItemById('2', { id: 99 });
nav.setItemById('3', { id: 11 });
assert.equal(cache.get('1').id, 88);
assert.equal(cache.get('2').id, 99);
assert.equal(cache.get('3').id, 11);
nav.haunted.cache.dropItem(cache.get('2'));
assert.isUndefined(nav.haunted.cache.get('2'));
nav.haunted.cache.dropItem(cache.get('1'));
assert.isUndefined(nav.haunted.cache.get('1'));
});
test('haunted.cache.dropItem called with null or unknown item', () => {
const cache = nav.haunted.cache;
let cacheKeys;
nav.setItemById('1', { id: 900 });
assert.equal(cache.get('1').id, 900);
cacheKeys = Object.keys(cache);
nav.haunted.cache.dropItem(null);
assert.equal(cacheKeys.length, Object.keys(cache).length);
cacheKeys = Object.keys(cache);
nav.haunted.cache.dropItem({});
assert.equal(cacheKeys.length, Object.keys(cache).length);
});
});
suite('other methods', () => {
let nav;
setup(async () => {
nav = await setupFixture();
});
test('setItemById warns about a unknown item', () => {
const warnSpy = sinon.spy(console, 'warn'),
data = { id: 23 };
nav.setItemById('23', data);
sinon.assert.calledWith(warnSpy,
'List item replacement failed, no matching idPath',
'id',
'with id',
'23',
'in the item list',
nav.items,
'to replace with item',
data);
warnSpy.restore();
});
test('isIncompleteFn checks if an item is incomplete', () => {
assert.isTrue(nav.isIncompleteFn());
assert.isTrue(nav.isIncompleteFn(null));
assert.isFalse(nav.isIncompleteFn({ id: '1' }));
nav.setItemById('1', { id: 1 });
assert.isTrue(nav.isIncompleteFn(nav.items[0]));
assert.isFalse(nav.isIncompleteFn(nav.items[1]));
});
test('selectById updates selected property', () => {
nav.setItemById('0', { id: 0 });
nav.setItemById('14', { id: 14 });
nav.setItemById('2', { id: 2 });
nav.setItemById('9', { id: 9 });
nav.setItemById('17', { id: 17 });
nav.selectById(2);
assert.equal(nav.selected, 2);
nav.selectById(14);
assert.equal(nav.selected, 14);
nav.selectById(0);
assert.equal(nav.selected, 0);
nav.selectById(17);
assert.equal(nav.selected, 17);
});
test('preloads data', async () => {
nav.haunted.cache.clear();
setTimeout(() => {
nav.items = getItems();
});
let event = await oneEvent(nav, 'need-data');
assert.equal(event.detail.id, '0');
const data = { id: '0' };
assert.isTrue(nav.isIncompleteFn(nav.items[0]));
assert.isTrue(nav.isIncompleteFn(nav.items[1]));
setTimeout(() => {
nav.setItemById('0', data);
});
event = await oneEvent(nav, 'need-data');
assert.equal(event.detail.id, '1');
assert.isFalse(nav.isIncompleteFn(nav.items[0]));
assert.isTrue(nav.isIncompleteFn(nav.items[1]));
assert.deepEqual(nav.items[0], data);
});
});
suite('navigation', () => {
let nav;
suiteSetup(async () => {
nav = await setupFixture();
});
test('selects next item', async () => {
nav.items = [0, 1];
nav.setItemById('0', { id: 0 });
nav.setItemById('1', { id: 1 });
nav._renderQueue();
nav._renderQueue();
const firstElement = nav._getElement(0),
instance = nav._getInstance(firstElement);
let nextBtn; /* eslint-disable-next-line no-return-assign */
await waitUntil(() => {
nextBtn = instance.querySelector('[cosmoz-data-nav-select="+1"]');
return !!nextBtn;
});
nextBtn.click();
await waitUntil(() => nav._selectDebouncer);
assert.isOk(nav._selectDebouncer);
nav._selectDebouncer.flush();
assert.isTrue(nav.animating);
assert.equal(nav.selected, 1);
// wait for animation to end and check animating is false
await oneEvent(nav, 'transitionend');
assert.isFalse(nav.animating);
});
});
suite('elements buffer', () => {
let nav;
setup(async () => {
nav = await setupFixture(html`
<cosmoz-data-nav elements-buffer="4">
<template>
<cosmoz-data-nav-test-view class="fit layout vertical" item="{{ item }}" index="[[ index ]]"></cosmoz-data-nav-test-view>
</template>
</cosmoz-data-nav>
`);
});
test('elementsBuffer property updates _elements', () => {
assert.equal(nav.elementsBuffer, 4);
assert.equal(nav._elements.length, 4);
});
test('_createElement does not create element if length is equal to elementsBuffer', () => {
assert.equal(nav.elementsBuffer, 4);
assert.equal(nav._elements.length, 4);
nav._createElement();
assert.equal(nav._elements.length, 4);
});
test('_createElement returns new element', () => {
const el = nav._createElement();
assert.equal(el.classList[0], 'animatable');
});
});
suite('renderQueue', () => {
let nav;
setup(async () => {
[, nav] = await Promise.all([
fixture(visibilityFixture),
setupFixture(html`
<cosmoz-data-nav elements-buffer="5">
<template>
<cosmoz-data-nav-test-view class="fit layout vertical" item="{{ item }}" index="[[ index ]]"></cosmoz-data-nav-test-view>
</template>
</cosmoz-data-nav>
`)
]);
});
test('selected instance notify prop', async () => {
nav.items = [{ id: '0' }, { id: '0' }, { id: '0' }];
nav._templatesObserver.flush();
flushRenderQueue(nav);
nav.selected = 1;
const inst = nav.selectedInstance;
inst.item = { ...inst.item };
});
test('renderQueue three items', async () => {
nav._elements.forEach(element => assert.isUndefined(element.__instance));
nav.setItemById('0', { id: 0 });
nav.setItemById('1', { id: 1 });
nav.setItemById('2', { id: 2 });
await waitUntil(() => nav._elements[2].__instance);
assert.deepEqual(nav._elements[0].__instance.item, { id: 0 });
assert.deepEqual(nav._elements[1].__instance.item, { id: 1 });
assert.deepEqual(nav._elements[2].__instance.item, { id: 2 });
});
test('renderQueue five items', async () => {
nav._elements.forEach(element => assert.isUndefined(element.__instance));
const ids = [0, 1, 2, 3, 4];
ids.forEach(id => {
nav.setItemById(id.toString(), { id });
});
await waitUntil(() => nav._elements[4].__instance);
assert.equal(nav._elements.length, 5);
nav._elements.forEach((el, id) => {
assert.deepEqual(el.__instance.item, { id });
});
});
});
suite('renderItem', () => {
let nav,
itemsRendered = 0;
const renderItem = (item, index) => {
itemsRendered += 1;
return html`
<cosmoz-data-nav-test-view class="fit layout vertical" item="${ item }" index="${ index }">
</cosmoz-data-nav-test-view>
`;
};
suiteSetup(async () => {
[, nav] = await Promise.all([
fixture(visibilityFixture),
setupFixture(html`
<cosmoz-data-nav .renderItem="${ renderItem }">
</cosmoz-data-nav>
`)
]);
});
test('renders items', async () => {
nav.items = [{ id: '0' }, { id: '1' }, { id: '2' }];
flushRenderQueue(nav);
assert.equal(itemsRendered, nav.items.map(nav.isIncompleteFn).filter(i => !i).length);
});
});