changeoperation.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. /**
  2. * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* bender-tags: document */
  6. 'use strict';
  7. var modules = bender.amd.require(
  8. 'document/document',
  9. 'document/changeoperation',
  10. 'document/position',
  11. 'document/range',
  12. 'document/character',
  13. 'document/attribute',
  14. 'document/nodelist',
  15. 'document/text',
  16. 'ckeditorerror' );
  17. describe( 'ChangeOperation', function() {
  18. it( 'should insert attribute to the set of nodes', function() {
  19. var Document = modules[ 'document/document' ];
  20. var ChangeOperation = modules[ 'document/changeoperation' ];
  21. var Position = modules[ 'document/position' ];
  22. var Range = modules[ 'document/range' ];
  23. var Attribute = modules[ 'document/attribute' ];
  24. var doc = new Document();
  25. var newAttr = new Attribute( 'isNew', true );
  26. doc.root.insertChildren( 0, 'bar' );
  27. doc.applyOperation( new ChangeOperation(
  28. new Range( new Position( [ 0 ], doc.root ), new Position( [ 2 ], doc.root ) ),
  29. null,
  30. newAttr,
  31. doc.version ) );
  32. expect( doc.version ).to.be.equal( 1 );
  33. expect( doc.root.getChildCount() ).to.be.equal( 3 );
  34. expect( doc.root.getChild( 0 ).hasAttr( newAttr ) ).to.be.true;
  35. expect( doc.root.getChild( 1 ).hasAttr( newAttr ) ).to.be.true;
  36. expect( doc.root.getChild( 2 ).getAttrCount() ).to.be.equal( 0 );
  37. } );
  38. it( 'should insert attribute to multiple ranges', function() {
  39. var Document = modules[ 'document/document' ];
  40. var ChangeOperation = modules[ 'document/changeoperation' ];
  41. var Position = modules[ 'document/position' ];
  42. var Range = modules[ 'document/range' ];
  43. var Attribute = modules[ 'document/attribute' ];
  44. var doc = new Document();
  45. var newAttr = new Attribute( 'isNew', true );
  46. doc.root.insertChildren( 0, 'bar' );
  47. doc.applyOperation( new ChangeOperation(
  48. [
  49. new Range( new Position( [ 0 ], doc.root ), new Position( [ 1 ], doc.root ) ),
  50. new Range( new Position( [ 2 ], doc.root ), new Position( [ 3 ], doc.root ) )
  51. ],
  52. null,
  53. newAttr,
  54. doc.version ) );
  55. expect( doc.version ).to.be.equal( 1 );
  56. expect( doc.root.getChildCount() ).to.be.equal( 3 );
  57. expect( doc.root.getChild( 0 ).hasAttr( newAttr ) ).to.be.true;
  58. expect( doc.root.getChild( 1 ).getAttrCount() ).to.be.equal( 0 );
  59. expect( doc.root.getChild( 2 ).hasAttr( newAttr ) ).to.be.true;
  60. } );
  61. it( 'should add attribute to the existing attributes', function() {
  62. var Document = modules[ 'document/document' ];
  63. var ChangeOperation = modules[ 'document/changeoperation' ];
  64. var Position = modules[ 'document/position' ];
  65. var Range = modules[ 'document/range' ];
  66. var Character = modules[ 'document/character' ];
  67. var Attribute = modules[ 'document/attribute' ];
  68. var doc = new Document();
  69. var newAttr = new Attribute( 'isNew', true );
  70. var fooAttr = new Attribute( 'foo', true );
  71. var barAttr = new Attribute( 'bar', true );
  72. doc.root.insertChildren( 0, new Character( 'x', [ fooAttr, barAttr ] ) );
  73. doc.applyOperation( new ChangeOperation(
  74. new Range( new Position( [ 0 ], doc.root ), new Position( [ 1 ], doc.root ) ),
  75. null,
  76. newAttr,
  77. doc.version ) );
  78. expect( doc.version ).to.be.equal( 1 );
  79. expect( doc.root.getChildCount() ).to.be.equal( 1 );
  80. expect( doc.root.getChild( 0 ).getAttrCount() ).to.be.equal( 3 );
  81. expect( doc.root.getChild( 0 ).hasAttr( newAttr ) ).to.be.true;
  82. expect( doc.root.getChild( 0 ).hasAttr( fooAttr ) ).to.be.true;
  83. expect( doc.root.getChild( 0 ).hasAttr( barAttr ) ).to.be.true;
  84. } );
  85. it( 'should change attributes on multiple ranges', function() {
  86. var Document = modules[ 'document/document' ];
  87. var ChangeOperation = modules[ 'document/changeoperation' ];
  88. var Position = modules[ 'document/position' ];
  89. var Range = modules[ 'document/range' ];
  90. var Text = modules[ 'document/text' ];
  91. var Attribute = modules[ 'document/attribute' ];
  92. var doc = new Document();
  93. var oldAttr = new Attribute( 'isNew', false );
  94. var newAttr = new Attribute( 'isNew', true );
  95. doc.root.insertChildren( 0, new Text( 'bar', [ oldAttr ] ) );
  96. doc.applyOperation( new ChangeOperation(
  97. [
  98. new Range( new Position( [ 0 ], doc.root ), new Position( [ 1 ], doc.root ) ),
  99. new Range( new Position( [ 2 ], doc.root ), new Position( [ 3 ], doc.root ) )
  100. ],
  101. oldAttr,
  102. newAttr,
  103. doc.version ) );
  104. expect( doc.version ).to.be.equal( 1 );
  105. expect( doc.root.getChildCount() ).to.be.equal( 3 );
  106. expect( doc.root.getChild( 0 ).getAttrCount() ).to.be.equal( 1 );
  107. expect( doc.root.getChild( 0 ).hasAttr( newAttr ) ).to.be.true;
  108. expect( doc.root.getChild( 1 ).getAttrCount() ).to.be.equal( 1 );
  109. expect( doc.root.getChild( 1 ).hasAttr( oldAttr ) ).to.be.true;
  110. expect( doc.root.getChild( 2 ).getAttrCount() ).to.be.equal( 1 );
  111. expect( doc.root.getChild( 2 ).hasAttr( newAttr ) ).to.be.true;
  112. } );
  113. it( 'should change attribute to the set of nodes', function() {
  114. var Document = modules[ 'document/document' ];
  115. var ChangeOperation = modules[ 'document/changeoperation' ];
  116. var Position = modules[ 'document/position' ];
  117. var Range = modules[ 'document/range' ];
  118. var Text = modules[ 'document/text' ];
  119. var Attribute = modules[ 'document/attribute' ];
  120. var doc = new Document();
  121. var oldAttr = new Attribute( 'isNew', false );
  122. var newAttr = new Attribute( 'isNew', true );
  123. doc.root.insertChildren( 0, new Text( 'bar', [ oldAttr ] ) );
  124. doc.applyOperation( new ChangeOperation(
  125. new Range( new Position( [ 0 ], doc.root ), new Position( [ 2 ], doc.root ) ),
  126. oldAttr,
  127. newAttr,
  128. doc.version ) );
  129. expect( doc.version ).to.be.equal( 1 );
  130. expect( doc.root.getChildCount() ).to.be.equal( 3 );
  131. expect( doc.root.getChild( 0 ).getAttrCount() ).to.be.equal( 1 );
  132. expect( doc.root.getChild( 0 ).hasAttr( newAttr ) ).to.be.true;
  133. expect( doc.root.getChild( 1 ).getAttrCount() ).to.be.equal( 1 );
  134. expect( doc.root.getChild( 1 ).hasAttr( newAttr ) ).to.be.true;
  135. expect( doc.root.getChild( 2 ).getAttrCount() ).to.be.equal( 1 );
  136. expect( doc.root.getChild( 2 ).hasAttr( oldAttr ) ).to.be.true;
  137. } );
  138. it( 'should change attribute in the middle of existing attributes', function() {
  139. var Document = modules[ 'document/document' ];
  140. var ChangeOperation = modules[ 'document/changeoperation' ];
  141. var Position = modules[ 'document/position' ];
  142. var Range = modules[ 'document/range' ];
  143. var Character = modules[ 'document/character' ];
  144. var Attribute = modules[ 'document/attribute' ];
  145. var doc = new Document();
  146. var fooAttr = new Attribute( 'foo', true );
  147. var x1Attr = new Attribute( 'x', 1 );
  148. var x2Attr = new Attribute( 'x', 2 );
  149. var barAttr = new Attribute( 'bar', true );
  150. doc.root.insertChildren( 0, new Character( 'x', [ fooAttr, x1Attr, barAttr ] ) );
  151. doc.applyOperation( new ChangeOperation(
  152. new Range( new Position( [ 0 ], doc.root ), new Position( [ 1 ], doc.root ) ),
  153. x1Attr,
  154. x2Attr,
  155. doc.version ) );
  156. expect( doc.version ).to.be.equal( 1 );
  157. expect( doc.root.getChildCount() ).to.be.equal( 1 );
  158. expect( doc.root.getChild( 0 ).getAttrCount() ).to.be.equal( 3 );
  159. expect( doc.root.getChild( 0 ).hasAttr( fooAttr ) ).to.be.true;
  160. expect( doc.root.getChild( 0 ).hasAttr( x2Attr ) ).to.be.true;
  161. expect( doc.root.getChild( 0 ).hasAttr( barAttr ) ).to.be.true;
  162. } );
  163. it( 'should remove attribute', function() {
  164. var Document = modules[ 'document/document' ];
  165. var ChangeOperation = modules[ 'document/changeoperation' ];
  166. var Position = modules[ 'document/position' ];
  167. var Range = modules[ 'document/range' ];
  168. var Character = modules[ 'document/character' ];
  169. var Attribute = modules[ 'document/attribute' ];
  170. var doc = new Document();
  171. var fooAttr = new Attribute( 'foo', true );
  172. var xAttr = new Attribute( 'x', true );
  173. var barAttr = new Attribute( 'bar', true );
  174. doc.root.insertChildren( 0, new Character( 'x', [ fooAttr, xAttr, barAttr ] ) );
  175. doc.applyOperation( new ChangeOperation(
  176. new Range( new Position( [ 0 ], doc.root ), new Position( [ 1 ], doc.root ) ),
  177. xAttr,
  178. null,
  179. doc.version ) );
  180. expect( doc.version ).to.be.equal( 1 );
  181. expect( doc.root.getChildCount() ).to.be.equal( 1 );
  182. expect( doc.root.getChild( 0 ).getAttrCount() ).to.be.equal( 2 );
  183. expect( doc.root.getChild( 0 ).hasAttr( fooAttr ) ).to.be.true;
  184. expect( doc.root.getChild( 0 ).hasAttr( barAttr ) ).to.be.true;
  185. } );
  186. it( 'should remove attributes on multiple ranges', function() {
  187. var Document = modules[ 'document/document' ];
  188. var ChangeOperation = modules[ 'document/changeoperation' ];
  189. var Position = modules[ 'document/position' ];
  190. var Range = modules[ 'document/range' ];
  191. var Text = modules[ 'document/text' ];
  192. var Attribute = modules[ 'document/attribute' ];
  193. var doc = new Document();
  194. var fooAttr = new Attribute( 'foo', true );
  195. doc.root.insertChildren( 0, new Text( 'bar', [ fooAttr ] ) );
  196. doc.applyOperation( new ChangeOperation(
  197. [
  198. new Range( new Position( [ 0 ], doc.root ), new Position( [ 1 ], doc.root ) ),
  199. new Range( new Position( [ 2 ], doc.root ), new Position( [ 3 ], doc.root ) )
  200. ],
  201. fooAttr,
  202. null,
  203. doc.version ) );
  204. expect( doc.version ).to.be.equal( 1 );
  205. expect( doc.root.getChildCount() ).to.be.equal( 3 );
  206. expect( doc.root.getChild( 0 ).getAttrCount() ).to.be.equal( 0 );
  207. expect( doc.root.getChild( 1 ).hasAttr( fooAttr ) ).to.be.true;
  208. expect( doc.root.getChild( 2 ).getAttrCount() ).to.be.equal( 0 );
  209. } );
  210. it( 'should create a change operation as a reverse', function() {
  211. var Document = modules[ 'document/document' ];
  212. var ChangeOperation = modules[ 'document/changeoperation' ];
  213. var Position = modules[ 'document/position' ];
  214. var Range = modules[ 'document/range' ];
  215. var Attribute = modules[ 'document/attribute' ];
  216. var doc = new Document();
  217. var oldAttr = new Attribute( 'x', 'old' );
  218. var newAttr = new Attribute( 'x', 'new' );
  219. var ranges = [ new Range( new Position( [ 0 ], doc.root ), new Position( [ 3 ], doc.root ) ) ];
  220. var oppertaion = new ChangeOperation( ranges, oldAttr, newAttr, doc.version );
  221. var reverse = oppertaion.reverseOperation();
  222. expect( reverse ).to.be.an.instanceof( ChangeOperation );
  223. expect( reverse.baseVersion ).to.be.equals( 1 );
  224. expect( reverse.ranges ).to.be.equals( ranges );
  225. expect( reverse.oldAttr ).to.be.equals( newAttr );
  226. expect( reverse.newAttr ).to.be.equals( oldAttr );
  227. } );
  228. it( 'should undo insert attribute by applying reverse operation', function() {
  229. var Document = modules[ 'document/document' ];
  230. var ChangeOperation = modules[ 'document/changeoperation' ];
  231. var Position = modules[ 'document/position' ];
  232. var Range = modules[ 'document/range' ];
  233. var Attribute = modules[ 'document/attribute' ];
  234. var doc = new Document();
  235. var newAttr = new Attribute( 'isNew', true );
  236. doc.root.insertChildren( 0, 'bar' );
  237. var oppertaion = new ChangeOperation(
  238. new Range( new Position( [ 0 ], doc.root ), new Position( [ 3 ], doc.root ) ),
  239. null,
  240. newAttr,
  241. doc.version );
  242. var reverse = oppertaion.reverseOperation();
  243. doc.applyOperation( oppertaion );
  244. doc.applyOperation( reverse );
  245. expect( doc.version ).to.be.equal( 2 );
  246. expect( doc.root.getChildCount() ).to.be.equal( 3 );
  247. expect( doc.root.getChild( 0 ).getAttrCount() ).to.be.equal( 0 );
  248. expect( doc.root.getChild( 1 ).getAttrCount() ).to.be.equal( 0 );
  249. expect( doc.root.getChild( 2 ).getAttrCount() ).to.be.equal( 0 );
  250. } );
  251. it( 'should undo change attribute by applying reverse operation', function() {
  252. var Document = modules[ 'document/document' ];
  253. var ChangeOperation = modules[ 'document/changeoperation' ];
  254. var Position = modules[ 'document/position' ];
  255. var Range = modules[ 'document/range' ];
  256. var Text = modules[ 'document/text' ];
  257. var Attribute = modules[ 'document/attribute' ];
  258. var doc = new Document();
  259. var oldAttr = new Attribute( 'isNew', false );
  260. var newAttr = new Attribute( 'isNew', true );
  261. doc.root.insertChildren( 0, new Text( 'bar', [ oldAttr ] ) );
  262. var oppertaion = new ChangeOperation(
  263. new Range( new Position( [ 0 ], doc.root ), new Position( [ 3 ], doc.root ) ),
  264. oldAttr,
  265. newAttr,
  266. doc.version );
  267. var reverse = oppertaion.reverseOperation();
  268. doc.applyOperation( oppertaion );
  269. doc.applyOperation( reverse );
  270. expect( doc.version ).to.be.equal( 2 );
  271. expect( doc.root.getChildCount() ).to.be.equal( 3 );
  272. expect( doc.root.getChild( 0 ).getAttrCount() ).to.be.equal( 1 );
  273. expect( doc.root.getChild( 0 ).hasAttr( oldAttr ) ).to.be.true;
  274. expect( doc.root.getChild( 1 ).getAttrCount() ).to.be.equal( 1 );
  275. expect( doc.root.getChild( 1 ).hasAttr( oldAttr ) ).to.be.true;
  276. expect( doc.root.getChild( 2 ).getAttrCount() ).to.be.equal( 1 );
  277. expect( doc.root.getChild( 2 ).hasAttr( oldAttr ) ).to.be.true;
  278. } );
  279. it( 'should undo remove attribute by applying reverse operation', function() {
  280. var Document = modules[ 'document/document' ];
  281. var ChangeOperation = modules[ 'document/changeoperation' ];
  282. var Position = modules[ 'document/position' ];
  283. var Range = modules[ 'document/range' ];
  284. var Text = modules[ 'document/text' ];
  285. var Attribute = modules[ 'document/attribute' ];
  286. var doc = new Document();
  287. var fooAttr = new Attribute( 'foo', false );
  288. doc.root.insertChildren( 0, new Text( 'bar', [ fooAttr ] ) );
  289. var oppertaion = new ChangeOperation(
  290. new Range( new Position( [ 0 ], doc.root ), new Position( [ 3 ], doc.root ) ),
  291. fooAttr,
  292. null,
  293. doc.version );
  294. var reverse = oppertaion.reverseOperation();
  295. doc.applyOperation( oppertaion );
  296. doc.applyOperation( reverse );
  297. expect( doc.version ).to.be.equal( 2 );
  298. expect( doc.root.getChildCount() ).to.be.equal( 3 );
  299. expect( doc.root.getChild( 0 ).getAttrCount() ).to.be.equal( 1 );
  300. expect( doc.root.getChild( 0 ).hasAttr( fooAttr ) ).to.be.true;
  301. expect( doc.root.getChild( 1 ).getAttrCount() ).to.be.equal( 1 );
  302. expect( doc.root.getChild( 1 ).hasAttr( fooAttr ) ).to.be.true;
  303. expect( doc.root.getChild( 2 ).getAttrCount() ).to.be.equal( 1 );
  304. expect( doc.root.getChild( 2 ).hasAttr( fooAttr ) ).to.be.true;
  305. } );
  306. it( 'should throw an error when one try to remove and the attribute does not exists', function() {
  307. var Document = modules[ 'document/document' ];
  308. var ChangeOperation = modules[ 'document/changeoperation' ];
  309. var Position = modules[ 'document/position' ];
  310. var Range = modules[ 'document/range' ];
  311. var Attribute = modules[ 'document/attribute' ];
  312. var CKEditorError = modules.ckeditorerror;
  313. var doc = new Document();
  314. var fooAttr = new Attribute( 'foo', true );
  315. doc.root.insertChildren( 0, 'x' );
  316. expect( function() {
  317. doc.applyOperation( new ChangeOperation(
  318. new Range( new Position( [ 0 ], doc.root ), new Position( [ 1 ], doc.root ) ),
  319. fooAttr,
  320. null,
  321. doc.version ) );
  322. } ).to.throw( CKEditorError, /operation-change-no-attr-to-remove/ );
  323. } );
  324. it( 'should throw an error when one try to insert and the attribute already exists', function() {
  325. var Document = modules[ 'document/document' ];
  326. var ChangeOperation = modules[ 'document/changeoperation' ];
  327. var Position = modules[ 'document/position' ];
  328. var Range = modules[ 'document/range' ];
  329. var Character = modules[ 'document/character' ];
  330. var Attribute = modules[ 'document/attribute' ];
  331. var CKEditorError = modules.ckeditorerror;
  332. var doc = new Document();
  333. var x1Attr = new Attribute( 'x', 1 );
  334. var x2Attr = new Attribute( 'x', 2 );
  335. doc.root.insertChildren( 0, new Character( 'x', [ x1Attr ] ) );
  336. expect( function() {
  337. doc.applyOperation( new ChangeOperation(
  338. new Range( new Position( [ 0 ], doc.root ), new Position( [ 1 ], doc.root ) ),
  339. null,
  340. x2Attr,
  341. doc.version ) );
  342. } ).to.throw( CKEditorError, /operation-change-attr-exists/ );
  343. } );
  344. it( 'should throw an error when one try to change and the new and old attributes have different keys', function() {
  345. var Document = modules[ 'document/document' ];
  346. var ChangeOperation = modules[ 'document/changeoperation' ];
  347. var Position = modules[ 'document/position' ];
  348. var Range = modules[ 'document/range' ];
  349. var Attribute = modules[ 'document/attribute' ];
  350. var CKEditorError = modules.ckeditorerror;
  351. var doc = new Document();
  352. var fooAttr = new Attribute( 'foo', true );
  353. var barAttr = new Attribute( 'bar', true );
  354. doc.root.insertChildren( 0, 'x' );
  355. expect( function() {
  356. doc.applyOperation( new ChangeOperation(
  357. new Range( new Position( [ 0 ], doc.root ), new Position( [ 1 ], doc.root ) ),
  358. fooAttr,
  359. barAttr,
  360. doc.version ) );
  361. } ).to.throw( CKEditorError, /operation-change-different-keys/ );
  362. } );
  363. it( 'should throw an error when one try to change and the old attribute does not exists', function() {
  364. var Document = modules[ 'document/document' ];
  365. var ChangeOperation = modules[ 'document/changeoperation' ];
  366. var Position = modules[ 'document/position' ];
  367. var Range = modules[ 'document/range' ];
  368. var Attribute = modules[ 'document/attribute' ];
  369. var CKEditorError = modules.ckeditorerror;
  370. var doc = new Document();
  371. var x1Attr = new Attribute( 'x', 1 );
  372. var x2Attr = new Attribute( 'x', 2 );
  373. doc.root.insertChildren( 0, 'x' );
  374. expect( function() {
  375. doc.applyOperation( new ChangeOperation(
  376. new Range( new Position( [ 0 ], doc.root ), new Position( [ 1 ], doc.root ) ),
  377. x1Attr,
  378. x2Attr,
  379. doc.version ) );
  380. } ).to.throw( CKEditorError, /operation-change-no-attr-to-change/ );
  381. } );
  382. } );