mixer-dataset.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. 'use strict';
  2. require('jsdom-global')();
  3. const chai = require('chai');
  4. const dom = require('../mock/dom');
  5. const mixitup = require('../../dist/mixitup.js');
  6. const JSONDataset = require('../mock/dataset');
  7. const dataset = JSONDataset.map(data => new dom.Item(data));
  8. chai.use(require('chai-shallow-deep-equal'));
  9. chai.use(require('chai-as-promised'));
  10. describe('mixitup()', () => {
  11. it('should throw an error if `load.dataset` does not match pre-rendered targets', () => {
  12. const emptyContainer = dom.getEmptyContainer();
  13. chai.assert.throws(() => {
  14. mixitup(emptyContainer, {
  15. load: {
  16. dataset: dataset
  17. }
  18. });
  19. }, mixitup.messages.errorDatasetPrerenderedMismatch());
  20. });
  21. it('should throw an error if UID not provided in dataset API mode', () => {
  22. const container = dom.getContainer();
  23. let mixer;
  24. chai.assert.throws(() => {
  25. mixer = mixitup(container, {
  26. load: {
  27. dataset: dataset
  28. }
  29. });
  30. }, mixitup.messages.errorConfigDataUidKeyNotSet());
  31. });
  32. it('should instantiate in dataset API mode when provided with `load.dataset` and a matching container', () => {
  33. const container = dom.getContainer();
  34. const targets = Array.prototype.slice.call(container.children);
  35. const mixer = mixitup(container, {
  36. data: {
  37. uidKey: 'id'
  38. },
  39. load: {
  40. dataset: dataset
  41. }
  42. });
  43. const state = mixer.getState();
  44. chai.assert.equal(state.activeFilter, null);
  45. chai.assert.equal(state.activeSort, null);
  46. chai.assert.deepEqual(state.activeDataset, dataset);
  47. chai.assert.deepEqual(state.targets, targets);
  48. chai.assert.deepEqual(state.show, targets);
  49. chai.assert.deepEqual(state.matching, []);
  50. mixer.destroy();
  51. });
  52. });
  53. describe('mixitup.Mixer', () => {
  54. describe('#dataset()', () => {
  55. const container = dom.getContainer();
  56. const workingDataset = dataset.slice();
  57. const config = {
  58. data: {
  59. uidKey: 'id',
  60. dirtyCheck: true
  61. },
  62. render: {
  63. target: mixitup.h.template(dom.ITEM_TEMPLATE)
  64. },
  65. load: {
  66. dataset: dataset
  67. }
  68. };
  69. const mixer = mixitup(container, config);
  70. const startTotalWhitespace = dom.getTotalWhitespace(container.outerHTML);
  71. after(() => mixer.destroy());
  72. it('should throw an error if an item is added to the dataset, without a render function defined', () => {
  73. const newDataset = dataset.slice();
  74. const container = dom.getContainer();
  75. const erMixer = mixitup(container, {
  76. data: {
  77. uidKey: 'id'
  78. },
  79. load: {
  80. dataset: dataset
  81. }
  82. });
  83. newDataset.push(new dom.Item({
  84. id: 99,
  85. categories: ['d']
  86. }));
  87. chai.assert.throws(() => {
  88. erMixer.dataset(newDataset);
  89. }, mixitup.messages.errorDatasetRendererNotSet());
  90. });
  91. it('should throw an error if an item is added to the dataset without a valid UID', () => {
  92. const newDataset = dataset.slice();
  93. const container = dom.getContainer();
  94. const erMixer = mixitup(container, config);
  95. newDataset.push(new dom.Item({
  96. categories: ['d']
  97. }));
  98. chai.assert.throws(() => {
  99. erMixer.dataset(newDataset);
  100. }, mixitup.messages.errorDatasetInvalidUidKey({
  101. uidKey: 'id'
  102. }));
  103. });
  104. it('should throw an error if an item with a duplicate UID is added to the dataset', () => {
  105. const newDataset = dataset.slice();
  106. const container = dom.getContainer();
  107. const erMixer = mixitup(container, config);
  108. newDataset.push(new dom.Item({
  109. id: 'target-1',
  110. categories: ['d']
  111. }));
  112. chai.assert.throws(() => {
  113. erMixer.dataset(newDataset);
  114. }, mixitup.messages.errorDatasetDuplicateUid({
  115. uid: 'target-1'
  116. }));
  117. });
  118. it('should insert a target when a new item is added to end of the dataset', () => {
  119. workingDataset.push(new dom.Item({
  120. id: 7,
  121. categories: ['d']
  122. }));
  123. return mixer.dataset(workingDataset)
  124. .then((state) => {
  125. chai.assert.equal(state.totalShow, 7);
  126. chai.assert.equal(state.show[6].id, '7');
  127. chai.assert.isOk(state.show[6].matches('.category-d'));
  128. });
  129. });
  130. it('should insert a target when a new item is added to the start of the dataset', () => {
  131. workingDataset.unshift(new dom.Item({
  132. id: 0,
  133. categories: ['d']
  134. }));
  135. return mixer.dataset(workingDataset)
  136. .then((state) => {
  137. chai.assert.equal(state.totalShow, 8);
  138. chai.assert.equal(state.show[0].id, '0');
  139. chai.assert.isOk(state.show[0].matches('.category-d'));
  140. });
  141. });
  142. it('should insert a target when a new item is added at an arbitrary point in the dataset', () => {
  143. workingDataset.splice(3, 0, new dom.Item({
  144. id: 999,
  145. categories: ['d']
  146. }));
  147. return mixer.dataset(workingDataset)
  148. .then((state) => {
  149. chai.assert.equal(state.totalShow, 9);
  150. chai.assert.equal(state.show[3].id, '999');
  151. chai.assert.isOk(state.show[3].matches('.category-d'));
  152. });
  153. });
  154. it('should remove a target when an item is removed from the end of the dataset', () => {
  155. workingDataset.pop();
  156. return mixer.dataset(workingDataset)
  157. .then((state) => {
  158. chai.assert.equal(state.totalShow, 8);
  159. chai.assert.notEqual(state.show[7].id, '7');
  160. });
  161. });
  162. it('should remove a target when an item is removed from the start of the dataset', () => {
  163. workingDataset.shift();
  164. return mixer.dataset(workingDataset)
  165. .then((state) => {
  166. chai.assert.equal(state.totalShow, 7);
  167. chai.assert.notEqual(state.show[0].id, '0');
  168. });
  169. });
  170. it('should remove a target when an item is removed from an arbitary point in the dataset', () => {
  171. const removed = workingDataset.splice(2, 1);
  172. chai.assert.equal(removed[0].id, 999);
  173. return mixer.dataset(workingDataset)
  174. .then((state) => {
  175. chai.assert.equal(state.totalShow, 6);
  176. chai.assert.notEqual(state.show[2].id, '999');
  177. });
  178. });
  179. it('should sort targets when the dataset is sorted', () => {
  180. workingDataset.reverse();
  181. const ids = workingDataset.map((item) => item.id.toString());
  182. return mixer.dataset(workingDataset)
  183. .then((state) => {
  184. const elIds = state.show.map((el) => el.id);
  185. chai.assert.equal(state.totalShow, 6);
  186. chai.assert.deepEqual(ids, elIds);
  187. });
  188. });
  189. it('should sort rerender targets if their data changes and dirtyChecking is enabled', () => {
  190. workingDataset[0] = new dom.Item(Object.assign({}, workingDataset[0]));
  191. workingDataset[0].categories.push('z');
  192. return mixer.dataset(workingDataset)
  193. .then((state) => {
  194. chai.assert.isOk(state.show[0].matches('.category-z'));
  195. });
  196. });
  197. it('should not insert excessive whitespace after DOM manipulations', () => {
  198. chai.assert.equal(dom.getTotalWhitespace(container.outerHTML), startTotalWhitespace);
  199. });
  200. it('should accept a callback function which is invoked after dataset change', () => {
  201. workingDataset.reverse();
  202. const ids = workingDataset.map((item) => item.id.toString());
  203. const promise = new Promise(resolve => mixer.dataset(workingDataset, resolve));
  204. chai.assert.isFulfilled(promise);
  205. return promise
  206. .then((state) => {
  207. const elIds = state.show.map((el) => el.id);
  208. chai.assert.equal(state.totalShow, 6);
  209. chai.assert.deepEqual(ids, elIds);
  210. });
  211. });
  212. it('should accept a boolean allowing toggling off of animation', () => {
  213. workingDataset.reverse();
  214. const ids = workingDataset.map((item) => item.id.toString());
  215. return mixer.dataset(workingDataset, false)
  216. .then(state => {
  217. const elIds = state.show.map((el) => el.id);
  218. chai.assert.equal(state.totalShow, 6);
  219. chai.assert.deepEqual(ids, elIds);
  220. });
  221. });
  222. it('should re-render targets reflective of template changes when `forceRender` is called', () => {
  223. let firstTarget = mixer.getState().show[0];
  224. chai.assert.equal(firstTarget.outerHTML, '<div id="target-6" class="mix category-a category-c category-z" data-ref="mix" data-category="a c z" data-published="20151020" data-views="95"></div>');
  225. mixer.configure({
  226. render: {
  227. target: mixitup.h.template(dom.ITEM_TEMPLATE_ALT)
  228. }
  229. });
  230. mixer.forceRender();
  231. firstTarget = mixer.getState().show[0];
  232. chai.assert.equal(firstTarget.outerHTML, '<div id="target-6"></div>');
  233. });
  234. });
  235. });