template.js 5.5 KB

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