PageRenderTime 42ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/spec/javascripts/backbone-paginated-collection-spec.js

https://github.com/dlikhten/paginated-collection
JavaScript | 373 lines | 301 code | 72 blank | 0 comment | 2 complexity | fceb7984b7af3f8edc209048da984c20 MD5 | raw file
  1. describe("Backbone.PaginatedCollection", function() {
  2. var TehModel = Backbone.Model.extend({
  3. defaults: {value: -1}
  4. });
  5. var RegularModelCollection = Backbone.Collection.extend({
  6. model: TehModel
  7. });
  8. var allModels;
  9. var collection;
  10. var modelRemoved;
  11. var removedIndex;
  12. var modelAdded;
  13. var addedIndex;
  14. var paginatedCount;
  15. beforeEach(function() {
  16. allModels = new RegularModelCollection();
  17. for(var i = 0; i < 10; i++) {
  18. allModels.add(new TehModel({value: i}));
  19. }
  20. collection = new Backbone.PaginatedCollection(null, {collection: allModels, perPage: 2});
  21. });
  22. beforeEach(function() {
  23. modelRemoved = null;
  24. removedIndex = null;
  25. modelAdded = null;
  26. addedIndex = null;
  27. paginatedCount = 0;
  28. collection.on("remove", function(model, collection, options) {
  29. modelRemoved = model;
  30. removedIndex = options.index;
  31. });
  32. collection.on("add", function(model, collection, options) {
  33. modelAdded = model;
  34. addedIndex = options.index;
  35. });
  36. collection.on("paginated", function() {
  37. paginatedCount += 1;
  38. });
  39. });
  40. afterEach(function() {
  41. collection.off(null, null, null);
  42. });
  43. describe("#totalPages", function() {
  44. it("should break up the number of pages", function() {
  45. collection.perPage = 2;
  46. expect(collection.totalPages()).toEqual(5);
  47. });
  48. it("should break up the number of pages, including partial pages", function() {
  49. collection.perPage = 3;
  50. expect(collection.totalPages()).toEqual(4);
  51. });
  52. });
  53. describe("#changePage", function() {
  54. it("should set the current page to the given page number, and only contain models in that page", function() {
  55. collection.changePage(0);
  56. expect(collection.length).toEqual(2);
  57. expect(collection.at(0).get('value')).toEqual(0);
  58. expect(collection.at(1).get('value')).toEqual(1);
  59. collection.changePage(1);
  60. expect(collection.length).toEqual(2);
  61. expect(collection.at(0).get('value')).toEqual(2);
  62. expect(collection.at(1).get('value')).toEqual(3);
  63. });
  64. it("should create an event that the page has changed", function() {
  65. var triggered = 0;
  66. var expectedPage;
  67. collection.bind("paginated", function(page, obj) {
  68. triggered += 1;
  69. expect(page).toEqual(expectedPage);
  70. expect(obj).toEqual(collection);
  71. });
  72. expectedPage = 0;
  73. collection.changePage(0);
  74. expectedPage = 3;
  75. collection.changePage(3);
  76. expect(triggered).toEqual(2);
  77. });
  78. it("should not trigger a reset event", function() {
  79. var triggered = 0;
  80. collection.bind("reset", function() {
  81. triggered += 1;
  82. });
  83. collection.changePage(1);
  84. collection.changePage(2);
  85. expect(triggered).toEqual(0);
  86. });
  87. it("should go to page 0 when setting to -1", function() {
  88. collection.changePage(-1);
  89. expect(collection.page).toEqual(0);
  90. });
  91. it("should go to last page when setting to beyond", function() {
  92. collection.changePage(collection.lastPage() + 1);
  93. expect(collection.page).toEqual(collection.lastPage());
  94. });
  95. });
  96. describe("event:add", function() {
  97. it("shifts the collection right if element added before current page", function() {
  98. collection.changePage(1)
  99. toBeAdded = allModels.models[1];
  100. toBeRemoved = allModels.models[3];
  101. allModels.add(new TehModel({value: 11}), {at: 0})
  102. expect(addedIndex).toEqual(0);
  103. expect(removedIndex).toEqual(1);
  104. expect(modelAdded).toEqual(toBeAdded);
  105. expect(modelRemoved).toEqual(toBeRemoved);
  106. });
  107. it("does nothing if element added after current page", function() {
  108. collection.changePage(1);
  109. allModels.add(new TehModel({value: 11}), {at: 9});
  110. expect(modelAdded).toEqual(null);
  111. expect(addedIndex).toEqual(null);
  112. expect(modelRemoved).toEqual(null);
  113. expect(removedIndex).toEqual(null);
  114. });
  115. it("inserts the new element, shifts everything right if added on current page", function() {
  116. collection.changePage(1)
  117. toBeAdded = new TehModel({value: 11});
  118. toBeRemoved = allModels.models[3];
  119. allModels.add(toBeAdded, {at: 2})
  120. expect(addedIndex).toEqual(0);
  121. expect(removedIndex).toEqual(1);
  122. expect(modelAdded).toEqual(toBeAdded);
  123. expect(modelRemoved).toEqual(toBeRemoved);
  124. });
  125. it("inserts the new element, removes last element if added at end of current page", function() {
  126. collection.changePage(1)
  127. toBeAdded = new TehModel({value: 11});
  128. toBeRemoved = allModels.models[3];
  129. allModels.add(toBeAdded, {at: 3})
  130. expect(addedIndex).toEqual(1);
  131. expect(removedIndex).toEqual(1);
  132. expect(modelAdded).toEqual(toBeAdded);
  133. expect(modelRemoved).toEqual(toBeRemoved);
  134. });
  135. it("should just add when added to the last page", function() {
  136. allModels.add(toBeAdded, {at: 3})
  137. collection.changePage(5)
  138. addedIndex = null
  139. removedIndex = null
  140. modelAdded = null
  141. modelRemoved = null
  142. toBeAdded = new TehModel({value: 11});
  143. allModels.add(toBeAdded)
  144. expect(modelAdded).toEqual(toBeAdded)
  145. expect(modelRemoved).toEqual(null)
  146. });
  147. it("notifies when an element is removed from a different page", function() {
  148. spyOther = jasmine.createSpy("other page added");
  149. collection.changePage(0);
  150. collection.on("add-other-page", spyOther);
  151. toBeAdded = new TehModel({value: 11});
  152. allModels.add(toBeAdded);
  153. expect(spyOther).toHaveBeenCalled();
  154. });
  155. });
  156. describe("event:remove", function() {
  157. it("does nothing if element added after current page", function() {
  158. collection.changePage(0);
  159. allModels.remove(allModels.models[9]); // last model
  160. expect(modelRemoved).toBeFalsy();
  161. expect(modelAdded).toBeFalsy();
  162. expect(removedIndex).toEqual(null);
  163. expect(addedIndex).toEqual(null);
  164. });
  165. it("removes removed element, and adds one from next page", function() {
  166. collection.changePage(0);
  167. var toRemove = allModels.models[0];
  168. var toAdd = allModels.models[2];
  169. allModels.remove(toRemove); // last model
  170. expect(modelRemoved).toEqual(toRemove);
  171. expect(modelAdded).toEqual(toAdd);
  172. expect(removedIndex).toEqual(0);
  173. expect(addedIndex).toEqual(1);
  174. });
  175. it("shifts the collection left if element removed before current page", function() {
  176. collection.changePage(1);
  177. var toRemove = allModels.models[2];
  178. var toAdd = allModels.models[4];
  179. allModels.remove(allModels.models[0]); // last model
  180. expect(modelRemoved).toEqual(toRemove);
  181. expect(modelAdded).toEqual(toAdd);
  182. expect(removedIndex).toEqual(0);
  183. expect(addedIndex).toEqual(1);
  184. });
  185. it("paginates one back back if last element on last page removed", function() {
  186. allModels.add(new TehModel({value: 11}))
  187. collection.changePage(5);
  188. expect(collection.length).toEqual(1);
  189. paginatedCount = 0;
  190. allModels.remove(allModels.models[10]); // last model
  191. expect(modelRemoved).toBeFalsy();
  192. expect(removedIndex).toEqual(null);
  193. expect(modelAdded).toBeFalsy();
  194. expect(addedIndex).toEqual(null);
  195. expect(paginatedCount).toEqual(1);
  196. });
  197. it("removes element on last page, does not paginate if one model on last page removed", function() {
  198. collection.changePage(4);
  199. expect(collection.length).toEqual(2);
  200. toRemove = allModels.models[8];
  201. allModels.remove(toRemove); // last model
  202. expect(modelRemoved).toEqual(toRemove);
  203. expect(removedIndex).toEqual(0);
  204. expect(modelAdded).toBeFalsy();
  205. expect(addedIndex).toEqual(null);
  206. });
  207. it("is ok when multiple elements are removed", function() {
  208. collection.changePage(0);
  209. allModels.remove(allModels.models[0]);
  210. allModels.remove(allModels.models[0]);
  211. allModels.remove(allModels.models[0]);
  212. allModels.remove(allModels.models[0]);
  213. allModels.remove(allModels.models[0]);
  214. allModels.remove(allModels.models[0]);
  215. expect(collection.models[0]).toEqual(allModels.models[0])
  216. });
  217. it("notifies when an element is removed from a different page", function() {
  218. spyOtherRemoved = jasmine.createSpy("other page removed");
  219. collection.changePage(0);
  220. collection.on("remove-other-page", spyOtherRemoved);
  221. allModels.remove(allModels.models[7]);
  222. expect(spyOtherRemoved).toHaveBeenCalled();
  223. });
  224. });
  225. describe("#lastPage", function() {
  226. it("should return the page index of the last page", function() {
  227. expect(collection.lastPage()).toEqual(collection.totalPages() - 1);
  228. });
  229. });
  230. describe("#isLastPage", function() {
  231. it("should return true if on the last page", function() {
  232. expect(collection.isLastPage()).toBeFalsy();
  233. collection.page = collection.totalPages() - 1;
  234. expect(collection.isLastPage()).toBeTruthy();
  235. });
  236. });
  237. describe("#modelToRemove", function() {
  238. it("should return the given model if that model is on the current page", function() {
  239. expect(collection.modelToRemove(collection.models[0], 0)).toEqual(collection.models[0])
  240. });
  241. it("should return null if the collection is on page 0 and the model not in the current view", function() {
  242. expect(collection.modelToRemove(allModels.models[9], 9)).toBeNull();
  243. });
  244. it("should return the first element of the collection if the model is on a previous page", function() {
  245. collection.changePage(2);
  246. expect(collection.modelToRemove(allModels.models[0], 0)).toEqual(collection.models[0]);
  247. });
  248. it("should return null if the element is on the following page", function() {
  249. collection.changePage(2);
  250. expect(collection.modelToRemove(allModels.models[9], 9)).toBeNull();
  251. });
  252. });
  253. describe("#paginatedOffset", function() {
  254. it("returns a number > this.perPage if the index is in a proceeding page", function() {
  255. collection.changePage(2);
  256. expect(collection.paginatedOffset(6)).toEqual(2);
  257. });
  258. it("returns a negative number if in a previous page", function() {
  259. collection.changePage(2);
  260. expect(collection.paginatedOffset(0)).toEqual(-4);
  261. });
  262. it("return a positive number < this.perPage if in current page", function() {
  263. collection.changePage(2);
  264. expect(collection.paginatedOffset(4)).toEqual(0);
  265. });
  266. it("returns the offset if there are no elements in the collection", function() {
  267. for(var i = 0; i < 10; i++) allModels.remove(allModels.at(0))
  268. collection.changePage(0);
  269. expect(collection.paginatedOffset(1)).toEqual(1);
  270. });
  271. });
  272. describe("events", function() {
  273. it("should trigger a reset event when the underlying model changed", function() {
  274. var triggered = 0;
  275. collection.bind("reset", function() {
  276. triggered += 1;
  277. });
  278. allModels.reset([new TehModel({value: 1})]);
  279. expect(triggered).toEqual(1);
  280. });
  281. it("should trigger a reset event when the underlying model has been sorted", function() {
  282. var triggered = 0;
  283. collection.bind("reset", function() {
  284. triggered += 1;
  285. });
  286. allModels.comparator = function(model) { return model.get("value"); };
  287. allModels.sort();
  288. expect(triggered).toEqual(1);
  289. });
  290. it("should trigger an event which we don't understand when underlying model triggers it", function() {
  291. var eventName = null;
  292. collection.on("all", function(triggeredEventName) {
  293. eventName = triggeredEventName;
  294. });
  295. allModels.trigger("filter-complete");
  296. expect(eventName).toEqual("filter-complete");
  297. allModels.trigger("foo");
  298. expect(eventName).toEqual("foo");
  299. });
  300. });
  301. });