template.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* bender-tags: ui */
  6. /* global HTMLElement */
  7. 'use strict';
  8. import testUtils from '/tests/_utils/utils.js';
  9. import Template from '/ckeditor5/core/ui/template.js';
  10. import CKEditorError from '/ckeditor5/core/ckeditorerror.js';
  11. testUtils.createSinonSandbox();
  12. describe( 'Template', () => {
  13. describe( 'constructor', () => {
  14. it( 'accepts the definition', () => {
  15. let def = {
  16. tag: 'p'
  17. };
  18. expect( new Template( def ).definition ).to.equal( def );
  19. } );
  20. } );
  21. describe( 'render', () => {
  22. it( 'throws when wrong template definition', () => {
  23. expect( () => {
  24. new Template( {} ).render();
  25. } ).to.throw( CKEditorError, /ui-template-wrong-syntax/ );
  26. expect( () => {
  27. new Template( {
  28. tag: 'p',
  29. text: 'foo'
  30. } ).render();
  31. } ).to.throw( CKEditorError, /ui-template-wrong-syntax/ );
  32. } );
  33. it( 'creates a HTMLElement', () => {
  34. let el = new Template( {
  35. tag: 'p',
  36. attributes: {
  37. 'class': [ 'a', 'b' ],
  38. x: 'bar'
  39. },
  40. children: [ 'foo' ]
  41. } ).render();
  42. expect( el ).to.be.instanceof( HTMLElement );
  43. expect( el.parentNode ).to.be.null;
  44. expect( el.outerHTML ).to.be.equal( '<p class="a b" x="bar">foo</p>' );
  45. } );
  46. it( 'creates HTMLElement\'s children', () => {
  47. let el = new Template( {
  48. tag: 'p',
  49. attributes: {
  50. a: 'A'
  51. },
  52. children: [
  53. {
  54. tag: 'b',
  55. children: [ 'B' ]
  56. },
  57. {
  58. tag: 'i',
  59. children: [
  60. 'C',
  61. {
  62. tag: 'b',
  63. children: [ 'D' ]
  64. }
  65. ]
  66. }
  67. ]
  68. } ).render();
  69. expect( el.outerHTML ).to.be.equal( '<p a="A"><b>B</b><i>C<b>D</b></i></p>' );
  70. } );
  71. it( 'creates a Text node', () => {
  72. let node = new Template( { text: 'foo' } ).render();
  73. expect( node.nodeType ).to.be.equal( 3 );
  74. expect( node.textContent ).to.be.equal( 'foo' );
  75. } );
  76. it( 'creates a child Text Node (different syntaxes)', () => {
  77. let el = new Template( {
  78. tag: 'p',
  79. children: [
  80. 'foo',
  81. { text: 'bar' }
  82. ]
  83. } ).render();
  84. expect( el.outerHTML ).to.be.equal( '<p>foobar</p>' );
  85. } );
  86. it( 'creates multiple child Text Nodes', () => {
  87. let el = new Template( {
  88. tag: 'p',
  89. children: [ 'a', 'b', { text: 'c' }, 'd' ]
  90. } ).render();
  91. expect( el.childNodes ).to.have.length( 4 );
  92. expect( el.outerHTML ).to.be.equal( '<p>abcd</p>' );
  93. } );
  94. describe( 'callback', () => {
  95. it( 'works for attributes', () => {
  96. let spy1 = testUtils.sinon.spy();
  97. let spy2 = testUtils.sinon.spy();
  98. let el = new Template( {
  99. tag: 'p',
  100. attributes: {
  101. 'class': spy1
  102. },
  103. children: [
  104. {
  105. tag: 'span',
  106. attributes: {
  107. id: spy2
  108. }
  109. }
  110. ]
  111. } ).render();
  112. sinon.assert.calledWithExactly( spy1, el, sinon.match.func );
  113. sinon.assert.calledWithExactly( spy2, el.firstChild, sinon.match.func );
  114. spy1.firstCall.args[ 1 ]( el, 'foo' );
  115. spy2.firstCall.args[ 1 ]( el.firstChild, 'bar' );
  116. expect( el.outerHTML ).to.be.equal( '<p class="foo"><span id="bar"></span></p>' );
  117. } );
  118. it( 'works for "text" property', () => {
  119. let spy1 = testUtils.sinon.spy();
  120. let spy2 = testUtils.sinon.spy();
  121. let el = new Template( {
  122. tag: 'p',
  123. children: [
  124. {
  125. text: spy1
  126. },
  127. {
  128. tag: 'span',
  129. children: [
  130. {
  131. text: spy2
  132. }
  133. ]
  134. }
  135. ]
  136. } ).render();
  137. sinon.assert.calledWithExactly( spy1, el.firstChild, sinon.match.func );
  138. sinon.assert.calledWithExactly( spy2, el.lastChild.firstChild, sinon.match.func );
  139. spy2.firstCall.args[ 1 ]( el.lastChild.firstChild, 'bar' );
  140. expect( el.outerHTML ).to.be.equal( '<p><span>bar</span></p>' );
  141. spy1.firstCall.args[ 1 ]( el.firstChild, 'foo' );
  142. expect( el.outerHTML ).to.be.equal( '<p>foo<span>bar</span></p>' );
  143. } );
  144. it( 'works for "on" property', () => {
  145. let spy1 = testUtils.sinon.spy();
  146. let spy2 = testUtils.sinon.spy();
  147. let spy3 = testUtils.sinon.spy();
  148. let spy4 = testUtils.sinon.spy();
  149. let el = new Template( {
  150. tag: 'p',
  151. children: [
  152. {
  153. tag: 'span',
  154. on: {
  155. bar: spy2
  156. }
  157. }
  158. ],
  159. on: {
  160. foo: spy1,
  161. baz: [ spy3, spy4 ]
  162. }
  163. } ).render();
  164. sinon.assert.calledWithExactly( spy1, el, 'foo', null );
  165. sinon.assert.calledWithExactly( spy2, el.firstChild, 'bar', null );
  166. sinon.assert.calledWithExactly( spy3, el, 'baz', null );
  167. sinon.assert.calledWithExactly( spy4, el, 'baz', null );
  168. } );
  169. it( 'works for "on" property with selectors', () => {
  170. let spy1 = testUtils.sinon.spy();
  171. let spy2 = testUtils.sinon.spy();
  172. let spy3 = testUtils.sinon.spy();
  173. let spy4 = testUtils.sinon.spy();
  174. let el = new Template( {
  175. tag: 'p',
  176. children: [
  177. {
  178. tag: 'span',
  179. attributes: {
  180. 'id': 'x'
  181. }
  182. },
  183. {
  184. tag: 'span',
  185. attributes: {
  186. 'class': 'y'
  187. },
  188. on: {
  189. 'bar@p': spy2
  190. }
  191. },
  192. ],
  193. on: {
  194. 'foo@span': spy1,
  195. 'baz@.y': [ spy3, spy4 ]
  196. }
  197. } ).render();
  198. sinon.assert.calledWithExactly( spy1, el, 'foo', 'span' );
  199. sinon.assert.calledWithExactly( spy2, el.lastChild, 'bar', 'p' );
  200. sinon.assert.calledWithExactly( spy3, el, 'baz', '.y' );
  201. sinon.assert.calledWithExactly( spy4, el, 'baz', '.y' );
  202. } );
  203. } );
  204. } );
  205. } );