view-to-model-converters.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import ViewConversionDispatcher from '../../src/conversion/viewconversiondispatcher';
  6. import ViewContainerElement from '../../src/view/containerelement';
  7. import ViewDocumentFragment from '../../src/view/documentfragment';
  8. import ViewText from '../../src/view/text';
  9. import Model from '../../src/model/model';
  10. import ModelDocumentFragment from '../../src/model/documentfragment';
  11. import ModelElement from '../../src/model/element';
  12. import ModelText from '../../src/model/text';
  13. import ModelRange from '../../src/model/range';
  14. import ModelPosition from '../../src/model/position';
  15. import { convertToModelFragment, convertText } from '../../src/conversion/view-to-model-converters';
  16. describe( 'view-to-model-converters', () => {
  17. let dispatcher, schema, context, model;
  18. beforeEach( () => {
  19. model = new Model();
  20. schema = model.schema;
  21. schema.register( 'paragraph', { inheritAllFrom: '$block' } );
  22. schema.extend( '$text', { allowIn: '$root' } );
  23. context = [ '$root' ];
  24. dispatcher = new ViewConversionDispatcher( model, { schema } );
  25. } );
  26. describe( 'convertText()', () => {
  27. it( 'should return converter converting ViewText to ModelText', () => {
  28. const viewText = new ViewText( 'foobar' );
  29. dispatcher.on( 'text', convertText() );
  30. const conversionResult = dispatcher.convert( viewText );
  31. expect( conversionResult ).to.be.instanceof( ModelDocumentFragment );
  32. expect( conversionResult.getChild( 0 ) ).to.be.instanceof( ModelText );
  33. expect( conversionResult.getChild( 0 ).data ).to.equal( 'foobar' );
  34. } );
  35. it( 'should not convert already consumed texts', () => {
  36. const viewText = new ViewText( 'foofuckbafuckr' );
  37. // Default converter for elements. Returns just converted children. Added with lowest priority.
  38. dispatcher.on( 'text', convertText(), { priority: 'lowest' } );
  39. // Added with normal priority. Should make the above converter not fire.
  40. dispatcher.on( 'text', ( evt, data, conversionApi ) => {
  41. if ( conversionApi.consumable.consume( data.viewItem ) ) {
  42. const text = conversionApi.writer.createText( data.viewItem.data.replace( /fuck/gi, '****' ) );
  43. conversionApi.writer.insert( text, data.modelCursor );
  44. data.modelRange = ModelRange.createFromPositionAndShift( data.modelCursor, text.offsetSize );
  45. data.modelCursor = data.modelRange.end;
  46. }
  47. } );
  48. const conversionResult = dispatcher.convert( viewText, context );
  49. expect( conversionResult ).to.be.instanceof( ModelDocumentFragment );
  50. expect( conversionResult.getChild( 0 ) ).to.be.instanceof( ModelText );
  51. expect( conversionResult.getChild( 0 ).data ).to.equal( 'foo****ba****r' );
  52. } );
  53. it( 'should not convert text if it is wrong with schema', () => {
  54. schema.addChildCheck( ( ctx, childDef ) => {
  55. if ( childDef.name == '$text' && ctx.endsWith( '$root' ) ) {
  56. return false;
  57. }
  58. } );
  59. const viewText = new ViewText( 'foobar' );
  60. dispatcher.on( 'text', convertText() );
  61. let conversionResult = dispatcher.convert( viewText, context );
  62. expect( conversionResult ).to.be.instanceof( ModelDocumentFragment );
  63. expect( conversionResult.childCount ).to.equal( 0 );
  64. conversionResult = dispatcher.convert( viewText, [ '$block' ] );
  65. expect( conversionResult ).to.be.instanceof( ModelDocumentFragment );
  66. expect( conversionResult.childCount ).to.equal( 1 );
  67. expect( conversionResult.getChild( 0 ) ).to.be.instanceof( ModelText );
  68. expect( conversionResult.getChild( 0 ).data ).to.equal( 'foobar' );
  69. } );
  70. it( 'should support unicode', () => {
  71. const viewText = new ViewText( 'நிலைக்கு' );
  72. dispatcher.on( 'text', convertText() );
  73. const conversionResult = dispatcher.convert( viewText, context );
  74. expect( conversionResult ).to.be.instanceof( ModelDocumentFragment );
  75. expect( conversionResult.getChild( 0 ) ).to.be.instanceof( ModelText );
  76. expect( conversionResult.getChild( 0 ).data ).to.equal( 'நிலைக்கு' );
  77. } );
  78. } );
  79. describe( 'convertToModelFragment()', () => {
  80. it( 'should return converter converting whole ViewDocumentFragment to ModelDocumentFragment', () => {
  81. const viewFragment = new ViewDocumentFragment( [
  82. new ViewContainerElement( 'p', null, new ViewText( 'foo' ) ),
  83. new ViewText( 'bar' )
  84. ] );
  85. // To get any meaningful results we have to actually convert something.
  86. dispatcher.on( 'text', convertText() );
  87. // This way P element won't be converted per-se but will fire converting it's children.
  88. dispatcher.on( 'element', convertToModelFragment() );
  89. dispatcher.on( 'documentFragment', convertToModelFragment() );
  90. const conversionResult = dispatcher.convert( viewFragment, context );
  91. expect( conversionResult ).to.be.instanceof( ModelDocumentFragment );
  92. expect( conversionResult.maxOffset ).to.equal( 6 );
  93. expect( conversionResult.getChild( 0 ).data ).to.equal( 'foobar' );
  94. } );
  95. it( 'should not convert already consumed (converted) changes', () => {
  96. const viewP = new ViewContainerElement( 'p', null, new ViewText( 'foo' ) );
  97. // To get any meaningful results we have to actually convert something.
  98. dispatcher.on( 'text', convertText() );
  99. // Default converter for elements. Returns just converted children. Added with lowest priority.
  100. dispatcher.on( 'element', convertToModelFragment(), { priority: 'lowest' } );
  101. // Added with normal priority. Should make the above converter not fire.
  102. dispatcher.on( 'element:p', ( evt, data, conversionApi ) => {
  103. if ( conversionApi.consumable.consume( data.viewItem, { name: true } ) ) {
  104. const paragraph = conversionApi.writer.createElement( 'paragraph' );
  105. conversionApi.writer.insert( paragraph, data.modelCursor );
  106. conversionApi.convertChildren( data.viewItem, ModelPosition.createAt( paragraph ) );
  107. data.modelRange = ModelRange.createOn( paragraph );
  108. data.modelCursor = data.modelRange.end;
  109. }
  110. } );
  111. const conversionResult = dispatcher.convert( viewP, context );
  112. expect( conversionResult ).to.be.instanceof( ModelDocumentFragment );
  113. expect( conversionResult.getChild( 0 ) ).to.be.instanceof( ModelElement );
  114. expect( conversionResult.getChild( 0 ).name ).to.equal( 'paragraph' );
  115. expect( conversionResult.getChild( 0 ).maxOffset ).to.equal( 3 );
  116. expect( conversionResult.getChild( 0 ).getChild( 0 ).data ).to.equal( 'foo' );
  117. } );
  118. } );
  119. } );