restrictededitingnavigationcommand.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  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 ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
  6. import { setData as setModelData, getData as getModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  7. import RestrictedEditingNavigationCommand from '../src/restrictededitingnavigationcommand';
  8. describe( 'RestrictedEditingNavigationCommand', () => {
  9. let editor, forwardCommand, backwardCommand, model;
  10. beforeEach( () => {
  11. return ModelTestEditor
  12. .create()
  13. .then( newEditor => {
  14. editor = newEditor;
  15. model = editor.model;
  16. forwardCommand = new RestrictedEditingNavigationCommand( editor, 'forward' );
  17. backwardCommand = new RestrictedEditingNavigationCommand( editor, 'backward' );
  18. model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );
  19. editor.model.schema.extend( '$text', { allowAttributes: [ 'restrictedEditingException' ] } );
  20. } );
  21. } );
  22. afterEach( () => {
  23. forwardCommand.destroy();
  24. backwardCommand.destroy();
  25. return editor.destroy();
  26. } );
  27. describe( 'forward command', () => {
  28. describe( 'isEnabled', () => {
  29. describe( 'collapsed selection', () => {
  30. it( 'should be true when there is a marker after the selection position', () => {
  31. setModelData( model, '<paragraph>[]foo bar baz</paragraph>' );
  32. const paragraph = model.document.getRoot().getChild( 0 );
  33. // <paragraph>[]foo <marker≥bar</marker> baz</paragraph>
  34. model.change( writer => {
  35. writer.addMarker( 'restricted-editing-exception:1', {
  36. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  37. usingOperation: true,
  38. affectsData: true
  39. } );
  40. } );
  41. expect( forwardCommand.isEnabled ).to.be.true;
  42. } );
  43. it( 'should be false when there is no marker after the selection position', () => {
  44. setModelData( model, '<paragraph>foo bar baz[]</paragraph>' );
  45. const paragraph = model.document.getRoot().getChild( 0 );
  46. // <paragraph>foo <marker≥bar</marker> baz[]</paragraph>
  47. model.change( writer => {
  48. writer.addMarker( 'restricted-editing-exception:1', {
  49. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  50. usingOperation: true,
  51. affectsData: true
  52. } );
  53. } );
  54. expect( forwardCommand.isEnabled ).to.be.false;
  55. } );
  56. it( 'should be false when the selection position is at a marker start and there is no more markers', () => {
  57. setModelData( model, '<paragraph>foo []bar baz</paragraph>' );
  58. const paragraph = model.document.getRoot().getChild( 0 );
  59. // <paragraph>foo []<marker≥bar</marker> baz</paragraph>
  60. model.change( writer => {
  61. writer.addMarker( 'restricted-editing-exception:1', {
  62. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  63. usingOperation: true,
  64. affectsData: true
  65. } );
  66. } );
  67. expect( forwardCommand.isEnabled ).to.be.false;
  68. } );
  69. it( 'should be false when the selection position is in a marker and there is no more markers', () => {
  70. setModelData( model, '<paragraph>foo b[]ar baz</paragraph>' );
  71. const paragraph = model.document.getRoot().getChild( 0 );
  72. // <paragraph>foo <marker≥b[]ar</marker> baz</paragraph>
  73. model.change( writer => {
  74. writer.addMarker( 'restricted-editing-exception:1', {
  75. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  76. usingOperation: true,
  77. affectsData: true
  78. } );
  79. } );
  80. expect( forwardCommand.isEnabled ).to.be.false;
  81. } );
  82. it( 'should be false when the selection position is at a marker end and there is no more markers', () => {
  83. setModelData( model, '<paragraph>foo bar[] baz</paragraph>' );
  84. const paragraph = model.document.getRoot().getChild( 0 );
  85. // <paragraph>foo <marker≥bar</marker>[] baz</paragraph>
  86. model.change( writer => {
  87. writer.addMarker( 'restricted-editing-exception:1', {
  88. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  89. usingOperation: true,
  90. affectsData: true
  91. } );
  92. } );
  93. expect( forwardCommand.isEnabled ).to.be.false;
  94. } );
  95. } );
  96. describe( 'expanded selection', () => {
  97. it( 'should be true when there is a marker after the first selection position', () => {
  98. setModelData( model, '<paragraph>[fo]o bar baz</paragraph>' );
  99. const paragraph = model.document.getRoot().getChild( 0 );
  100. // <paragraph>[fo]o <marker≥bar</marker> baz</paragraph>
  101. model.change( writer => {
  102. writer.addMarker( 'restricted-editing-exception:1', {
  103. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  104. usingOperation: true,
  105. affectsData: true
  106. } );
  107. } );
  108. expect( forwardCommand.isEnabled ).to.be.true;
  109. } );
  110. it( 'should be true when the selection overlaps the marker but the start position is before it', () => {
  111. setModelData( model, '<paragraph>[foo ba]r baz</paragraph>' );
  112. const paragraph = model.document.getRoot().getChild( 0 );
  113. // <paragraph>[foo <marker≥ba]r</marker> baz</paragraph>
  114. model.change( writer => {
  115. writer.addMarker( 'restricted-editing-exception:1', {
  116. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  117. usingOperation: true,
  118. affectsData: true
  119. } );
  120. } );
  121. expect( forwardCommand.isEnabled ).to.be.true;
  122. } );
  123. it( 'should be false when the selection overlaps the marker but the start position is after it', () => {
  124. setModelData( model, '<paragraph>foo ba[r baz]</paragraph>' );
  125. const paragraph = model.document.getRoot().getChild( 0 );
  126. // <paragraph>foo <marker≥ba[r</marker> baz]</paragraph>
  127. model.change( writer => {
  128. writer.addMarker( 'restricted-editing-exception:1', {
  129. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  130. usingOperation: true,
  131. affectsData: true
  132. } );
  133. } );
  134. expect( forwardCommand.isEnabled ).to.be.false;
  135. } );
  136. } );
  137. } );
  138. describe( 'execute()', () => {
  139. describe( 'collapsed selection', () => {
  140. it( 'should move to the next marker', () => {
  141. setModelData( model, '<paragraph>[]foo bar baz</paragraph>' );
  142. const paragraph = model.document.getRoot().getChild( 0 );
  143. // <paragraph>[]foo <marker≥bar</marker> baz</paragraph>
  144. model.change( writer => {
  145. writer.addMarker( 'restricted-editing-exception:1', {
  146. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  147. usingOperation: true,
  148. affectsData: true
  149. } );
  150. } );
  151. // <paragraph>[]foo <marker≥bar</marker> <marker≥baz</marker≥</paragraph>
  152. model.change( writer => {
  153. writer.addMarker( 'restricted-editing-exception:2', {
  154. range: writer.createRange( writer.createPositionAt( paragraph, 8 ), writer.createPositionAt( paragraph, 11 ) ),
  155. usingOperation: true,
  156. affectsData: true
  157. } );
  158. } );
  159. forwardCommand.execute();
  160. expect( getModelData( model ) ).to.equal( '<paragraph>foo []bar baz</paragraph>' );
  161. forwardCommand.execute();
  162. expect( getModelData( model ) ).to.equal( '<paragraph>foo bar []baz</paragraph>' );
  163. } );
  164. } );
  165. describe( 'expanded selection', () => {
  166. it( 'should move to the next marker when the selection end overlaps the marker', () => {
  167. setModelData( model, '<paragraph>[foo b]ar baz</paragraph>' );
  168. const paragraph = model.document.getRoot().getChild( 0 );
  169. // <paragraph>[foo <marker≥b]ar</marker> baz</paragraph>
  170. model.change( writer => {
  171. writer.addMarker( 'restricted-editing-exception:1', {
  172. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  173. usingOperation: true,
  174. affectsData: true
  175. } );
  176. } );
  177. // <paragraph>[foo <marker≥b]ar</marker> <marker≥baz</marker≥</paragraph>
  178. model.change( writer => {
  179. writer.addMarker( 'restricted-editing-exception:2', {
  180. range: writer.createRange( writer.createPositionAt( paragraph, 8 ), writer.createPositionAt( paragraph, 11 ) ),
  181. usingOperation: true,
  182. affectsData: true
  183. } );
  184. } );
  185. forwardCommand.execute();
  186. expect( getModelData( model ) ).to.equal( '<paragraph>foo []bar baz</paragraph>' );
  187. forwardCommand.execute();
  188. expect( getModelData( model ) ).to.equal( '<paragraph>foo bar []baz</paragraph>' );
  189. } );
  190. it( 'should move to the next marker when the selection start overlaps the marker', () => {
  191. setModelData( model, '<paragraph>foo b[ar b]az</paragraph>' );
  192. const paragraph = model.document.getRoot().getChild( 0 );
  193. // <paragraph>foo <marker≥b[ar</marker> b]az</paragraph>
  194. model.change( writer => {
  195. writer.addMarker( 'restricted-editing-exception:1', {
  196. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  197. usingOperation: true,
  198. affectsData: true
  199. } );
  200. } );
  201. // <paragraph>foo <marker≥b[ar</marker> <marker≥b]az</marker≥</paragraph>
  202. model.change( writer => {
  203. writer.addMarker( 'restricted-editing-exception:2', {
  204. range: writer.createRange( writer.createPositionAt( paragraph, 8 ), writer.createPositionAt( paragraph, 11 ) ),
  205. usingOperation: true,
  206. affectsData: true
  207. } );
  208. } );
  209. forwardCommand.execute();
  210. expect( getModelData( model ) ).to.equal( '<paragraph>foo bar []baz</paragraph>' );
  211. } );
  212. } );
  213. } );
  214. } );
  215. describe( 'backward command', () => {
  216. describe( 'isEnabled', () => {
  217. describe( 'collapsed selection', () => {
  218. it( 'should be true when there is a marker before the selection position', () => {
  219. setModelData( model, '<paragraph>foo bar baz[]</paragraph>' );
  220. const paragraph = model.document.getRoot().getChild( 0 );
  221. // <paragraph>foo <marker≥bar</marker> baz</paragraph>
  222. model.change( writer => {
  223. writer.addMarker( 'restricted-editing-exception:1', {
  224. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  225. usingOperation: true,
  226. affectsData: true
  227. } );
  228. } );
  229. expect( backwardCommand.isEnabled ).to.be.true;
  230. } );
  231. it( 'should be false when there is no marker before the selection position', () => {
  232. setModelData( model, '<paragraph>[]foo bar baz</paragraph>' );
  233. const paragraph = model.document.getRoot().getChild( 0 );
  234. // <paragraph>[]foo <marker≥bar</marker> baz</paragraph>
  235. model.change( writer => {
  236. writer.addMarker( 'restricted-editing-exception:1', {
  237. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  238. usingOperation: true,
  239. affectsData: true
  240. } );
  241. } );
  242. expect( backwardCommand.isEnabled ).to.be.false;
  243. } );
  244. it( 'should be false when the selection position is at a marker end and there is no more markers', () => {
  245. setModelData( model, '<paragraph>foo bar[] baz</paragraph>' );
  246. const paragraph = model.document.getRoot().getChild( 0 );
  247. // <paragraph>foo <marker≥bar</marker>[] baz</paragraph>
  248. model.change( writer => {
  249. writer.addMarker( 'restricted-editing-exception:1', {
  250. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  251. usingOperation: true,
  252. affectsData: true
  253. } );
  254. } );
  255. expect( backwardCommand.isEnabled ).to.be.false;
  256. } );
  257. it( 'should be false when the selection position is in a marker and there is no more markers', () => {
  258. setModelData( model, '<paragraph>foo b[]ar baz</paragraph>' );
  259. const paragraph = model.document.getRoot().getChild( 0 );
  260. // <paragraph>foo <marker≥b[]ar</marker> baz</paragraph>
  261. model.change( writer => {
  262. writer.addMarker( 'restricted-editing-exception:1', {
  263. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  264. usingOperation: true,
  265. affectsData: true
  266. } );
  267. } );
  268. expect( backwardCommand.isEnabled ).to.be.false;
  269. } );
  270. it( 'should be false when the selection position is at a marker start and there is no more markers', () => {
  271. setModelData( model, '<paragraph>foo []bar baz</paragraph>' );
  272. const paragraph = model.document.getRoot().getChild( 0 );
  273. // <paragraph>foo []<marker≥bar</marker> baz</paragraph>
  274. model.change( writer => {
  275. writer.addMarker( 'restricted-editing-exception:1', {
  276. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  277. usingOperation: true,
  278. affectsData: true
  279. } );
  280. } );
  281. expect( backwardCommand.isEnabled ).to.be.false;
  282. } );
  283. } );
  284. describe( 'expanded selection', () => {
  285. it( 'should be true when there is a marker before the first selection position', () => {
  286. setModelData( model, '<paragraph>foo bar b[az]</paragraph>' );
  287. const paragraph = model.document.getRoot().getChild( 0 );
  288. // <paragraph>[fo]o <marker≥bar</marker> b[az]</paragraph>
  289. model.change( writer => {
  290. writer.addMarker( 'restricted-editing-exception:1', {
  291. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  292. usingOperation: true,
  293. affectsData: true
  294. } );
  295. } );
  296. expect( backwardCommand.isEnabled ).to.be.true;
  297. } );
  298. it( 'should be false when the selection overlaps the marker but the start position is after it', () => {
  299. setModelData( model, '<paragraph>foo b[ar baz]</paragraph>' );
  300. const paragraph = model.document.getRoot().getChild( 0 );
  301. // <paragraph>foo <marker≥b[ar</marker> baz]</paragraph>
  302. model.change( writer => {
  303. writer.addMarker( 'restricted-editing-exception:1', {
  304. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  305. usingOperation: true,
  306. affectsData: true
  307. } );
  308. } );
  309. expect( backwardCommand.isEnabled ).to.be.false;
  310. } );
  311. it( 'should be false when the selection overlaps the marker but the after position is after it', () => {
  312. setModelData( model, '<paragraph>[foo b]ar baz</paragraph>' );
  313. const paragraph = model.document.getRoot().getChild( 0 );
  314. // <paragraph>[foo <marker≥b]ar</marker> baz</paragraph>
  315. model.change( writer => {
  316. writer.addMarker( 'restricted-editing-exception:1', {
  317. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  318. usingOperation: true,
  319. affectsData: true
  320. } );
  321. } );
  322. expect( backwardCommand.isEnabled ).to.be.false;
  323. } );
  324. } );
  325. } );
  326. describe( 'execute()', () => {
  327. describe( 'collapsed selection', () => {
  328. it( 'should move to the previous marker', () => {
  329. setModelData( model, '<paragraph>foo bar baz[]</paragraph>' );
  330. const paragraph = model.document.getRoot().getChild( 0 );
  331. // <paragraph>foo <marker≥bar</marker> baz[]</paragraph>
  332. model.change( writer => {
  333. writer.addMarker( 'restricted-editing-exception:1', {
  334. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  335. usingOperation: true,
  336. affectsData: true
  337. } );
  338. } );
  339. // <paragraph>foo <marker≥bar</marker> <marker≥baz</marker≥[]</paragraph>
  340. model.change( writer => {
  341. writer.addMarker( 'restricted-editing-exception:2', {
  342. range: writer.createRange( writer.createPositionAt( paragraph, 8 ), writer.createPositionAt( paragraph, 11 ) ),
  343. usingOperation: true,
  344. affectsData: true
  345. } );
  346. } );
  347. backwardCommand.execute();
  348. expect( getModelData( model ) ).to.equal( '<paragraph>foo []bar baz</paragraph>' );
  349. } );
  350. } );
  351. describe( 'expanded selection', () => {
  352. it( 'should move to the previous marker when the selection end overlaps the marker', () => {
  353. setModelData( model, '<paragraph>foo bar b[az]</paragraph>' );
  354. const paragraph = model.document.getRoot().getChild( 0 );
  355. // <paragraph>foo <marker≥bar</marker> b[az]</paragraph>
  356. model.change( writer => {
  357. writer.addMarker( 'restricted-editing-exception:1', {
  358. range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ),
  359. usingOperation: true,
  360. affectsData: true
  361. } );
  362. } );
  363. // <paragraph>foo <marker≥bar</marker> <marker≥b[az</marker≥]</paragraph>
  364. model.change( writer => {
  365. writer.addMarker( 'restricted-editing-exception:2', {
  366. range: writer.createRange( writer.createPositionAt( paragraph, 8 ), writer.createPositionAt( paragraph, 11 ) ),
  367. usingOperation: true,
  368. affectsData: true
  369. } );
  370. } );
  371. backwardCommand.execute();
  372. expect( getModelData( model ) ).to.equal( '<paragraph>foo []bar baz</paragraph>' );
  373. } );
  374. } );
  375. } );
  376. } );
  377. } );