PageRenderTime 60ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/test/body.js

https://github.com/TechnotronicOz/backgrid
JavaScript | 491 lines | 392 code | 84 blank | 15 comment | 0 complexity | c4e5420d2fa518fee0189601061a8243 MD5 | raw file
Possible License(s): MIT
  1. /*
  2. backgrid
  3. http://github.com/wyuenho/backgrid
  4. Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors
  5. Licensed under the MIT license.
  6. */
  7. describe("A Body", function () {
  8. var col;
  9. var body;
  10. beforeEach(function () {
  11. col = new Backbone.Collection([{id: 2}, {id: 1}, {id: 3}]);
  12. body = new Backgrid.Body({
  13. columns: [{
  14. name: "id",
  15. cell: "integer"
  16. }],
  17. collection: col
  18. });
  19. body.render();
  20. });
  21. it("renders table rows using the given column definitions and collection", function () {
  22. expect(body.el.tagName).toBe("TBODY");
  23. var $trs = body.$el.children();
  24. expect($trs.length).toBe(3);
  25. expect($(body.el).html().toLowerCase().replace(/\s*</g, '<'))
  26. .toBe('<tr><td class="integer-cell editable sortable renderable">2</td></tr>' +
  27. '<tr><td class="integer-cell editable sortable renderable">1</td></tr>' +
  28. '<tr><td class="integer-cell editable sortable renderable">3</td></tr>');
  29. });
  30. it("will render a new row if a new model is added to its collection", function () {
  31. body.collection.add({
  32. id: 4
  33. });
  34. var $trs = body.$el.children();
  35. expect($trs.length).toBe(4);
  36. expect($("<div>").append($trs.eq(3).clone()).html().toLowerCase().replace(/\s*</g, '<'))
  37. .toBe('<tr><td class="integer-cell editable sortable renderable">4</td></tr>');
  38. body.collection.add({
  39. id: 5
  40. }, {at: 1});
  41. $trs = body.$el.children();
  42. expect($trs.length).toBe(5);
  43. expect($("<div>").append($trs.eq(1).clone()).html().toLowerCase().replace(/\s*</g, '<'))
  44. .toBe('<tr><td class="integer-cell editable sortable renderable">5</td></tr>');
  45. });
  46. it("will render a new row by calling insertRow directly with a new model", function () {
  47. body = new Backgrid.Body({
  48. columns: [{
  49. name: "id",
  50. cell: "integer"
  51. }],
  52. collection: new Backbone.Collection()
  53. });
  54. body.render();
  55. body.insertRow({
  56. id: 4
  57. });
  58. var $trs = body.$el.children();
  59. expect($trs.length).toBe(1);
  60. expect($("<div>").append($trs.eq(0).clone()).html().toLowerCase().replace(/\s*</g, '<'))
  61. .toBe('<tr><td class="integer-cell editable sortable renderable">4</td></tr>');
  62. body.insertRow({
  63. id: 5
  64. }, {at: 0});
  65. $trs = body.$el.children();
  66. expect($trs.length).toBe(2);
  67. expect($("<div>").append($trs.eq(0).clone()).html().toLowerCase().replace(/\s*</g, '<'))
  68. .toBe('<tr><td class="integer-cell editable sortable renderable">5</td></tr>');
  69. });
  70. it("will remove a row from the DOM if a model is removed from its collection", function () {
  71. var m1 = body.collection.at(1);
  72. body.collection.remove(m1);
  73. var $trs = body.$el.children();
  74. expect($trs.length).toBe(2);
  75. expect($(body.el).html().toLowerCase().replace(/\s+</g, '<'))
  76. .toBe('<tr><td class="integer-cell editable sortable renderable">2</td></tr>' +
  77. '<tr><td class="integer-cell editable sortable renderable">3</td></tr>');
  78. });
  79. it("will remove a row from the DOM is removeRow is called directly with a model", function () {
  80. var m1 = body.collection.at(1);
  81. body.removeRow(m1);
  82. var $trs = body.$el.children();
  83. expect($trs.length).toBe(2);
  84. expect($(body.el).html().toLowerCase().replace(/\s+</g, '<'))
  85. .toBe('<tr><td class="integer-cell editable sortable renderable">2</td></tr>' +
  86. '<tr><td class="integer-cell editable sortable renderable">3</td></tr>');
  87. });
  88. it("will refresh if its collection is reset", function () {
  89. var eventFired = false;
  90. var handler = function () {
  91. eventFired = true;
  92. };
  93. body.collection.on("backgrid:refresh", handler);
  94. body.collection.reset([{
  95. id: 6
  96. }]);
  97. body.collection.off("backgrid:refresh", handler);
  98. expect(eventFired).toBe(true);
  99. var $trs = body.$el.children();
  100. expect($trs.length).toBe(1);
  101. expect($(body.el).html().toLowerCase().replace(/\s+</g, '<'))
  102. .toBe('<tr><td class="integer-cell editable sortable renderable">6</td></tr>');
  103. });
  104. it("will render rows using the Row class supplied in the constructor options", function () {
  105. var CustomRow = Backgrid.Row.extend({});
  106. spyOn(CustomRow.prototype, "render").andCallThrough();
  107. body = new Backgrid.Body({
  108. columns: [{
  109. name: "id",
  110. cell: "integer"
  111. }],
  112. collection: col,
  113. row: CustomRow
  114. });
  115. body.render();
  116. expect(CustomRow.prototype.render).toHaveBeenCalled();
  117. });
  118. describe("maintain page size at page boundary", function () {
  119. var col;
  120. beforeEach(function () {
  121. col = new Backbone.PageableCollection([
  122. {id: 1},
  123. {id: 2},
  124. {id: 3}
  125. ], {
  126. state: {
  127. pageSize: 2
  128. },
  129. mode: "client"
  130. });
  131. body = new Backgrid.Body({
  132. columns: [{
  133. name: "id",
  134. cell: "integer"
  135. }],
  136. collection: col
  137. });
  138. body.render();
  139. });
  140. it("when adding to a full page", function () {
  141. col.add(new Backbone.Model({id: 4}));
  142. expect(body.$el.find("tr").length).toBe(2);
  143. });
  144. it("when removing from a full page", function () {
  145. col.remove(col.get(1));
  146. expect(body.$el.find("tr").length).toBe(2);
  147. });
  148. });
  149. it("will not display the empty row if collection is not empty", function () {
  150. body = new Backgrid.Body({
  151. emptyText: " ",
  152. columns: [{
  153. name: "id",
  154. cell: "integer"
  155. }],
  156. collection: col
  157. });
  158. body.render();
  159. expect(body.$el.find("tr.empty").length).toBe(0);
  160. });
  161. it("will not display the empty row if `options.emptyText` is not supplied", function () {
  162. expect(body.$el.find("tr.empty").length).toBe(0);
  163. col.reset();
  164. body = new Backgrid.Body({
  165. columns: [{
  166. name: "id",
  167. cell: "integer"
  168. }],
  169. collection: col
  170. });
  171. body.render();
  172. expect(body.$el.find("tr.empty").length).toBe(0);
  173. });
  174. it("will display the empty row if the collection is empty and `options.emptyText` is supplied", function () {
  175. col.reset();
  176. body = new Backgrid.Body({
  177. emptyText: " ",
  178. columns: [{
  179. name: "id",
  180. cell: "integer"
  181. }],
  182. collection: col
  183. });
  184. body.render();
  185. expect(body.$el.find("tr.empty").length).toBe(1);
  186. expect(body.$el.find("tr.empty > td").attr("colspan")).toBe("1");
  187. });
  188. it("will clear the empty row if a new model is added to an empty collection", function () {
  189. col.reset();
  190. body = new Backgrid.Body({
  191. emptyText: " ",
  192. columns: [{
  193. name: "id",
  194. cell: "integer"
  195. }],
  196. collection: col
  197. });
  198. body.render();
  199. expect(body.$el.find("tr.empty").length).toBe(1);
  200. col.add({id: 4});
  201. expect(body.$el.find("tr.empty").length).toBe(0);
  202. col.reset();
  203. expect(body.$el.find("tr.empty").length).toBe(1);
  204. body.insertRow({id: 5});
  205. expect(body.$el.find("tr.empty").length).toBe(0);
  206. });
  207. it("#sort will throw a RangeError is direction is not ascending, descending or null", function () {
  208. body = new Backgrid.Body({
  209. collection: col,
  210. columns: [{
  211. name: "id",
  212. cell: "integer"
  213. }],
  214. }).render();
  215. expect(function () {
  216. body.sort("id", "wat");
  217. }).toThrow();
  218. });
  219. it("can sort the underlying collection using the default comparator", function () {
  220. body = new Backgrid.Body({
  221. collection: col,
  222. columns: [{
  223. name: "id",
  224. cell: "integer"
  225. }],
  226. }).render();
  227. body.collection.trigger("backgrid:sort", body.columns.at(0), "ascending");
  228. expect(body.collection.toJSON()).toEqual([{id: 1}, {id: 2}, {id: 3}]);
  229. expect(body.columns.at(0).get("direction"), "ascending");
  230. body.collection.trigger("backgrid:sort", body.columns.at(0), "descending");
  231. expect(body.collection.toJSON()).toEqual([{id: 3}, {id: 2}, {id: 1}]);
  232. expect(body.columns.at(0).get("direction"), "descending");
  233. col.at(0).cid = "c100";
  234. col.at(1).cid = "c1";
  235. col.at(2).cid = "c10";
  236. body.collection.trigger("backgrid:sort", body.columns.at(0), null);
  237. expect(body.collection.toJSON()).toEqual([{id: 2}, {id: 1}, {id: 3}]);
  238. expect(body.columns.at(0).get("direction"), null);
  239. });
  240. it("can sort the underlying collection using a custom value extractor on `backgrid:sort`", function () {
  241. var sortValue = function (model, attr) {
  242. return 3 - model.get(attr);
  243. };
  244. body = new Backgrid.Body({
  245. collection: col,
  246. columns: [{
  247. name: "id",
  248. cell: "integer",
  249. sortValue: sortValue
  250. }],
  251. }).render();
  252. body.collection.trigger("backgrid:sort", body.columns.at(0), "ascending");
  253. expect(body.collection.toJSON()).toEqual([{id: 3}, {id: 2}, {id: 1}]);
  254. expect(body.columns.at(0).get("direction"), "ascending");
  255. });
  256. it("can sort on a server-mode Backbone.PageableCollection", function () {
  257. var oldAjax = $.ajax;
  258. $.ajax = function (settings) {
  259. settings.success([{"total_entries": 3}, [{id: 2}, {id: 1}]]);
  260. };
  261. var col = new (Backbone.PageableCollection.extend({
  262. url: "test-headercell"
  263. }))([{id: 1}, {id: 2}], {
  264. state: {
  265. pageSize: 3
  266. }
  267. });
  268. body = new Backgrid.Body({
  269. columns: [{
  270. name: "id",
  271. cell: "integer"
  272. }],
  273. collection: col
  274. });
  275. body.render();
  276. expect(body.collection.at(0).get("id")).toBe(1);
  277. expect(body.collection.at(1).get("id")).toBe(2);
  278. var onBackgridSortedCallArgs = [];
  279. col.on("backgrid:sorted", function () {
  280. onBackgridSortedCallArgs.push([].slice.apply(arguments));
  281. });
  282. body.collection.trigger("backgrid:sort", body.columns.at(0), "descending");
  283. expect(body.collection.at(0).get("id")).toBe(2);
  284. expect(body.collection.at(1).get("id")).toBe(1);
  285. expect(body.columns.at(0).get("direction"), "descending");
  286. expect(onBackgridSortedCallArgs.length).toBe(1);
  287. expect(onBackgridSortedCallArgs[0][0]).toBe(body.columns.at(0));
  288. expect(onBackgridSortedCallArgs[0][1]).toBe("descending");
  289. $.ajax = oldAjax;
  290. });
  291. it("can sort on a client-mode Backbone.PageableCollection", function () {
  292. var col = new Backbone.PageableCollection([{id: 2}, {id: 1}, {id: 3}], {
  293. state: {
  294. pageSize: 1
  295. },
  296. mode: "client"
  297. });
  298. body = new Backgrid.Body({
  299. columns: [{
  300. name: "id",
  301. cell: "integer",
  302. sortValue: function (model, attr) {
  303. return 3 - model.get(attr);
  304. }
  305. }],
  306. collection: col
  307. });
  308. body.render();
  309. var onBackgridSortedCallArgs = [];
  310. col.on("backgrid:sorted", function () {
  311. onBackgridSortedCallArgs.push([].slice.apply(arguments));
  312. });
  313. col.trigger("backgrid:sort", body.columns.at(0), "ascending");
  314. expect(body.collection.toJSON()).toEqual([{id: 3}]);
  315. expect(body.columns.at(0).get("direction"), "ascending");
  316. expect(onBackgridSortedCallArgs.length).toBe(1);
  317. expect(onBackgridSortedCallArgs[0][0]).toBe(body.columns.at(0));
  318. expect(onBackgridSortedCallArgs[0][1]).toBe("ascending");
  319. expect(onBackgridSortedCallArgs[0][2]).toBe(col);
  320. body.collection.getPage(2);
  321. expect(body.collection.toJSON()).toEqual([{id: 2}]);
  322. body.collection.getPage(3);
  323. expect(body.collection.toJSON()).toEqual([{id: 1}]);
  324. body.collection.getFirstPage();
  325. col.trigger("backgrid:sort", body.columns.at(0), "descending");
  326. expect(body.columns.at(0).get("direction"), "descending");
  327. expect(body.collection.toJSON()).toEqual([{id: 1}]);
  328. expect(onBackgridSortedCallArgs.length).toBe(2);
  329. expect(onBackgridSortedCallArgs[1][0]).toBe(body.columns.at(0));
  330. expect(onBackgridSortedCallArgs[1][1]).toBe("descending");
  331. expect(onBackgridSortedCallArgs[1][2]).toBe(col);
  332. col.trigger("backgrid:sort", body.columns.at(0), null);
  333. expect(body.columns.at(0).get("direction"), null);
  334. expect(body.collection.toJSON()).toEqual([{id: 2}]);
  335. expect(onBackgridSortedCallArgs.length).toBe(3);
  336. expect(onBackgridSortedCallArgs[2][0]).toBe(body.columns.at(0));
  337. expect(onBackgridSortedCallArgs[2][1]).toBe(null);
  338. expect(onBackgridSortedCallArgs[2][2]).toBe(col);
  339. });
  340. it("will put the next editable and renderable cell in edit mode when a save or one of the navigation commands is triggered via backgrid:edited from the collection", function () {
  341. var people = new Backbone.Collection([
  342. {name: "alice", age: 28, married: false},
  343. {name: "bob", age: 30, married: true}
  344. ]);
  345. var columns = new Backgrid.Columns([{
  346. name: "name",
  347. cell: "string"
  348. }, {
  349. name: "age",
  350. cell: "integer",
  351. editable: false
  352. }, {
  353. name: "sex",
  354. cell: "boolean",
  355. renderable: false
  356. }]);
  357. var body = new Backgrid.Body({
  358. collection: people,
  359. columns: columns
  360. });
  361. body.render();
  362. body.rows[0].cells[0].enterEditMode();
  363. // Just making sure a cell has exited edit mode before the next cell goes
  364. // into edit mode. Fixes #187.
  365. var oldExitEditMode = body.rows[0].cells[0].exitEditMode;
  366. var callOrders = [];
  367. body.rows[0].cells[0].exitEditMode = function () {
  368. callOrders.push("exit");
  369. return oldExitEditMode.apply(this, arguments);
  370. };
  371. var oldEnterEditMode = body.rows[1].cells[0].enterEditMode;
  372. body.rows[1].cells[0].enterEditMode = function () {
  373. callOrders.push("enter");
  374. return oldEnterEditMode.apply(this, arguments);
  375. };
  376. // right
  377. people.trigger("backgrid:edited", people.at(0), columns.at(0), new Backgrid.Command({keyCode: 9}));
  378. expect(body.rows[0].cells[0].$el.hasClass("editor")).toBe(false);
  379. expect(body.rows[1].cells[0].$el.hasClass("editor")).toBe(true);
  380. expect(callOrders[0]).toBe("exit");
  381. expect(callOrders[1]).toBe("enter");
  382. body.rows[0].cells[0].exitEditMode = oldExitEditMode;
  383. body.rows[1].cells[0].enterEditMode = oldEnterEditMode;
  384. // left
  385. people.trigger("backgrid:edited", people.at(1), columns.at(0), new Backgrid.Command({keyCode: 9, shiftKey: true}));
  386. expect(body.rows[0].cells[0].$el.hasClass("editor")).toBe(true);
  387. expect(body.rows[1].cells[0].$el.hasClass("editor")).toBe(false);
  388. // down
  389. people.trigger("backgrid:edited", people.at(0), columns.at(0), new Backgrid.Command({keyCode: 40}));
  390. expect(body.rows[0].cells[0].$el.hasClass("editor")).toBe(false);
  391. expect(body.rows[1].cells[0].$el.hasClass("editor")).toBe(true);
  392. // up
  393. people.trigger("backgrid:edited", people.at(1), columns.at(0), new Backgrid.Command({keyCode: 38}));
  394. expect(body.rows[0].cells[0].$el.hasClass("editor")).toBe(true);
  395. expect(body.rows[1].cells[0].$el.hasClass("editor")).toBe(false);
  396. // enter
  397. people.trigger("backgrid:edited", people.at(0), columns.at(0), new Backgrid.Command({keyCode: 13}));
  398. expect(body.rows[0].cells[0].$el.hasClass("editor")).toBe(false);
  399. expect(body.rows[1].cells[0].$el.hasClass("editor")).toBe(false);
  400. // esc
  401. body.rows[1].cells[0].enterEditMode();
  402. people.trigger("backgrid:edited", people.at(1), columns.at(0), new Backgrid.Command({keyCode: 27}));
  403. expect(body.rows[0].cells[0].$el.hasClass("editor")).toBe(false);
  404. expect(body.rows[1].cells[0].$el.hasClass("editor")).toBe(false);
  405. });
  406. });