PageRenderTime 26ms CodeModel.GetById 3ms RepoModel.GetById 0ms app.codeStats 0ms

/spec/javascripts/itemView.spec.js

https://bitbucket.org/stevepict/backbone.marionette
JavaScript | 357 lines | 273 code | 84 blank | 0 comment | 0 complexity | 8ff7a61cd62a6219561924966eb7afac MD5 | raw file
  1. describe("item view", function(){
  2. var Model = Backbone.Model.extend();
  3. var Collection = Backbone.Collection.extend({
  4. model: Model
  5. });
  6. var ItemView = Backbone.Marionette.ItemView.extend({});
  7. beforeEach(function(){
  8. loadFixtures("itemTemplate.html", "collectionItemTemplate.html", "emptyTemplate.html");
  9. });
  10. describe("when rendering", function(){
  11. var OnRenderView = Backbone.Marionette.ItemView.extend({
  12. template: "#emptyTemplate",
  13. beforeRender: function(){},
  14. onRender: function(){}
  15. });
  16. var view;
  17. var renderResult;
  18. var deferredDone;
  19. beforeEach(function(){
  20. view = new OnRenderView({});
  21. spyOn(view, "beforeRender").andCallThrough();
  22. spyOn(view, "onRender").andCallThrough();
  23. spyOn(view, "trigger").andCallThrough();
  24. view.render();
  25. });
  26. it("should call a `beforeRender` method on the view", function(){
  27. expect(view.beforeRender).toHaveBeenCalled();
  28. });
  29. it("should call an `onRender` method on the view", function(){
  30. expect(view.onRender).toHaveBeenCalled();
  31. });
  32. it("should trigger a before:render event", function(){
  33. expect(view.trigger).toHaveBeenCalledWith("item:before:render", view);
  34. });
  35. it("should trigger a rendered event", function(){
  36. expect(view.trigger).toHaveBeenCalledWith("item:rendered", view);
  37. });
  38. });
  39. describe("when an item view has a model and is rendered", function(){
  40. var view;
  41. beforeEach(function(){
  42. loadFixtures("itemTemplate.html");
  43. view = new ItemView({
  44. template: "#itemTemplate",
  45. model: new Model({
  46. foo: "bar"
  47. })
  48. });
  49. spyOn(view, "serializeData").andCallThrough();
  50. view.render();
  51. });
  52. it("should serialize the model", function(){
  53. expect(view.serializeData).toHaveBeenCalled();
  54. });
  55. it("should render the template with the serialized model", function(){
  56. expect($(view.el)).toHaveText(/bar/);
  57. });
  58. });
  59. describe("when an item view has asynchronous data and is rendered", function(){
  60. var view;
  61. beforeEach(function(){
  62. loadFixtures("itemTemplate.html");
  63. view = new ItemView({
  64. template: "#itemTemplate",
  65. serializeData: function() {
  66. var that = this;
  67. var deferred = $.Deferred();
  68. setTimeout(function() {
  69. deferred.resolve(that.model.toJSON());
  70. }, 100);
  71. return deferred.promise();
  72. },
  73. model: new Model({
  74. foo: "bar"
  75. })
  76. });
  77. spyOn(view, "serializeData").andCallThrough();
  78. view.render();
  79. });
  80. it("should serialize the model", function(){
  81. expect(view.serializeData).toHaveBeenCalled();
  82. });
  83. it("should render the template with the serialized model", function(){
  84. expect($(view.el)).toHaveText(/bar/);
  85. });
  86. });
  87. describe("when an item view has an asynchronous onRender and is rendered", function(){
  88. var AsyncOnRenderView = Backbone.Marionette.ItemView.extend({
  89. template: "#emptyTemplate",
  90. asyncCallback: function(){},
  91. onRender: function() {
  92. var that = this;
  93. var deferred = $.Deferred();
  94. setTimeout(function() {
  95. deferred.resolve(that.asyncCallback());
  96. }, 0);
  97. return deferred.promise();
  98. }
  99. });
  100. var view, promise, callbackSpy;
  101. beforeEach(function(){
  102. loadFixtures("emptyTemplate.html");
  103. view = new AsyncOnRenderView();
  104. callbackSpy = spyOn(view, "asyncCallback").andCallThrough();
  105. promise = view.render();
  106. });
  107. it("should delay until onRender resolves", function(){
  108. waits(0);
  109. runs(function(){
  110. $.when(promise).then(function(){
  111. expect(callbackSpy).toHaveBeenCalled();
  112. });
  113. });
  114. });
  115. });
  116. describe("when an item view has a collection and is rendered", function(){
  117. var view;
  118. beforeEach(function(){
  119. view = new ItemView({
  120. template: "#collectionItemTemplate",
  121. collection: new Collection([ { foo: "bar" }, { foo: "baz" } ])
  122. });
  123. spyOn(view, "serializeData").andCallThrough();
  124. view.render();
  125. });
  126. it("should serialize the collection", function(){
  127. expect(view.serializeData).toHaveBeenCalled();
  128. });
  129. it("should render the template with the serialized collection", function(){
  130. expect($(view.el)).toHaveText(/bar/);
  131. expect($(view.el)).toHaveText(/baz/);
  132. });
  133. });
  134. describe("when an item view's collection is reset", function(){
  135. var view;
  136. beforeEach(function(){
  137. var collection = new Collection();
  138. view = new ItemView({
  139. template: "#collectionItemTemplate",
  140. collection: collection
  141. });
  142. spyOn(view, "serializeData").andCallThrough();
  143. collection.reset([ { foo: "bar" }, { foo: "baz" } ]);
  144. });
  145. it("should serialize the collection", function(){
  146. expect(view.serializeData).toHaveBeenCalled();
  147. });
  148. it("should render the template with the serialized collection", function(){
  149. expect($(view.el)).toHaveText(/bar/);
  150. expect($(view.el)).toHaveText(/baz/);
  151. });
  152. });
  153. describe("when an item view has a model and collection, and is rendered", function(){
  154. var view;
  155. beforeEach(function(){
  156. view = new ItemView({
  157. template: "#itemTemplate",
  158. model: new Model({foo: "bar"}),
  159. collection: new Collection([ { foo: "bar" }, { foo: "baz" } ])
  160. });
  161. spyOn(view, "serializeData").andCallThrough();
  162. view.render();
  163. });
  164. it("should serialize the model", function(){
  165. expect(view.serializeData).toHaveBeenCalled();
  166. });
  167. it("should render the template with the serialized model", function(){
  168. expect($(view.el)).toHaveText(/bar/);
  169. expect($(view.el)).not.toHaveText(/baz/);
  170. });
  171. });
  172. describe("when closing an item view", function(){
  173. var EventedView = Backbone.Marionette.ItemView.extend({
  174. template: "#emptyTemplate",
  175. modelChange: function(){ },
  176. collectionChange: function(){ },
  177. beforeClose: function(){},
  178. onClose: function(){ }
  179. });
  180. var view;
  181. var model;
  182. var collection;
  183. beforeEach(function(){
  184. loadFixtures("itemTemplate.html");
  185. model = new Model({foo: "bar"});
  186. collection = new Collection();
  187. view = new EventedView({
  188. template: "#itemTemplate",
  189. model: model,
  190. collection: collection
  191. });
  192. view.render();
  193. spyOn(view, "unbind").andCallThrough();
  194. spyOn(view, "remove").andCallThrough();
  195. spyOn(view, "unbindAll").andCallThrough();
  196. spyOn(view, "modelChange").andCallThrough();
  197. spyOn(view, "collectionChange").andCallThrough();
  198. spyOn(view, "beforeClose").andCallThrough();
  199. spyOn(view, "onClose").andCallThrough();
  200. spyOn(view, "trigger").andCallThrough();
  201. view.bindTo(model, "change:foo", view.modelChange);
  202. view.bindTo(collection, "foo", view.collectionChange);
  203. view.close();
  204. model.set({foo: "bar"});
  205. collection.trigger("foo");
  206. });
  207. it("should unbind model events for the view", function(){
  208. expect(view.modelChange).not.toHaveBeenCalled();
  209. });
  210. it("should unbind all collection events for the view", function(){
  211. expect(view.collectionChange).not.toHaveBeenCalled();
  212. });
  213. it("should unbind any listener to custom view events", function(){
  214. expect(view.unbind).toHaveBeenCalled();
  215. });
  216. it("should remove the view's EL from the DOM", function(){
  217. expect(view.remove).toHaveBeenCalled();
  218. });
  219. it("should trigger 'item:before:close'", function(){
  220. expect(view.trigger).toHaveBeenCalledWith("item:before:close");
  221. });
  222. it("should trigger 'item:closed", function(){
  223. expect(view.trigger).toHaveBeenCalledWith("item:closed");
  224. });
  225. it("should call `beforeClose` if provided", function(){
  226. expect(view.beforeClose).toHaveBeenCalled();
  227. });
  228. it("should call `onClose` if provided", function(){
  229. expect(view.onClose).toHaveBeenCalled();
  230. });
  231. });
  232. describe("when a view with a checkbox is bound to re-render on the 'change:done' event of the model", function(){
  233. describe("and rendering the view, then changing the checkbox from unchecked, to checked, and back to unchecked", function(){
  234. var View = Backbone.Marionette.ItemView.extend({
  235. template: "#item-with-checkbox",
  236. setupHandler: function(){
  237. this.bindTo(this.model, "change:done", this.render, this);
  238. },
  239. events: {
  240. "change #chk": "changeClicked"
  241. },
  242. changeClicked: function(e){
  243. var chk = $(e.currentTarget);
  244. var checkedAttr = chk.attr("checked");
  245. var checked = !!checkedAttr;
  246. this.model.set({done: checked});
  247. }
  248. });
  249. var view, spy, model, chk;
  250. beforeEach(function(){
  251. loadFixtures("itemWithCheckbox.html");
  252. model = new Backbone.Model({
  253. done: false
  254. });
  255. view = new View({
  256. model: model
  257. });
  258. spy = spyOn(view, "render").andCallThrough();
  259. view.setupHandler();
  260. view.render();
  261. chk = view.$("#chk");
  262. chk.attr("checked", "checked");
  263. chk.trigger('change');
  264. chk = view.$("#chk");
  265. chk.removeAttr("checked");
  266. chk.trigger('change');
  267. });
  268. it("should render the view 3 times total", function(){
  269. expect(spy.callCount).toBe(3);
  270. });
  271. });
  272. });
  273. });