utils.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /**
  2. * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. import Model from '../../../src/model/model';
  6. import DocumentFragment from '../../../src/model/documentfragment';
  7. import Element from '../../../src/model/element';
  8. import Text from '../../../src/model/text';
  9. import TextProxy from '../../../src/model/textproxy';
  10. import Position from '../../../src/model/position';
  11. import Range from '../../../src/model/range';
  12. import * as utils from '../../../src/model/operation/utils';
  13. import { getData } from '../../../src/dev-utils/model';
  14. import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
  15. let model, doc, root;
  16. describe( 'Operation utils', () => {
  17. beforeEach( () => {
  18. model = new Model();
  19. doc = model.document;
  20. model.schema.extend( '$text', { allowIn: '$root' } );
  21. root = doc.createRoot();
  22. // index: 0001112333
  23. // offset: 0123456789
  24. // data: foobarIxyz
  25. // bold: ___BBBB___
  26. root._appendChild( [
  27. new Text( 'foo' ),
  28. new Text( 'bar', { bold: true } ),
  29. new Element( 'image', { src: 'img.jpg' } ),
  30. new Text( 'xyz' )
  31. ] );
  32. } );
  33. describe( 'insert', () => {
  34. it( 'should insert nodes between nodes', () => {
  35. utils._insert( Position._createAt( root, 3 ), [ 'xxx', new Element( 'p' ) ] );
  36. expectData( 'fooxxx<p></p><$text bold="true">bar</$text><image src="img.jpg"></image>xyz' );
  37. } );
  38. it( 'should split text node if nodes at inserted at offset inside text node', () => {
  39. utils._insert( Position._createAt( root, 5 ), new Element( 'p' ) );
  40. expectData( 'foo<$text bold="true">ba</$text><p></p><$text bold="true">r</$text><image src="img.jpg"></image>xyz' );
  41. } );
  42. it( 'should merge text nodes if possible', () => {
  43. utils._insert( Position._createAt( root, 3 ), new Text( 'xxx', { bold: true } ) );
  44. expectData( 'foo<$text bold="true">xxxbar</$text><image src="img.jpg"></image>xyz' );
  45. } );
  46. } );
  47. describe( 'remove', () => {
  48. it( 'should remove nodes in given range', () => {
  49. const range = new Range( Position._createAt( root, 3 ), Position._createAt( root, 6 ) );
  50. utils._remove( range );
  51. expectData( 'foo<image src="img.jpg"></image>xyz' );
  52. } );
  53. it( 'should split text node if range starts or ends inside text node', () => {
  54. const range = new Range( Position._createAt( root, 1 ), Position._createAt( root, 5 ) );
  55. utils._remove( range );
  56. expectData( 'f<$text bold="true">r</$text><image src="img.jpg"></image>xyz' );
  57. } );
  58. it( 'should merge text nodes if possible', () => {
  59. const range = new Range( Position._createAt( root, 3 ), Position._createAt( root, 7 ) );
  60. utils._remove( range );
  61. expectData( 'fooxyz' );
  62. expect( root.childCount ).to.equal( 1 );
  63. } );
  64. it( 'should throw if given range is not flat', () => {
  65. expectToThrowCKEditorError( () => {
  66. utils._remove( new Range( new Position( root, [ 0 ] ), new Position( root, [ 1, 2 ] ) ) );
  67. }, /operation-utils-remove-range-not-flat/ );
  68. } );
  69. } );
  70. describe( 'move', () => {
  71. it( 'should move a range of nodes', () => {
  72. const range = new Range( Position._createAt( root, 3 ), Position._createAt( root, 6 ) );
  73. utils._move( range, Position._createAt( root, 0 ) );
  74. expectData( '<$text bold="true">bar</$text>foo<image src="img.jpg"></image>xyz' );
  75. } );
  76. it( 'should correctly move if target position is in same element as moved range, but after range', () => {
  77. const range = new Range( Position._createAt( root, 3 ), Position._createAt( root, 6 ) );
  78. utils._move( range, Position._createAt( root, 10 ) );
  79. expectData( 'foo<image src="img.jpg"></image>xyz<$text bold="true">bar</$text>' );
  80. } );
  81. it( 'should throw if given range is not flat', () => {
  82. expectToThrowCKEditorError( () => {
  83. utils._move( new Range( new Position( root, [ 0 ] ), new Position( root, [ 1, 2 ] ) ), null );
  84. }, /operation-utils-move-range-not-flat/ );
  85. } );
  86. } );
  87. describe( 'setAttribute', () => {
  88. it( 'should set attribute on given range of nodes', () => {
  89. const range = new Range( Position._createAt( root, 6 ), Position._createAt( root, 8 ) );
  90. utils._setAttribute( range, 'newAttr', true );
  91. expectData( 'foo<$text bold="true">bar</$text><image newAttr="true" src="img.jpg"></image><$text newAttr="true">x</$text>yz' );
  92. } );
  93. it( 'should remove attribute if null was passed as a value', () => {
  94. const range = new Range( Position._createAt( root, 6 ), Position._createAt( root, 7 ) );
  95. utils._setAttribute( range, 'src', null );
  96. expectData( 'foo<$text bold="true">bar</$text><image></image>xyz' );
  97. } );
  98. it( 'should merge nodes if possible', () => {
  99. const range = new Range( Position._createAt( root, 0 ), Position._createAt( root, 3 ) );
  100. utils._setAttribute( range, 'bold', true );
  101. expectData( '<$text bold="true">foobar</$text><image src="img.jpg"></image>xyz' );
  102. } );
  103. } );
  104. } );
  105. describe( 'normalizeNodes', () => {
  106. it( 'should change single object into an array', () => {
  107. const p = new Element( 'p' );
  108. expect( utils._normalizeNodes( p ) ).to.deep.equal( [ p ] );
  109. } );
  110. it( 'should change strings to text nodes', () => {
  111. const text = utils._normalizeNodes( 'abc' )[ 0 ];
  112. expect( text ).to.be.instanceof( Text );
  113. expect( text.data ).to.equal( 'abc' );
  114. } );
  115. it( 'should change text proxies to text nodes', () => {
  116. const textNode = new Text( 'abc' );
  117. const textProxy = new TextProxy( textNode, 1, 1 );
  118. const text = utils._normalizeNodes( textProxy )[ 0 ];
  119. expect( text ).to.be.instanceof( Text );
  120. expect( text.data ).to.equal( 'b' );
  121. } );
  122. it( 'should not change elements', () => {
  123. const p = new Element( 'p' );
  124. expect( utils._normalizeNodes( p )[ 0 ] ).to.equal( p );
  125. } );
  126. it( 'should omit unrecognized objects', () => {
  127. expect( utils._normalizeNodes( 1 ) ).to.deep.equal( [] );
  128. } );
  129. it( 'should accept arrays', () => {
  130. const text = new Text( 'foo', { bold: true } );
  131. const image = new Element( 'image' );
  132. const nodes = [ 'abc', text, image, 1, 'xyz' ];
  133. const normalized = utils._normalizeNodes( nodes );
  134. expect( normalized[ 0 ] ).to.be.instanceof( Text );
  135. expect( normalized[ 1 ] ).to.equal( text );
  136. expect( normalized[ 2 ] ).to.equal( image );
  137. expect( normalized[ 3 ] ).to.be.instanceof( Text );
  138. } );
  139. it( 'should merge text nodes if mergeTextNodes flag is set to true', () => {
  140. const normalized = utils._normalizeNodes( [ 'foo', 'bar' ], true );
  141. expect( normalized.length ).to.equal( 1 );
  142. expect( normalized[ 0 ].data ).to.equal( 'foobar' );
  143. } );
  144. it( 'should replace document fragment by the list of it\'s children', () => {
  145. const nodes = [
  146. new Text( 'foo', { bold: true } ),
  147. new DocumentFragment( [ new Text( 'bar', { bold: true } ), new Element( 'image' ) ] ),
  148. 'xyz'
  149. ];
  150. const normalized = utils._normalizeNodes( nodes, true );
  151. expect( normalized[ 0 ] ).to.be.instanceof( Text );
  152. expect( normalized[ 0 ].getAttribute( 'bold' ) ).to.be.true;
  153. expect( normalized[ 0 ].data ).to.equal( 'foobar' );
  154. expect( normalized[ 1 ].name ).to.equal( 'image' );
  155. expect( normalized[ 2 ].data ).to.equal( 'xyz' );
  156. } );
  157. } );
  158. function expectData( html ) {
  159. expect( getData( model, { withoutSelection: true } ) ).to.equal( html );
  160. }