You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Vestride_Shuffle/test/test.js

740 lines
22 KiB
JavaScript

/* globals describe, it, beforeEach, afterEach */
/* jshint expr: true */
'use strict';
var expect = window.chai.expect;
var sinon = window.sinon;
describe('shuffle', function () {
var Shuffle = window.shuffle;
var fixtures = {};
var fixture;
var instance;
function getFixture(id) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.responseText);
};
xhr.onerror = reject;
xhr.open('GET', 'fixtures/' + id + '.html');
xhr.send();
});
}
function appendFixture(id) {
function insert(content) {
content = content.trim();
var holder = document.createElement('div');
holder.innerHTML = content;
var element = holder.firstChild;
document.body.appendChild(element);
fixture = element;
}
if (fixtures[id]) {
insert(fixtures[id]);
return Promise.resolve();
} else {
return getFixture(id).then(function (html) {
fixtures[id] = html;
insert(html);
});
}
}
function removeFixture() {
if (instance && instance.element) {
instance.destroy();
}
if (fixture) {
fixture.parentNode.removeChild(fixture);
}
instance = null;
fixture = null;
}
function once(element, eventType, fn) {
var handler = function (e) {
element.removeEventListener(eventType, handler);
fn(e);
};
element.addEventListener(eventType, handler);
}
function toArray(thing) {
return Array.prototype.slice.call(thing);
}
function id(id) {
return document.getElementById(id);
}
function whenTransitionDoneStub(element, itemCallback, done) {
setTimeout(done, 0);
}
describe('regular fixture', function () {
beforeEach(function (done) {
// Mock the transition end event wrapper.
sinon.stub(Shuffle.prototype, '_whenTransitionDone').callsFake(whenTransitionDoneStub);
appendFixture('regular').then(done);
});
afterEach(function () {
Shuffle.prototype._whenTransitionDone.restore();
removeFixture();
});
it('should have default options', function () {
instance = new Shuffle(fixture);
expect(instance.items.length).to.equal(10);
expect(instance.visibleItems).to.equal(10);
expect(instance.options.group).to.equal('all');
expect(instance.options.speed).to.equal(250);
expect(instance.options.itemSelector).to.equal('*');
expect(instance.options.sizer).to.equal(null);
expect(instance.options.columnWidth).to.equal(0);
expect(instance.options.gutterWidth).to.equal(0);
expect(instance.options.delimeter).to.equal(null);
expect(instance.options.initialSort).to.equal(null);
expect(instance.options.throttleTime).to.equal(300);
expect(instance.useSizer).to.equal(false);
expect(instance.id).to.equal('shuffle_0');
expect(instance.isInitialized).to.be.true;
});
it('should add classes and default styles', function () {
instance = new Shuffle(fixture);
expect(instance.element).to.have.class('shuffle');
var styles = window.getComputedStyle(instance.element, null);
expect(styles.position).to.equal('relative');
expect(styles.overflow).to.equal('hidden');
expect(instance.containerWidth).not.to.equal(0);
instance.items.forEach(function (item) {
expect(item.element).to.have.class('shuffle-item');
expect(item.element).to.have.class('shuffle-item--visible');
expect(item.element.style.opacity).to.be.defined;
expect(item.element.style.position).to.equal('absolute');
expect(item.element.style.visibility).to.equal('visible');
expect(item.isVisible).to.equal(true);
expect(item.scale).to.equal(Shuffle.ShuffleItem.Scale.VISIBLE);
expect(item.point.x).to.be.defined;
expect(item.point.y).to.be.defined;
});
});
it('should be 3 columns with gutters', function () {
fixture.style.width = '1000px';
for (var i = 0; i < fixture.children.length; i++) {
fixture.children[i].style.width = '300px';
fixture.children[i].style.height = '150px';
}
instance = new Shuffle(fixture, {
columnWidth: 300,
gutterWidth: 50,
});
expect(instance.colWidth).to.equal(350);
expect(instance.cols).to.equal(3);
expect(instance.positions).to.deep.equal([600, 450, 450]);
});
it('can have a function for columns and gutters', function () {
fixture.style.width = '1000px';
for (var i = 0; i < fixture.children.length; i++) {
fixture.children[i].style.width = '300px';
fixture.children[i].style.height = '150px';
}
instance = new Shuffle(fixture, {
columnWidth: function (containerWidth) {
expect(containerWidth).to.equal(1000);
return 300;
},
gutterWidth: function () {
return 50;
},
});
expect(instance._getGutterSize(1000)).to.equal(50);
expect(instance._getColumnSize(1000, 50)).to.equal(350);
expect(instance.colWidth).to.equal(350);
expect(instance.cols).to.equal(3);
expect(instance.positions).to.deep.equal([600, 450, 450]);
});
it('can have a function for columns/gutters and span multiple columns', function () {
fixture.style.width = '1200px';
for (var i = 0; i < fixture.children.length; i++) {
fixture.children[i].style.width = '300px';
fixture.children[i].style.height = '10px';
}
fixture.children[1].style.width = '600px';
fixture.children[5].style.width = '600px';
fixture.children[6].style.width = '900px';
instance = new Shuffle(fixture, {
columnWidth: function (containerWidth) {
expect(containerWidth).to.equal(1200);
return 300;
},
gutterWidth: function () {
return 0;
},
});
expect(instance._getGutterSize(1200)).to.equal(0);
expect(instance._getColumnSize(1200, 0)).to.equal(300);
expect(instance.colWidth).to.equal(300);
expect(instance.cols).to.equal(4);
expect(instance.positions).to.deep.equal([40, 40, 30, 30]);
});
it('can filter by the data attribute', function (done) {
instance = new Shuffle(fixture, {
speed: 0,
});
function second() {
expect(instance.visibleItems).to.equal(3);
var hidden = [3, 4, 5, 6, 7, 8, 10].map(function (num) {
return id('item' + num);
});
var visible = [1, 2, 9].map(function (num) {
return id('item' + num);
});
hidden.forEach(function (element) {
expect(element).to.have.class(Shuffle.Classes.HIDDEN);
expect(element.style.visibility).to.equal('hidden');
});
visible.forEach(function (element) {
expect(element).to.have.class(Shuffle.Classes.VISIBLE);
expect(element.style.visibility).to.equal('visible');
});
once(fixture, Shuffle.EventType.LAYOUT, third);
// Filter by green.
instance.filter('green');
}
function third() {
expect(instance.visibleItems).to.equal(2);
var hidden = [1, 2, 5, 6, 7, 8, 9, 10].map(function (num) {
return id('item' + num);
});
var visible = [3, 4].map(function (num) {
return id('item' + num);
});
hidden.forEach(function (element) {
expect(element).to.have.class(Shuffle.Classes.HIDDEN);
expect(element.style.visibility).to.equal('hidden');
});
visible.forEach(function (element) {
expect(element).to.have.class(Shuffle.Classes.VISIBLE);
expect(element.style.visibility).to.equal('visible');
});
done();
}
once(fixture, Shuffle.EventType.LAYOUT, second);
instance.filter('design');
});
it('can initialize filtered and the category parameter is optional', function () {
instance = new Shuffle(fixture, {
speed: 40,
group: 'design',
});
expect(instance.visibleItems).to.equal(3);
});
it('can initialize sorted', function () {
var sortObj = {
by: function (element) {
return parseInt(element.getAttribute('data-age'), 10);
},
};
instance = new Shuffle(fixture, {
speed: 40,
initialSort: sortObj,
});
expect(instance.lastSort).to.deep.equal(sortObj);
});
it('can calculate column spans', function () {
// itemWidth, columnWidth, columns, threshold
expect(Shuffle.__getColumnSpan(50, 100, 3, 0)).to.equal(1);
expect(Shuffle.__getColumnSpan(200, 100, 3, 0)).to.equal(2);
expect(Shuffle.__getColumnSpan(200, 200, 3, 0)).to.equal(1);
expect(Shuffle.__getColumnSpan(300, 100, 3, 0)).to.equal(3);
// Column span should not be larger than the number of columns.
expect(Shuffle.__getColumnSpan(300, 50, 3, 0)).to.equal(3);
// Fix for percentage values.
expect(Shuffle.__getColumnSpan(100.02, 100, 4, 0)).to.equal(2);
expect(Shuffle.__getColumnSpan(100.02, 100, 4, 0.01)).to.equal(1);
expect(Shuffle.__getColumnSpan(99.98, 100, 4, 0.01)).to.equal(1);
});
it('can calculate column sets', function () {
// getAvailablePositions(positions, columnSpan, columns)
var positions = [150, 0, 0, 0];
expect(Shuffle.__getAvailablePositions(positions, 1, 4)).to.deep.equal([150, 0, 0, 0]);
expect(Shuffle.__getAvailablePositions(positions, 2, 4)).to.deep.equal([150, 0, 0]);
});
it('can get an element option', function () {
instance = new Shuffle(fixture);
var first = fixture.firstElementChild;
expect(instance._getElementOption(first)).to.equal(first);
expect(instance._getElementOption('#item1')).to.equal(first);
expect(instance._getElementOption('#hello-world')).to.be.null;
expect(instance._getElementOption(null)).to.be.null;
expect(instance._getElementOption(undefined)).to.be.null;
expect(instance._getElementOption(function () {
return first;
})).to.be.null;
});
it('can test elements against filters', function () {
instance = new Shuffle(fixture);
var first = fixture.firstElementChild;
expect(instance._doesPassFilter('design', first)).to.be.true;
expect(instance._doesPassFilter('black', first)).to.be.false;
expect(instance._doesPassFilter(function (element) {
expect(element).to.exist;
return element.getAttribute('data-age') === '21';
}, first)).to.equal(true);
expect(instance._doesPassFilter(function (element) {
return element.getAttribute('data-age') === '22';
}, first)).to.equal(false);
// Arrays.
expect(instance._doesPassFilter(['design'], first)).to.be.true;
expect(instance._doesPassFilter(['red'], first)).to.be.true;
expect(instance._doesPassFilter(['design', 'black'], first)).to.be.true;
// Change filter mode.
instance.options.filterMode = Shuffle.FilterMode.ALL;
expect(instance._doesPassFilter(['design'], first)).to.be.true;
expect(instance._doesPassFilter(['design', 'red'], first)).to.be.true;
expect(instance._doesPassFilter(['design', 'black'], first)).to.be.false;
});
it('will maintain the last sort object', function () {
instance = new Shuffle(fixture);
var initialSort = instance.lastSort;
instance.sort();
expect(instance.lastSort).to.deep.equal(initialSort);
instance.sort({ glen: true });
expect(instance.lastSort).to.deep.equal({ glen: true });
instance.sort();
expect(instance.lastSort).to.deep.equal({ glen: true });
});
it('should reset columns', function () {
instance = new Shuffle(fixture);
expect(instance.cols).to.be.above(0);
instance._resetCols();
var positions = new Array(instance.cols);
for (var i = 0; i < instance.cols; i++) {
positions[i] = 0;
}
expect(instance.positions).to.deep.equal(positions);
});
it('should destroy properly', function () {
instance = new Shuffle(fixture);
instance.destroy();
expect(instance.element).to.be.null;
expect(instance.items).to.be.null;
expect(instance.options.sizer).to.be.null;
expect(instance.isDestroyed).to.be.true;
expect(fixture).to.not.have.class('shuffle');
toArray(fixture.children).forEach(function (child) {
expect(child).to.not.have.class('shuffle-item');
expect(child).to.not.have.class('shuffle-item--visible');
expect(child).to.not.have.class('shuffle-item--hidden');
});
});
it('should not update or shuffle when disabled or destroyed', function () {
instance = new Shuffle(fixture);
var update = sinon.spy(instance, 'update');
var _filter = sinon.spy(instance, '_filter');
instance.disable();
instance.filter('design');
expect(_filter.called).to.be.false;
expect(update.called).to.be.false;
instance.enable(false);
instance.destroy();
instance._onResize();
expect(update.called).to.be.false;
});
it('should not update when the container is the same size', function () {
instance = new Shuffle(fixture);
var update = sinon.spy(instance, 'update');
instance._onResize();
expect(update.called).to.be.false;
});
describe('removing elements', function () {
var children;
beforeEach(function () {
children = toArray(fixture.children);
});
afterEach(function () {
children = null;
});
it('can remove items', function (done) {
instance = new Shuffle(fixture, {
speed: 16,
});
once(fixture, Shuffle.EventType.REMOVED, function (evt) {
var detail = evt.detail;
expect(detail.shuffle.visibleItems).to.equal(8);
expect(detail.collection[0].id).to.equal('item1');
expect(detail.collection[1].id).to.equal('item2');
expect(detail.shuffle.element.children).to.have.lengthOf(8);
expect(instance.isTransitioning).to.be.false;
done();
});
var itemsToRemove = children.slice(0, 2);
instance.remove(itemsToRemove);
});
it('can remove items without transforms', function (done) {
instance = new Shuffle(fixture, {
speed: 100,
useTransforms: false,
});
once(fixture, Shuffle.EventType.REMOVED, function (evt) {
var detail = evt.detail;
expect(detail.shuffle.visibleItems).to.equal(8);
expect(detail.collection[0].id).to.equal('item2');
expect(detail.collection[1].id).to.equal('item3');
expect(detail.shuffle.element.children).to.have.lengthOf(8);
expect(detail.shuffle.isTransitioning).to.be.false;
done();
});
var itemsToRemove = children.slice(1, 3);
instance.remove(itemsToRemove);
});
});
it('can get the real width of an element which is scaled', function () {
var div = document.createElement('div');
div.style.cssText = 'width:100px;height:100px;';
div.style.transform = 'translate(0px,0px) scale(0.5)';
document.body.appendChild(div);
expect(Shuffle.getSize(div, false).width).to.equal(100);
expect(Shuffle.getSize(div, true).width).to.equal(100);
expect(Shuffle.getSize(div, false).height).to.equal(100);
expect(Shuffle.getSize(div, true).height).to.equal(100);
div.style.marginLeft = '10px';
div.style.marginRight = '20px';
div.style.marginTop = '30px';
div.style.marginBottom = '40px';
expect(Shuffle.getSize(div, false).width).to.equal(100);
expect(Shuffle.getSize(div, true).width).to.equal(130);
expect(Shuffle.getSize(div, false).height).to.equal(100);
expect(Shuffle.getSize(div, true).height).to.equal(170);
document.body.removeChild(div);
});
describe('inserting elements', function () {
var items = [];
beforeEach(function () {
var eleven = document.createElement('div');
eleven.className = 'item';
eleven.setAttribute('data-age', 36);
eleven.setAttribute('data-groups', '["ux", "black"]');
eleven.id = 'item11';
eleven.textContent = 'Person 11';
var twelve = document.createElement('div');
twelve.className = 'item';
twelve.setAttribute('data-age', 37);
twelve.setAttribute('data-groups', '["strategy", "blue"]');
twelve.id = 'item12';
twelve.textContent = 'Person 12';
items.push(eleven, twelve);
instance = new Shuffle(fixture, {
speed: 100,
group: 'black',
});
});
afterEach(function (done) {
once(fixture, Shuffle.EventType.LAYOUT, function () {
items.length = 0;
done();
});
});
it('can add items', function () {
fixture.appendChild(items[0]);
fixture.appendChild(items[1]);
instance.add(items);
// Already 2 in the items, plus number 11.
expect(instance.visibleItems).to.equal(3);
expect(instance.items).to.have.lengthOf(12);
});
it('can prepend items', function () {
fixture.insertBefore(items[1], fixture.firstElementChild);
fixture.insertBefore(items[0], fixture.firstElementChild);
instance.add(items);
expect(instance.items[0].element).to.equal(items[0]);
expect(instance.items[1].element).to.equal(items[1]);
expect(instance.items).to.have.lengthOf(12);
});
});
});
describe('delimeter fixture', function () {
beforeEach(function (done) {
// Mock the transition end event wrapper.
sinon.stub(Shuffle.prototype, '_whenTransitionDone').callsFake(whenTransitionDoneStub);
appendFixture('delimeter').then(done);
});
afterEach(function () {
Shuffle.prototype._whenTransitionDone.restore();
removeFixture();
});
it('can have a custom delimeter', function () {
instance = new Shuffle(fixture, {
delimeter: ',',
group: 'design',
});
expect(instance.visibleItems).to.equal(3);
});
});
describe('Custom shuffle item styles', function () {
var original = Shuffle.ShuffleItem.Css;
beforeEach(function (done) {
appendFixture('regular').then(done);
});
afterEach(function () {
Shuffle.ShuffleItem.Css = original;
removeFixture();
});
it('will apply before and after styles even if the item will not move', function () {
Shuffle.ShuffleItem.Css.INITIAL.opacity = 0;
instance = new Shuffle(fixture, { speed: 0 });
// The layout method will have already set styles to their 'after' states
// upon initialization. Reset them here.
instance.items.forEach(function (item) {
item.applyCss(Shuffle.ShuffleItem.Css.INITIAL);
});
instance.items.forEach(function (item) {
expect(item.element.style.opacity).to.equal('0');
});
instance._layout(instance.items);
instance._processQueue();
instance.items.forEach(function (item) {
expect(item.element.style.opacity).to.equal('1');
});
});
});
describe('the sorter', function () {
var items;
var clone;
beforeEach(function (done) {
appendFixture('regular').then(function () {
items = toArray(fixture.children).map(function (element) {
return { element: element };
});
clone = toArray(items);
done();
});
});
afterEach(function () {
items.length = 0;
clone.length = 0;
removeFixture();
});
it('will catch empty objects', function () {
expect(Shuffle.__sorter({})).to.deep.equal([]);
});
it('can randomize the elements', function () {
expect(items).to.have.lengthOf(10);
expect(Shuffle.__sorter(items)).to.deep.equal(items);
var clone = toArray(items);
expect(Shuffle.__sorter(clone, { randomize: true })).to.not.deep.equal(items);
});
it('can sort by a function', function () {
clone.sort(function (a, b) {
var age1 = parseInt(a.element.getAttribute('data-age'), 10);
var age2 = parseInt(b.element.getAttribute('data-age'), 10);
return age1 - age2;
});
var result = Shuffle.__sorter(items, {
by: function (element) {
expect(element).to.exist;
expect(element.nodeType).to.equal(1);
return parseInt(element.getAttribute('data-age'), 10);
},
});
expect(result).to.deep.equal(clone);
});
it('can sort by a function and reverse it', function () {
clone.sort(function (a, b) {
var age1 = parseInt(a.element.getAttribute('data-age'), 10);
var age2 = parseInt(b.element.getAttribute('data-age'), 10);
return age1 - age2;
}).reverse();
var result = Shuffle.__sorter(items, {
reverse: true,
by: function (element) {
return parseInt(element.getAttribute('data-age'), 10);
},
});
expect(result).to.deep.equal(clone);
});
it('will revert to DOM order if the `by` function returns undefined', function () {
var count = 0;
expect(Shuffle.__sorter(items, {
by: function () {
count++;
return count < 5 ? Math.random() : undefined;
},
})).to.deep.equal(clone);
});
it('can sort things to the top', function () {
items = items.slice(0, 4);
var final = [items[1], items[0], items[3], items[2]];
expect(Shuffle.__sorter(items, {
by: function (element) {
var age = parseInt(element.getAttribute('data-age'), 10);
if (age === 50) {
return 'sortFirst';
} else {
return age;
}
},
})).to.deep.equal(final);
});
it('can sort things to the bottom', function () {
items = items.slice(0, 4);
var final = [items[0], items[2], items[1], items[3]];
expect(Shuffle.__sorter(items, {
by: function (element) {
var age = parseInt(element.getAttribute('data-age'), 10);
if (age === 27) {
return 'sortLast';
} else {
return age;
}
},
})).to.deep.equal(final);
});
});
});