PageRenderTime 63ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/test/unit/composite-view.spec.js

https://gitlab.com/CodeYellowBV/backbone.marionette
JavaScript | 839 lines | 655 code | 169 blank | 15 comment | 7 complexity | 121f0f1472de69f92f60ddf53d527690 MD5 | raw file
  1. describe('composite view', function() {
  2. 'use strict';
  3. beforeEach(function() {
  4. var suite = this;
  5. // Models
  6. this.Model = Backbone.Model.extend();
  7. this.User = Backbone.Model.extend();
  8. this.Node = Backbone.Model.extend({
  9. initialize: function() {
  10. var nodes = this.get('nodes');
  11. if (nodes) {
  12. this.nodes = new suite.NodeCollection(nodes);
  13. this.unset('nodes');
  14. }
  15. }
  16. });
  17. // Collections
  18. this.Collection = Backbone.Collection.extend({
  19. model: this.Model
  20. });
  21. this.UserCollection = Backbone.Collection.extend({
  22. model: this.User
  23. });
  24. this.NodeCollection = Backbone.Collection.extend({
  25. model: this.Node
  26. });
  27. // Views
  28. this.treeViewTemplateFn = _.template('<li>name: <%= name %></li>');
  29. this.TreeView = Backbone.Marionette.CompositeView.extend({
  30. tagName: 'ul',
  31. template: this.treeViewTemplateFn,
  32. initialize: function() {
  33. this.collection = this.model.nodes;
  34. }
  35. });
  36. });
  37. describe('when a composite view has a template without a model', function() {
  38. beforeEach(function() {
  39. this.templateFn = _.template('composite template');
  40. this.ChildView = Backbone.Marionette.ItemView.extend({
  41. tagName: 'span',
  42. render: function() {
  43. this.$el.html(this.model.get('foo'));
  44. }
  45. });
  46. this.CompositeViewNoModel = Backbone.Marionette.CompositeView.extend({
  47. childView: this.ChildView,
  48. template: this.templateFn
  49. });
  50. this.m1 = new this.Model({foo: 'bar'});
  51. this.m2 = new this.Model({foo: 'baz'});
  52. this.collection = new this.Collection([this.m1, this.m2]);
  53. this.compositeView = new this.CompositeViewNoModel({
  54. collection: this.collection
  55. });
  56. this.compositeView.render();
  57. });
  58. it('should render the template', function() {
  59. expect(this.compositeView.$el).to.contain.$text('composite');
  60. });
  61. it('should render the collections items', function() {
  62. expect(this.compositeView.$el).to.contain.$text('bar');
  63. expect(this.compositeView.$el).to.contain.$text('baz');
  64. });
  65. });
  66. describe('when rendering with a overridden attachElContent', function() {
  67. beforeEach(function() {
  68. this.attachElContentStub = this.sinon.stub();
  69. this.CompositeView = Marionette.CompositeView.extend({
  70. template: function() {},
  71. attachElContent: this.attachElContentStub
  72. });
  73. this.compositeView = new this.CompositeView();
  74. this.compositeView.render();
  75. });
  76. it('should render according to the custom attachElContent logic', function() {
  77. expect(this.attachElContentStub).to.have.been.calledOnce.and.calledWith(undefined);
  78. });
  79. });
  80. describe('when a composite view has a model and a template', function() {
  81. beforeEach(function() {
  82. this.templateFn = _.template('composite <%= foo %>');
  83. this.ChildView = Backbone.Marionette.ItemView.extend({
  84. tagName: 'span',
  85. render: function() {
  86. this.$el.html(this.model.get('foo'));
  87. }
  88. });
  89. this.CompositeView = Backbone.Marionette.CompositeView.extend({
  90. childView: this.ChildView,
  91. template: this.templateFn,
  92. onRender: function() {}
  93. });
  94. this.m1 = new this.Model({foo: 'bar'});
  95. this.m2 = new this.Model({foo: 'baz'});
  96. this.collection = new this.Collection();
  97. this.collection.add(this.m2);
  98. this.compositeView = new this.CompositeView({
  99. model: this.m1,
  100. collection: this.collection
  101. });
  102. this.sinon.spy(Marionette.Renderer, 'render');
  103. this.compositeView.render();
  104. });
  105. it('should render the template with the model', function() {
  106. expect(this.compositeView.$el).to.contain.$text('composite bar');
  107. });
  108. it('should render the collections items', function() {
  109. expect(this.compositeView.$el).to.contain.$text('baz');
  110. });
  111. it('should pass template fn, data, and view instance to Marionette.Renderer.Render', function() {
  112. expect(Marionette.Renderer.render).to.have.been.calledWith(this.templateFn, {foo: 'bar'}, this.compositeView);
  113. });
  114. });
  115. describe('when a composite view triggers render in initialize', function() {
  116. beforeEach(function() {
  117. var suite = this;
  118. this.collectionTemplateFn = _.template('');
  119. this.collectionItemTemplateFn = _.template('<% _.each(items, function(item){ %><span><%= item.foo %></span><% }) %>');
  120. this.emptyTemplateFn = _.template('&nbsp;');
  121. this.EmptyView = Backbone.Marionette.ItemView.extend({
  122. template: this.emptyTemplateFn,
  123. tagName: 'hr',
  124. onShow: function() {
  125. suite.onShow.push('EMPTY');
  126. }
  127. });
  128. this.ChildView = Backbone.Marionette.ItemView.extend({
  129. template: this.collectionItemTemplateFn,
  130. tagName: 'span'
  131. });
  132. this.CompositeView = Backbone.Marionette.CompositeView.extend({
  133. childView: this.ChildView,
  134. emptyView: this.EmptyView,
  135. template: this.collectionTemplateFn,
  136. initialize: function() {
  137. this.render();
  138. },
  139. onRender: function() {}
  140. });
  141. this.m1 = new this.Model({foo: 'bar'});
  142. this.compositeView = new this.CompositeView({
  143. model: this.m1,
  144. collection: new this.Collection()
  145. });
  146. this.onShow = [];
  147. this.compositeView.trigger('show');
  148. });
  149. it('should call "onShowCallbacks.add"', function() {
  150. expect(this.onShow.length === 1).to.be.ok;
  151. });
  152. });
  153. describe('when rendering a composite view without a template', function() {
  154. beforeEach(function() {
  155. this.ChildView = Backbone.Marionette.ItemView.extend({
  156. tagName: 'span',
  157. render: function() {
  158. this.$el.html(this.model.get('foo'));
  159. }
  160. });
  161. this.CompositeView = Backbone.Marionette.CompositeView.extend({
  162. childView: this.ChildView
  163. });
  164. this.m1 = new this.Model({foo: 'bar'});
  165. this.m2 = new this.Model({foo: 'baz'});
  166. this.collection = new this.Collection();
  167. this.collection.add(this.m2);
  168. this.compositeView = new this.CompositeView({
  169. model: this.m1,
  170. collection: this.collection
  171. });
  172. });
  173. it('should throw an exception because there was no valid template', function() {
  174. expect(this.compositeView.render).to.throw('Cannot render the template since its false, null or undefined.');
  175. });
  176. });
  177. describe('when rendering a composite view', function() {
  178. beforeEach(function() {
  179. var suite = this;
  180. this.templateFn = _.template('composite <%= foo %>');
  181. this.ChildView = Backbone.Marionette.ItemView.extend({
  182. tagName: 'span',
  183. render: function() {
  184. this.$el.html(this.model.get('foo'));
  185. }
  186. });
  187. this.CompositeView = Backbone.Marionette.CompositeView.extend({
  188. childView: this.ChildView,
  189. template: this.templateFn,
  190. onBeforeRender: function() {
  191. return this.isRendered;
  192. },
  193. onRender: function() {
  194. return this.isRendered;
  195. }
  196. });
  197. this.order = [];
  198. this.m1 = new this.Model({foo: 'bar'});
  199. this.m2 = new this.Model({foo: 'baz'});
  200. this.collection = new this.Collection();
  201. this.collection.add(this.m2);
  202. this.compositeView = new this.CompositeView({
  203. model: this.m1,
  204. collection: this.collection
  205. });
  206. this.compositeView.on('render:template', function() {
  207. suite.order.push(suite.compositeView.renderedModelView);
  208. });
  209. this.compositeView.on('render:collection', function() {
  210. suite.order.push(suite.compositeView.collection);
  211. });
  212. this.compositeView.on('render', function() {
  213. suite.order.push(suite.compositeView);
  214. });
  215. this.sinon.spy(this.compositeView, 'trigger');
  216. this.sinon.spy(this.compositeView, 'onBeforeRender');
  217. this.sinon.spy(this.compositeView, 'onRender');
  218. this.compositeView.render();
  219. });
  220. it('should trigger a render event for the model view', function() {
  221. expect(this.compositeView.trigger).to.have.been.calledWith('render:template');
  222. });
  223. it('should trigger a before:render event for the collection', function() {
  224. expect(this.compositeView.trigger).to.have.been.calledWith('before:render:collection', this.compositeView);
  225. });
  226. it('should trigger a render event for the collection', function() {
  227. expect(this.compositeView.trigger).to.have.been.calledWith('render:collection', this.compositeView);
  228. });
  229. it('should trigger a render event for the composite view', function() {
  230. expect(this.compositeView.trigger).to.have.been.calledWith('render', this.compositeView);
  231. });
  232. it('should guarantee rendering of the model before rendering the collection', function() {
  233. expect(this.order[0]).to.equal(this.compositeView.renderedModelView);
  234. expect(this.order[1]).to.equal(this.compositeView.collection);
  235. expect(this.order[2]).to.equal(this.compositeView);
  236. });
  237. it('should call "onBeforeRender"', function() {
  238. expect(this.compositeView.onBeforeRender).to.have.been.calledOnce;
  239. });
  240. it('should call "onRender"', function() {
  241. expect(this.compositeView.onRender).to.have.been.calledOnce;
  242. });
  243. it('should call "onBeforeRender" before "onRender"', function() {
  244. expect(this.compositeView.onBeforeRender).to.have.been.calledBefore(this.compositeView.onRender);
  245. });
  246. it('should not be rendered when "onBeforeRender" is called', function() {
  247. expect(this.compositeView.onBeforeRender.lastCall.returnValue).not.to.be.ok;
  248. });
  249. it('should be rendered when "onRender" is called', function() {
  250. expect(this.compositeView.onRender.lastCall.returnValue).to.be.true;
  251. });
  252. it('should mark as rendered', function() {
  253. expect(this.compositeView).to.have.property('isRendered', true);
  254. });
  255. });
  256. describe('when rendering a composite view twice', function() {
  257. beforeEach(function() {
  258. this.templateFn = _.template('composite <%= foo %>');
  259. this.ChildView = Backbone.Marionette.ItemView.extend({
  260. tagName: 'span',
  261. render: function() {
  262. this.$el.html(this.model.get('foo'));
  263. }
  264. });
  265. this.CompositeModelView = Backbone.Marionette.CompositeView.extend({
  266. childView: this.ChildView,
  267. template: this.templateFn
  268. });
  269. this.m1 = new this.Model({foo: 'bar'});
  270. this.m2 = new this.Model({foo: 'baz'});
  271. this.collection = new this.Collection();
  272. this.collection.add(this.m2);
  273. this.compositeView = new this.CompositeModelView({
  274. model: this.m1,
  275. collection: this.collection
  276. });
  277. this.sinon.spy(this.compositeView, 'render');
  278. this.sinon.spy(this.compositeView, 'destroyChildren');
  279. this.sinon.spy(Backbone.Marionette.Renderer, 'render');
  280. this.compositeRenderSpy = this.compositeView.render;
  281. this.compositeView.render();
  282. this.compositeView.render();
  283. });
  284. it('should re-render the template view', function() {
  285. expect(Backbone.Marionette.Renderer.render.callCount).to.equal(2);
  286. });
  287. it('should destroy all of the child collection child views', function() {
  288. expect(this.compositeView.destroyChildren).to.have.been.called;
  289. expect(this.compositeView.destroyChildren.callCount).to.equal(2);
  290. });
  291. it('should re-render the collections items', function() {
  292. expect(this.compositeRenderSpy.callCount).to.equal(2);
  293. });
  294. });
  295. describe('when rendering a composite view with an empty collection and then resetting the collection', function() {
  296. beforeEach(function() {
  297. this.templateFn = _.template('composite <%= foo %>');
  298. this.ChildView = Backbone.Marionette.ItemView.extend({
  299. tagName: 'span',
  300. render: function() {
  301. this.$el.html(this.model.get('foo'));
  302. }
  303. });
  304. this.CompositeView = Backbone.Marionette.CompositeView.extend({
  305. childView: this.ChildView,
  306. template: this.templateFn,
  307. onRender: function() {}
  308. });
  309. this.m1 = new this.Model({foo: 'bar'});
  310. this.collection = new this.Collection();
  311. this.compositeView = new this.CompositeView({
  312. model: this.m1,
  313. collection: this.collection
  314. });
  315. this.compositeView.render();
  316. this.m2 = new this.Model({foo: 'baz'});
  317. this.collection.reset([this.m2]);
  318. });
  319. it('should render the template with the model', function() {
  320. expect(this.compositeView.$el).to.contain.$text('composite bar');
  321. });
  322. it('should render the collections items', function() {
  323. expect(this.compositeView.$el).to.contain.$text('baz');
  324. });
  325. });
  326. describe('when rendering a composite view without a collection', function() {
  327. beforeEach(function() {
  328. this.templateFn = _.template('composite <%= foo %>');
  329. this.ChildView = Backbone.Marionette.ItemView.extend({
  330. tagName: 'span',
  331. render: function() {
  332. this.$el.html(this.model.get('foo'));
  333. }
  334. });
  335. this.CompositeView = Backbone.Marionette.CompositeView.extend({
  336. childView: this.ChildView,
  337. template: this.templateFn,
  338. onRender: function() {}
  339. });
  340. this.m1 = new this.Model({foo: 'bar'});
  341. this.compositeView = new this.CompositeView({
  342. model: this.m1
  343. });
  344. this.compositeView.render();
  345. });
  346. it('should render the template with the model', function() {
  347. expect(this.compositeView.$el).to.contain.$text('composite bar');
  348. });
  349. it('should not render the collections items', function() {
  350. expect(this.compositeView.$el).not.to.contain.$text('baz');
  351. });
  352. });
  353. describe('when rendering a composite with a collection', function() {
  354. beforeEach(function() {
  355. this.templateFn = _.template('composite <%= foo %>');
  356. this.childViewTagName = 'span';
  357. this.ChildView = Backbone.Marionette.ItemView.extend({
  358. tagName: this.childViewTagName,
  359. render: function() {
  360. this.$el.html(this.model.get('foo'));
  361. }
  362. });
  363. this.CompositeView = Backbone.Marionette.CompositeView.extend({
  364. childView: this.ChildView,
  365. template: this.templateFn,
  366. onRender: function() {}
  367. });
  368. this.m1 = new this.Model({foo: 'bar'});
  369. this.m2 = new this.Model({foo: 'baz'});
  370. this.collection = new this.Collection([this.m2]);
  371. this.compositeView = new this.CompositeView({
  372. model: this.m1,
  373. collection: this.collection
  374. });
  375. this.compositeView.render();
  376. this.sinon.spy(this.compositeView, '_renderTemplate');
  377. this.sinon.spy(this.compositeView, 'getChildViewContainer');
  378. });
  379. describe('and then resetting the collection', function() {
  380. beforeEach(function() {
  381. this.m3 = new this.Model({foo: 'quux'});
  382. this.m4 = new this.Model({foo: 'widget'});
  383. this.collection.reset([this.m3, this.m4]);
  384. });
  385. it('should not re-render the template with the model', function() {
  386. expect(this.compositeView._renderTemplate).not.to.have.been.called;
  387. });
  388. it('should render the collections items', function() {
  389. expect(this.compositeView.$el).not.to.contain.$text('baz');
  390. expect(this.compositeView.$el).to.contain.$text('quux');
  391. expect(this.compositeView.$el).to.contain.$text('widget');
  392. });
  393. });
  394. describe('and then adding to the collection', function() {
  395. beforeEach(function() {
  396. this.m3 = new this.Model({foo: 'quux'});
  397. this.collection.add(this.m3);
  398. });
  399. it('should not re-render the template with the model', function() {
  400. expect(this.compositeView._renderTemplate).not.to.have.been.called;
  401. });
  402. it('should add to the collections items', function() {
  403. expect(this.compositeView.$el).to.contain.$text('bar');
  404. expect(this.compositeView.$el).to.contain.$text('baz');
  405. expect(this.compositeView.$el).to.contain.$text('quux');
  406. });
  407. it('shound send childView to getChildViewContainer', function() {
  408. expect(this.compositeView.getChildViewContainer).to.have.been.called;
  409. expect(this.compositeView.getChildViewContainer.getCall(0).args[1].tagName).to.equal(this.childViewTagName);
  410. });
  411. });
  412. describe('and then removing from the collection', function() {
  413. beforeEach(function() {
  414. this.model = this.collection.at(0);
  415. this.collection.remove(this.model);
  416. });
  417. it('should not re-render the template with the model', function() {
  418. expect(this.compositeView._renderTemplate).not.to.have.been.called;
  419. });
  420. it('should remove from the collections items', function() {
  421. expect(this.compositeView.$el).not.to.contain.$text('baz');
  422. });
  423. });
  424. });
  425. describe('when working with a composite and recursive model', function() {
  426. beforeEach(function() {
  427. this.data = {
  428. name: 'level 1',
  429. nodes: [
  430. {
  431. name: 'level 2',
  432. nodes: [
  433. {
  434. name: 'level 3'
  435. }
  436. ]
  437. }
  438. ]
  439. };
  440. this.node = new this.Node(this.data);
  441. this.treeView = new this.TreeView({
  442. model: this.node
  443. });
  444. this.treeView.render();
  445. });
  446. it('should render the template with the model', function() {
  447. expect(this.treeView.$el).to.contain.$text('level 1');
  448. });
  449. it('should render the collections items', function() {
  450. expect(this.treeView.$el).to.contain.$text('level 2');
  451. });
  452. it('should render all the levels of the nested object', function() {
  453. expect(this.treeView.$el).to.contain.$text('level 3');
  454. });
  455. });
  456. describe('when destroying a composite view', function() {
  457. beforeEach(function() {
  458. this.templateFn = _.template('composite <%= foo %>');
  459. this.ChildView = Backbone.Marionette.ItemView.extend({
  460. tagName: 'span',
  461. render: function() {
  462. this.$el.html(this.model.get('foo'));
  463. }
  464. });
  465. this.CompositeModelView = Backbone.Marionette.CompositeView.extend({
  466. childView: this.ChildView,
  467. template: this.templateFn
  468. });
  469. this.m1 = new this.Model({foo: 'bar'});
  470. this.m2 = new this.Model({foo: 'baz'});
  471. this.collection = new this.Collection();
  472. this.collection.add(this.m2);
  473. this.compositeView = new this.CompositeModelView({
  474. model: this.m1,
  475. collection: this.collection
  476. });
  477. this.sinon.spy(this.CompositeModelView.prototype, 'destroy');
  478. this.compositeView.render();
  479. this.compositeView.destroy();
  480. });
  481. it('should delete the model view', function() {
  482. expect(this.compositeView.renderedModelView).to.be.undefined;
  483. });
  484. it('should destroy the collection of views', function() {
  485. expect(this.CompositeModelView.prototype.destroy).to.have.been.calledOnce;
  486. });
  487. it('should be marked destroyed', function() {
  488. expect(this.compositeView).to.have.property('isDestroyed', true);
  489. });
  490. it('should be marked not rendered', function() {
  491. expect(this.compositeView).to.have.property('isRendered', false);
  492. });
  493. });
  494. describe('when rendering a composite view with no model, using a template to create a grid', function() {
  495. beforeEach(function() {
  496. this.gridTemplateFn = _.template('<thead><tr><th>Username</th><th>Full Name</th><tr></thead><tbody></tbody>');
  497. this.gridRowTemplateFn = _.template('<td><%= username %></td><td><%= fullname %></td>');
  498. // A Grid Row
  499. this.GridRow = Backbone.Marionette.ItemView.extend({
  500. tagName: 'tr',
  501. template: this.gridRowTemplateFn
  502. });
  503. // The grid view
  504. this.GridView = Backbone.Marionette.CompositeView.extend({
  505. tagName: 'table',
  506. template: this.gridTemplateFn,
  507. childView: this.GridRow,
  508. attachHtml: function(collectionView, itemView) {
  509. collectionView.$('tbody').append(itemView.el);
  510. }
  511. });
  512. this.userData = [
  513. {
  514. username: 'dbailey',
  515. fullname: 'Derick Bailey'
  516. },
  517. {
  518. username: 'jbob',
  519. fullname: 'Joe Bob'
  520. },
  521. {
  522. username: 'fbar',
  523. fullname: 'Foo Bar'
  524. }
  525. ];
  526. this.userList = new this.UserCollection(this.userData);
  527. this.gridView = new this.GridView({
  528. tagName: 'table',
  529. collection: this.userList
  530. });
  531. this.gridView.render();
  532. });
  533. it('should render the table', function() {
  534. expect(this.gridView.$('th').length).not.to.equal(0);
  535. });
  536. it('should render the users', function() {
  537. var body = this.gridView.$('tbody');
  538. expect(body).to.contain.$text('dbailey');
  539. expect(body).to.contain.$text('jbob');
  540. expect(body).to.contain.$text('fbar');
  541. });
  542. });
  543. describe('when a composite view has a ui elements hash', function() {
  544. beforeEach(function() {
  545. this.gridTemplateFn = _.template('<thead><tr><th>Username</th><th>Full Name</th><tr></thead><tbody></tbody>');
  546. this.gridRowTemplateFn = _.template('<td><%= username %></td><td><%= fullname %></td>');
  547. this.GridViewWithUIBindingsTemplateFn = _.template('<thead><tr><th><%= userHeader %></th><th><%= nameHeader %></th><tr></thead><tbody></tbody>');
  548. // A Grid Row
  549. this.GridRow = Backbone.Marionette.ItemView.extend({
  550. tagName: 'tr',
  551. template: this.gridRowTemplateFn
  552. });
  553. // The grid view
  554. this.GridView = Backbone.Marionette.CompositeView.extend({
  555. tagName: 'table',
  556. template: this.gridTemplateFn,
  557. childView: this.GridRow,
  558. attachHtml: function(collectionView, itemView) {
  559. collectionView.$('tbody').append(itemView.el);
  560. }
  561. });
  562. this.GridViewWithUIBindings = this.GridView.extend({
  563. template: this.GridViewWithUIBindingsTemplateFn,
  564. ui: {
  565. headersRow: 'thead tr',
  566. unfoundElement: '#unfound',
  567. itemRows: 'tbody tr'
  568. }
  569. });
  570. this.userData = [
  571. {
  572. username: 'dbailey',
  573. fullname: 'Derick Bailey'
  574. },
  575. {
  576. username: 'jbob',
  577. fullname: 'Joe Bob'
  578. }
  579. ];
  580. this.headersModel = new Backbone.Model({
  581. userHeader: 'Username',
  582. nameHeader: 'Full name'
  583. });
  584. this.userList = new this.UserCollection(this.userData);
  585. this.gridView = new this.GridViewWithUIBindings({
  586. tagName: 'table',
  587. model: this.headersModel,
  588. collection: this.userList
  589. });
  590. // We don't render the view here since we need more fine-tuned control on when the view is rendered,
  591. // specifically in the test that asserts the composite view template elements are accessible before
  592. // the collection is rendered.
  593. });
  594. describe('after the whole composite view finished rendering', function() {
  595. beforeEach(function() {
  596. this.gridView.render();
  597. });
  598. describe('accessing a ui element that belongs to the model template', function() {
  599. it('should return its jQuery selector if it can be found', function() {
  600. expect(this.gridView.ui.headersRow.find('th:first-child')).to.contain.$text('Username');
  601. });
  602. it('should return an empty jQuery object if it cannot be found', function() {
  603. expect(this.gridView.ui.unfoundElement.length).to.equal(0);
  604. });
  605. it('should return an up-to-date selector on subsequent renders', function() {
  606. // asserting state before subsequent render
  607. expect(this.gridView.ui.headersRow.find('th:first-child')).to.contain.$text('Username');
  608. this.headersModel.set('userHeader', 'User');
  609. this.gridView.render();
  610. expect(this.gridView.ui.headersRow.find('th:first-child')).to.contain.$text('User');
  611. });
  612. });
  613. describe('accessing a ui element that belongs to the collection', function() {
  614. // This test makes it clear that not allowing access to the collection elements is a design decision
  615. // and not a bug.
  616. it('should return an empty jQuery object', function() {
  617. expect(this.gridView.ui.itemRows.length).to.equal(0);
  618. });
  619. });
  620. });
  621. describe('after the model finished rendering, but before the collection rendered', function() {
  622. describe('accessing a ui element that belongs to the model template', function() {
  623. beforeEach(function() {
  624. var suite = this;
  625. this.gridView.onBeforeRender = function() {
  626. suite.called = true;
  627. };
  628. this.sinon.spy(this.gridView, 'onBeforeRender');
  629. this.gridView.render();
  630. });
  631. // this test enforces that ui elements should be accessible as soon as their html was inserted
  632. // to the DOM
  633. it('should return its jQuery selector', function() {
  634. expect(this.gridView.onBeforeRender).to.have.been.called;
  635. });
  636. it('should set the username', function() {
  637. expect($(this.gridView.ui.headersRow).find('th:first-child').text()).to.equal('Username');
  638. });
  639. });
  640. });
  641. });
  642. describe('when serializing view data', function() {
  643. beforeEach(function() {
  644. this.modelData = {foo: 'bar'};
  645. this.view = new Marionette.CompositeView();
  646. this.sinon.spy(this.view, 'serializeModel');
  647. });
  648. it('should return an empty object without data', function() {
  649. expect(this.view.serializeData()).to.deep.equal({});
  650. });
  651. describe('and the view has a model', function() {
  652. beforeEach(function() {
  653. this.view.model = new Backbone.Model(this.modelData);
  654. this.view.serializeData();
  655. });
  656. it('should call serializeModel', function() {
  657. expect(this.view.serializeModel).to.have.been.calledOnce;
  658. });
  659. });
  660. });
  661. describe('has a valid inheritance chain back to Marionette.CollectionView', function() {
  662. beforeEach(function() {
  663. this.constructor = this.sinon.spy(Marionette, 'CollectionView');
  664. this.compositeView = new Marionette.CompositeView();
  665. });
  666. it('calls the parent Marionette.CollectionViews constructor function on instantiation', function() {
  667. expect(this.constructor).to.have.been.calledOnce;
  668. });
  669. });
  670. });