viewconversiondispatcher.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* bender-tags: conversion */
  6. import ViewConversionDispatcher from '/ckeditor5/engine/conversion/viewconversiondispatcher.js';
  7. import ViewContainerElement from '/ckeditor5/engine/view/containerelement.js';
  8. import ViewAttributeElement from '/ckeditor5/engine/view/attributeelement.js';
  9. import ViewDocumentFragment from '/ckeditor5/engine/view/documentfragment.js';
  10. import ViewText from '/ckeditor5/engine/view/text.js';
  11. describe( 'ViewConversionDispatcher', () => {
  12. describe( 'constructor', () => {
  13. it( 'should create ViewConversionDispatcher with passed api', () => {
  14. const apiObj = {};
  15. const dispatcher = new ViewConversionDispatcher( { apiObj } );
  16. expect( dispatcher.conversionApi.apiObj ).to.equal( apiObj );
  17. expect( dispatcher.conversionApi ).to.have.property( 'convertItem' ).that.is.instanceof( Function );
  18. expect( dispatcher.conversionApi ).to.have.property( 'convertChildren' ).that.is.instanceof( Function );
  19. } );
  20. } );
  21. describe( 'convert', () => {
  22. let dispatcher;
  23. beforeEach( () => {
  24. dispatcher = new ViewConversionDispatcher();
  25. } );
  26. it( 'should fire viewCleanup event on converted view part', () => {
  27. sinon.spy( dispatcher, 'fire' );
  28. const viewP = new ViewContainerElement( 'p' );
  29. dispatcher.convert( viewP );
  30. expect( dispatcher.fire.calledWith( 'viewCleanup', viewP ) ).to.be.true;
  31. } );
  32. it( 'should fire proper events', () => {
  33. const viewText = new ViewText( 'foobar' );
  34. const viewElement = new ViewContainerElement( 'p', null, viewText );
  35. const viewFragment = new ViewDocumentFragment( viewElement );
  36. sinon.spy( dispatcher, 'fire' );
  37. dispatcher.convert( viewText );
  38. dispatcher.convert( viewElement );
  39. dispatcher.convert( viewFragment );
  40. expect( dispatcher.fire.calledWith( 'text' ) ).to.be.true;
  41. expect( dispatcher.fire.calledWith( 'element:p' ) ).to.be.true;
  42. expect( dispatcher.fire.calledWith( 'documentFragment' ) ).to.be.true;
  43. } );
  44. it( 'should convert ViewText', () => {
  45. const viewText = new ViewText( 'foobar' );
  46. dispatcher.on( 'text', ( evt, data, consumable, conversionApi ) => {
  47. const result = {
  48. eventName: evt.name,
  49. input: data.input,
  50. // Check whether additional data has been passed.
  51. foo: data.foo
  52. };
  53. // Check whether consumable has appropriate value to consume.
  54. expect( consumable.consume( data.input ) ).to.be.true;
  55. // Check whether conversionApi of `dispatcher` has been passed.
  56. expect( conversionApi ).to.equal( dispatcher.conversionApi );
  57. // Set conversion result to `output` property of `data`.
  58. // Later we will check if it was returned by `convert` method.
  59. data.output = result;
  60. } );
  61. // Use `additionalData` parameter to check if it was passed to the event.
  62. const result = dispatcher.convert( viewText, { foo: 'bar' } );
  63. // Check conversion result.
  64. expect( result ).to.deep.equal( {
  65. eventName: 'text',
  66. input: viewText,
  67. foo: 'bar'
  68. } );
  69. } );
  70. it( 'should convert ViewContainerElement', () => {
  71. const viewElement = new ViewContainerElement( 'p', { attrKey: 'attrValue' } );
  72. dispatcher.on( 'element', ( evt, data, consumable, conversionApi ) => {
  73. const result = {
  74. eventName: evt.name,
  75. input: data.input,
  76. // Check whether additional data has been passed.
  77. foo: data.foo
  78. };
  79. // Check whether consumable has appropriate value to consume.
  80. expect( consumable.consume( data.input, { name: true } ) ).to.be.true;
  81. expect( consumable.consume( data.input, { attribute: 'attrKey' } ) ).to.be.true;
  82. // Check whether conversionApi of `dispatcher` has been passed.
  83. expect( conversionApi ).to.equal( dispatcher.conversionApi );
  84. // Set conversion result to `output` property of `data`.
  85. // Later we will check if it was returned by `convert` method.
  86. data.output = result;
  87. } );
  88. // Use `additionalData` parameter to check if it was passed to the event.
  89. const result = dispatcher.convert( viewElement, { foo: 'bar' } );
  90. // Check conversion result.
  91. expect( result ).to.deep.equal( {
  92. eventName: 'element:p',
  93. input: viewElement,
  94. foo: 'bar'
  95. } );
  96. } );
  97. it( 'should convert ViewDocumentFragment', () => {
  98. const viewFragment = new ViewDocumentFragment();
  99. dispatcher.on( 'documentFragment', ( evt, data, consumable, conversionApi ) => {
  100. const result = {
  101. eventName: evt.name,
  102. input: data.input,
  103. // Check whether additional data has been passed.
  104. foo: data.foo
  105. };
  106. // Check whether consumable has appropriate value to consume.
  107. expect( consumable.consume( data.input ) ).to.be.true;
  108. // Check whether conversionApi of `dispatcher` has been passed.
  109. expect( conversionApi ).to.equal( dispatcher.conversionApi );
  110. // Set conversion result to `output` property of `data`.
  111. // Later we will check if it was returned by `convert` method.
  112. data.output = result;
  113. } );
  114. // Use `additionalData` parameter to check if it was passed to the event.
  115. const result = dispatcher.convert( viewFragment, { foo: 'bar' } );
  116. // Check conversion result.
  117. expect( result ).to.deep.equal( {
  118. eventName: 'documentFragment',
  119. input: viewFragment,
  120. foo: 'bar'
  121. } );
  122. } );
  123. } );
  124. describe( 'conversionApi#convertItem', () => {
  125. it( 'should convert view elements and view text', () => {
  126. const dispatcher = new ViewConversionDispatcher();
  127. const viewFragment = new ViewDocumentFragment( [
  128. new ViewContainerElement( 'p' ), new ViewText( 'foobar' )
  129. ] );
  130. dispatcher.on( 'text', ( evt, data ) => {
  131. data.output = { text: data.input.data };
  132. } );
  133. dispatcher.on( 'element:p', ( evt, data ) => {
  134. data.output = { name: 'p' };
  135. } );
  136. dispatcher.on( 'documentFragment', ( evt, data, consumable, conversionApi ) => {
  137. data.output = [];
  138. for ( let child of data.input.getChildren() ) {
  139. data.output.push( conversionApi.convertItem( child ) );
  140. }
  141. } );
  142. const result = dispatcher.convert( viewFragment );
  143. expect( result ).to.deep.equal( [
  144. { name: 'p' },
  145. { text: 'foobar' }
  146. ] );
  147. } );
  148. } );
  149. describe( 'conversionApi#convertChildren', () => {
  150. it( 'should fire proper events for all children of passed view part', () => {
  151. const dispatcher = new ViewConversionDispatcher();
  152. const viewFragment = new ViewDocumentFragment( [
  153. new ViewContainerElement( 'p' ), new ViewText( 'foobar' )
  154. ] );
  155. dispatcher.on( 'text', ( evt, data ) => {
  156. data.output = { text: data.input.data };
  157. } );
  158. dispatcher.on( 'element:p', ( evt, data ) => {
  159. data.output = { name: 'p' };
  160. } );
  161. dispatcher.on( 'documentFragment', ( evt, data, consumable, conversionApi ) => {
  162. data.output = conversionApi.convertChildren( data.input );
  163. } );
  164. const result = dispatcher.convert( viewFragment );
  165. expect( result ).to.deep.equal( [
  166. { name: 'p' },
  167. { text: 'foobar' }
  168. ] );
  169. } );
  170. it( 'should flatten structure of non-converted elements', () => {
  171. const dispatcher = new ViewConversionDispatcher();
  172. dispatcher.on( 'text', ( evt, data ) => {
  173. data.output = data.input.data;
  174. } );
  175. dispatcher.on( 'element', ( evt, data, consumable, conversionApi ) => {
  176. data.output = conversionApi.convertChildren( data.input, consumable );
  177. } );
  178. const viewStructure = new ViewContainerElement( 'div', null, [
  179. new ViewContainerElement( 'p', null, [
  180. new ViewContainerElement( 'span', { class: 'nice' }, [
  181. new ViewAttributeElement( 'a', { href: 'foo.html' }, new ViewText( 'foo' ) ),
  182. new ViewText( ' bar ' ),
  183. new ViewAttributeElement( 'i', null, new ViewText( 'xyz' ) )
  184. ] )
  185. ] ),
  186. new ViewContainerElement( 'p', null, [
  187. new ViewAttributeElement( 'strong', null, [
  188. new ViewText( 'aaa ' ),
  189. new ViewAttributeElement( 'span', null, new ViewText( 'bbb' ) ),
  190. new ViewText( ' ' ),
  191. new ViewAttributeElement( 'a', { href: 'bar.html' }, new ViewText( 'ccc' ) )
  192. ] )
  193. ] )
  194. ] );
  195. expect( dispatcher.convert( viewStructure ) ).to.deep.equal( [ 'foo', ' bar ', 'xyz', 'aaa ', 'bbb', ' ', 'ccc' ] );
  196. } );
  197. } );
  198. } );