restrictededitingmodeediting-commands.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
  6. import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
  7. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  8. import Typing from '@ckeditor/ckeditor5-typing/src/typing';
  9. import Command from '@ckeditor/ckeditor5-core/src/command';
  10. import { setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  11. import RestrictedEditingModeEditing from './../src/restrictededitingmodeediting';
  12. import UndoEditing from '@ckeditor/ckeditor5-undo/src/undoediting';
  13. describe( 'RestrictedEditingEditing - commands', () => {
  14. let editor;
  15. testUtils.createSinonSandbox();
  16. describe( 'commands toggling', () => {
  17. let model, firstParagraph;
  18. beforeEach( async () => {
  19. editor = await VirtualTestEditor.create( { plugins: [ Paragraph, Typing, RestrictedEditingModeEditing ] } );
  20. model = editor.model;
  21. setModelData( model, '<paragraph>[]foo bar baz</paragraph>' );
  22. firstParagraph = model.document.getRoot().getChild( 0 );
  23. model.change( writer => {
  24. writer.addMarker( 'restrictedEditingException:1', {
  25. range: writer.createRange(
  26. writer.createPositionAt( firstParagraph, 4 ),
  27. writer.createPositionAt( firstParagraph, 7 ) ),
  28. usingOperation: true,
  29. affectsData: true
  30. } );
  31. } );
  32. } );
  33. afterEach( async () => {
  34. await editor.destroy();
  35. } );
  36. describe( 'always enabled commands', () => {
  37. describe( 'undo', () => {
  38. beforeEach( () => {
  39. editor.commands.add( 'undo', buildFakeCommand( editor ) );
  40. } );
  41. it( 'should be enabled outside exception marker', () => {
  42. model.change( writer => {
  43. writer.setSelection( firstParagraph, 1 );
  44. } );
  45. expect( editor.commands.get( 'undo' ).isEnabled ).to.be.true;
  46. } );
  47. it( 'should be enabled inside exception marker', () => {
  48. model.change( writer => {
  49. writer.setSelection( firstParagraph, 5 );
  50. } );
  51. expect( editor.commands.get( 'undo' ).isEnabled ).to.be.true;
  52. } );
  53. } );
  54. describe( 'redo', () => {
  55. beforeEach( () => {
  56. editor.commands.add( 'redo', buildFakeCommand( editor ) );
  57. } );
  58. it( 'should be enabled outside exception marker', () => {
  59. model.change( writer => {
  60. writer.setSelection( firstParagraph, 1 );
  61. } );
  62. expect( editor.commands.get( 'redo' ).isEnabled ).to.be.true;
  63. } );
  64. it( 'should be enabled inside exception marker', () => {
  65. model.change( writer => {
  66. writer.setSelection( firstParagraph, 5 );
  67. } );
  68. expect( editor.commands.get( 'redo' ).isEnabled ).to.be.true;
  69. } );
  70. } );
  71. } );
  72. describe( 'commands enabled in exception marker', () => {
  73. describe( 'input', () => {
  74. beforeEach( () => {
  75. editor.commands.add( 'input', buildFakeCommand( editor ) );
  76. model.change( writer => {
  77. writer.setSelection( firstParagraph, 'end' );
  78. } );
  79. } );
  80. it( 'should be disabled when caret is outside exception marker', () => {
  81. model.change( writer => {
  82. writer.setSelection( firstParagraph, 1 );
  83. } );
  84. expect( editor.commands.get( 'input' ).isEnabled ).to.be.false;
  85. } );
  86. it( 'should be enabled when caret is inside exception marker (not touching boundaries)', () => {
  87. model.change( writer => {
  88. writer.setSelection( firstParagraph, 5 );
  89. } );
  90. expect( editor.commands.get( 'input' ).isEnabled ).to.be.true;
  91. } );
  92. it( 'should be disabled when caret is inside other marker', () => {
  93. model.change( writer => {
  94. writer.addMarker( 'foo-bar:1', {
  95. range: writer.createRange(
  96. writer.createPositionAt( firstParagraph, 0 ),
  97. writer.createPositionAt( firstParagraph, 3 ) ),
  98. usingOperation: true,
  99. affectsData: true
  100. } );
  101. writer.setSelection( firstParagraph, 1 );
  102. } );
  103. expect( editor.commands.get( 'input' ).isEnabled ).to.be.false;
  104. } );
  105. it( 'should be enabled when caret is inside exception marker (start boundary)', () => {
  106. model.change( writer => {
  107. writer.setSelection( firstParagraph, 4 );
  108. } );
  109. expect( editor.commands.get( 'input' ).isEnabled ).to.be.true;
  110. } );
  111. it( 'should be enabled when caret is inside exception marker (end boundary)', () => {
  112. model.change( writer => {
  113. writer.setSelection( firstParagraph, 7 );
  114. } );
  115. expect( editor.commands.get( 'input' ).isEnabled ).to.be.true;
  116. } );
  117. it( 'should be disabled for multi-range selection (collapsed ranges)', () => {
  118. model.change( writer => {
  119. writer.setSelection( [
  120. writer.createRange(
  121. writer.createPositionAt( firstParagraph, 5 )
  122. ),
  123. writer.createRange(
  124. writer.createPositionAt( firstParagraph, 9 )
  125. )
  126. ] );
  127. } );
  128. expect( editor.commands.get( 'input' ).isEnabled ).to.be.false;
  129. } );
  130. it( 'should be disabled for non-collapsed selection that expands over exception marker', () => {
  131. model.change( writer => {
  132. writer.setSelection( writer.createRange(
  133. writer.createPositionAt( firstParagraph, 0 ),
  134. writer.createPositionAt( firstParagraph, 5 )
  135. ) );
  136. } );
  137. expect( editor.commands.get( 'input' ).isEnabled ).to.be.false;
  138. } );
  139. it( 'should be enabled for non-collapsed selection that is fully contained inside exception marker', () => {
  140. model.change( writer => {
  141. writer.setSelection( writer.createRange(
  142. writer.createPositionAt( firstParagraph, 5 ),
  143. writer.createPositionAt( firstParagraph, 6 )
  144. ) );
  145. } );
  146. expect( editor.commands.get( 'input' ).isEnabled ).to.be.true;
  147. } );
  148. it( 'should be enabled for non-collapsed selection inside exception marker (start position on marker boundary)', () => {
  149. model.change( writer => {
  150. writer.setSelection( writer.createRange(
  151. writer.createPositionAt( firstParagraph, 4 ),
  152. writer.createPositionAt( firstParagraph, 6 )
  153. ) );
  154. } );
  155. expect( editor.commands.get( 'input' ).isEnabled ).to.be.true;
  156. } );
  157. it( 'should be enabled for non-collapsed selection inside exception marker (end position on marker boundary)', () => {
  158. model.change( writer => {
  159. writer.setSelection( writer.createRange(
  160. writer.createPositionAt( firstParagraph, 5 ),
  161. writer.createPositionAt( firstParagraph, 7 )
  162. ) );
  163. } );
  164. expect( editor.commands.get( 'input' ).isEnabled ).to.be.true;
  165. } );
  166. it( 'should be enabled for non-collapsed selection is equal to exception marker', () => {
  167. model.change( writer => {
  168. writer.setSelection( writer.createRange(
  169. writer.createPositionAt( firstParagraph, 4 ),
  170. writer.createPositionAt( firstParagraph, 7 )
  171. ) );
  172. } );
  173. expect( editor.commands.get( 'input' ).isEnabled ).to.be.true;
  174. } );
  175. it( 'should be disabled for non-collapsed selection with more then one range', () => {
  176. model.change( writer => {
  177. writer.setSelection( [
  178. writer.createRange(
  179. writer.createPositionAt( firstParagraph, 5 ),
  180. writer.createPositionAt( firstParagraph, 6 )
  181. ),
  182. writer.createRange(
  183. writer.createPositionAt( firstParagraph, 8 ),
  184. writer.createPositionAt( firstParagraph, 9 )
  185. )
  186. ] );
  187. } );
  188. expect( editor.commands.get( 'input' ).isEnabled ).to.be.false;
  189. } );
  190. } );
  191. describe( 'delete', () => {
  192. beforeEach( () => {
  193. editor.commands.add( 'delete', buildFakeCommand( editor ) );
  194. model.change( writer => {
  195. writer.setSelection( firstParagraph, 'end' );
  196. } );
  197. } );
  198. it( 'should be disabled when caret is outside exception marker', () => {
  199. model.change( writer => {
  200. writer.setSelection( firstParagraph, 1 );
  201. } );
  202. expect( editor.commands.get( 'delete' ).isEnabled ).to.be.false;
  203. } );
  204. it( 'should be enabled when caret is inside exception marker (not touching boundaries)', () => {
  205. model.change( writer => {
  206. writer.setSelection( firstParagraph, 5 );
  207. } );
  208. expect( editor.commands.get( 'delete' ).isEnabled ).to.be.true;
  209. } );
  210. it( 'should be disabled when caret is inside exception marker (start boundary)', () => {
  211. model.change( writer => {
  212. writer.setSelection( firstParagraph, 4 );
  213. } );
  214. expect( editor.commands.get( 'delete' ).isEnabled ).to.be.false;
  215. } );
  216. it( 'should be disabled when caret moves to start boundary and it was enabled previously', () => {
  217. model.change( writer => {
  218. writer.setSelection( firstParagraph, 5 );
  219. } );
  220. expect( editor.commands.get( 'delete' ).isEnabled ).to.be.true;
  221. model.change( writer => {
  222. writer.setSelection( firstParagraph, 4 );
  223. } );
  224. expect( editor.commands.get( 'delete' ).isEnabled ).to.be.false;
  225. } );
  226. it( 'should be enabled when caret is inside exception marker (end boundary)', () => {
  227. model.change( writer => {
  228. writer.setSelection( firstParagraph, 7 );
  229. } );
  230. expect( editor.commands.get( 'delete' ).isEnabled ).to.be.true;
  231. } );
  232. it( 'should be disabled for non-collapsed selection that expands over exception marker', () => {
  233. model.change( writer => {
  234. writer.setSelection( writer.createRange(
  235. writer.createPositionAt( firstParagraph, 0 ),
  236. writer.createPositionAt( firstParagraph, 5 )
  237. ) );
  238. } );
  239. expect( editor.commands.get( 'delete' ).isEnabled ).to.be.false;
  240. } );
  241. it( 'should be enabled for non-collapsed selection that is fully contained inside exception marker', () => {
  242. model.change( writer => {
  243. writer.setSelection( writer.createRange(
  244. writer.createPositionAt( firstParagraph, 5 ),
  245. writer.createPositionAt( firstParagraph, 6 )
  246. ) );
  247. } );
  248. expect( editor.commands.get( 'delete' ).isEnabled ).to.be.true;
  249. } );
  250. it( 'should be enabled for non-collapsed selection inside exception marker (start position on marker boundary)', () => {
  251. model.change( writer => {
  252. writer.setSelection( writer.createRange(
  253. writer.createPositionAt( firstParagraph, 4 ),
  254. writer.createPositionAt( firstParagraph, 6 )
  255. ) );
  256. } );
  257. expect( editor.commands.get( 'delete' ).isEnabled ).to.be.true;
  258. } );
  259. it( 'should be enabled for non-collapsed selection inside exception marker (end position on marker boundary)', () => {
  260. model.change( writer => {
  261. writer.setSelection( writer.createRange(
  262. writer.createPositionAt( firstParagraph, 5 ),
  263. writer.createPositionAt( firstParagraph, 7 )
  264. ) );
  265. } );
  266. expect( editor.commands.get( 'delete' ).isEnabled ).to.be.true;
  267. } );
  268. it( 'should be enabled for non-collapsed selection is equal to exception marker', () => {
  269. model.change( writer => {
  270. writer.setSelection( writer.createRange(
  271. writer.createPositionAt( firstParagraph, 4 ),
  272. writer.createPositionAt( firstParagraph, 7 )
  273. ) );
  274. } );
  275. expect( editor.commands.get( 'delete' ).isEnabled ).to.be.true;
  276. } );
  277. } );
  278. describe( 'forwardDelete', () => {
  279. beforeEach( () => {
  280. editor.commands.add( 'forwardDelete', buildFakeCommand( editor ) );
  281. model.change( writer => {
  282. writer.setSelection( firstParagraph, 'end' );
  283. } );
  284. } );
  285. it( 'should be disabled when caret is outside exception marker', () => {
  286. model.change( writer => {
  287. writer.setSelection( firstParagraph, 1 );
  288. } );
  289. expect( editor.commands.get( 'forwardDelete' ).isEnabled ).to.be.false;
  290. } );
  291. it( 'should be enabled when caret is inside exception marker (not touching boundaries)', () => {
  292. model.change( writer => {
  293. writer.setSelection( firstParagraph, 5 );
  294. } );
  295. expect( editor.commands.get( 'forwardDelete' ).isEnabled ).to.be.true;
  296. } );
  297. it( 'should be enabled when caret is inside exception marker (start boundary)', () => {
  298. model.change( writer => {
  299. writer.setSelection( firstParagraph, 4 );
  300. } );
  301. expect( editor.commands.get( 'forwardDelete' ).isEnabled ).to.be.true;
  302. } );
  303. it( 'should be disabled when caret is inside exception marker (end boundary)', () => {
  304. model.change( writer => {
  305. writer.setSelection( firstParagraph, 7 );
  306. } );
  307. expect( editor.commands.get( 'forwardDelete' ).isEnabled ).to.be.false;
  308. } );
  309. it( 'should be disabled when caret moves to end boundary and it was enabled previously', () => {
  310. model.change( writer => {
  311. writer.setSelection( firstParagraph, 5 );
  312. } );
  313. expect( editor.commands.get( 'forwardDelete' ).isEnabled ).to.be.true;
  314. model.change( writer => {
  315. writer.setSelection( firstParagraph, 7 );
  316. } );
  317. expect( editor.commands.get( 'forwardDelete' ).isEnabled ).to.be.false;
  318. } );
  319. it( 'should be disabled for non-collapsed selection that expands over exception marker', () => {
  320. model.change( writer => {
  321. writer.setSelection( writer.createRange(
  322. writer.createPositionAt( firstParagraph, 0 ),
  323. writer.createPositionAt( firstParagraph, 5 )
  324. ) );
  325. } );
  326. expect( editor.commands.get( 'forwardDelete' ).isEnabled ).to.be.false;
  327. } );
  328. it( 'should be enabled for non-collapsed selection that is fully contained inside exception marker', () => {
  329. model.change( writer => {
  330. writer.setSelection( writer.createRange(
  331. writer.createPositionAt( firstParagraph, 5 ),
  332. writer.createPositionAt( firstParagraph, 6 )
  333. ) );
  334. } );
  335. expect( editor.commands.get( 'forwardDelete' ).isEnabled ).to.be.true;
  336. } );
  337. it( 'should be enabled for non-collapsed selection inside exception marker (start position on marker boundary)', () => {
  338. model.change( writer => {
  339. writer.setSelection( writer.createRange(
  340. writer.createPositionAt( firstParagraph, 4 ),
  341. writer.createPositionAt( firstParagraph, 6 )
  342. ) );
  343. } );
  344. expect( editor.commands.get( 'forwardDelete' ).isEnabled ).to.be.true;
  345. } );
  346. it( 'should be enabled for non-collapsed selection inside exception marker (end position on marker boundary)', () => {
  347. model.change( writer => {
  348. writer.setSelection( writer.createRange(
  349. writer.createPositionAt( firstParagraph, 5 ),
  350. writer.createPositionAt( firstParagraph, 7 )
  351. ) );
  352. } );
  353. expect( editor.commands.get( 'forwardDelete' ).isEnabled ).to.be.true;
  354. } );
  355. it( 'should be enabled for non-collapsed selection is equal to exception marker', () => {
  356. model.change( writer => {
  357. writer.setSelection( writer.createRange(
  358. writer.createPositionAt( firstParagraph, 4 ),
  359. writer.createPositionAt( firstParagraph, 7 )
  360. ) );
  361. } );
  362. expect( editor.commands.get( 'forwardDelete' ).isEnabled ).to.be.true;
  363. } );
  364. } );
  365. } );
  366. describe( 'non-enabled commands', () => {
  367. beforeEach( () => {
  368. editor.commands.add( 'other', buildFakeCommand( editor ) );
  369. model.change( writer => {
  370. writer.setSelection( firstParagraph, 'end' );
  371. } );
  372. } );
  373. it( 'should be disabled outside exception marker', () => {
  374. model.change( writer => {
  375. writer.setSelection( firstParagraph, 1 );
  376. } );
  377. expect( editor.commands.get( 'other' ).isEnabled ).to.be.false;
  378. } );
  379. it( 'should be disabled inside exception marker', () => {
  380. model.change( writer => {
  381. writer.setSelection( firstParagraph, 1 );
  382. } );
  383. model.change( writer => {
  384. writer.setSelection( firstParagraph, 5 );
  385. } );
  386. expect( editor.commands.get( 'other' ).isEnabled ).to.be.false;
  387. } );
  388. it( 'should be disabled when caret is inside exception marker (not touching boundaries)', () => {
  389. model.change( writer => {
  390. writer.setSelection( firstParagraph, 5 );
  391. } );
  392. expect( editor.commands.get( 'other' ).isEnabled ).to.be.false;
  393. } );
  394. it( 'should be disabled when caret is inside exception marker (start boundary)', () => {
  395. model.change( writer => {
  396. writer.setSelection( firstParagraph, 4 );
  397. } );
  398. expect( editor.commands.get( 'other' ).isEnabled ).to.be.false;
  399. } );
  400. it( 'should be disabled when caret is inside exception marker (end boundary)', () => {
  401. model.change( writer => {
  402. writer.setSelection( firstParagraph, 7 );
  403. } );
  404. expect( editor.commands.get( 'other' ).isEnabled ).to.be.false;
  405. } );
  406. it( 'should be disabled for non-collapsed selection that expands over exception marker', () => {
  407. model.change( writer => {
  408. writer.setSelection( writer.createRange(
  409. writer.createPositionAt( firstParagraph, 0 ),
  410. writer.createPositionAt( firstParagraph, 5 )
  411. ) );
  412. } );
  413. expect( editor.commands.get( 'other' ).isEnabled ).to.be.false;
  414. } );
  415. it( 'should be disabled for non-collapsed selection that is fully contained inside exception marker', () => {
  416. model.change( writer => {
  417. writer.setSelection( writer.createRange(
  418. writer.createPositionAt( firstParagraph, 5 ),
  419. writer.createPositionAt( firstParagraph, 6 )
  420. ) );
  421. } );
  422. expect( editor.commands.get( 'other' ).isEnabled ).to.be.false;
  423. } );
  424. it( 'should be disabled for non-collapsed selection inside exception marker (start position on marker boundary)', () => {
  425. model.change( writer => {
  426. writer.setSelection( writer.createRange(
  427. writer.createPositionAt( firstParagraph, 4 ),
  428. writer.createPositionAt( firstParagraph, 6 )
  429. ) );
  430. } );
  431. expect( editor.commands.get( 'other' ).isEnabled ).to.be.false;
  432. } );
  433. it( 'should be disabled for non-collapsed selection inside exception marker (end position on marker boundary)', () => {
  434. model.change( writer => {
  435. writer.setSelection( writer.createRange(
  436. writer.createPositionAt( firstParagraph, 5 ),
  437. writer.createPositionAt( firstParagraph, 7 )
  438. ) );
  439. } );
  440. expect( editor.commands.get( 'other' ).isEnabled ).to.be.false;
  441. } );
  442. it( 'should be disabled for non-collapsed selection is equal to exception marker', () => {
  443. model.change( writer => {
  444. writer.setSelection( writer.createRange(
  445. writer.createPositionAt( firstParagraph, 4 ),
  446. writer.createPositionAt( firstParagraph, 7 )
  447. ) );
  448. } );
  449. expect( editor.commands.get( 'other' ).isEnabled ).to.be.false;
  450. } );
  451. } );
  452. } );
  453. describe( 'commands enabled in exception marker by configuration', () => {
  454. let model, firstParagraph;
  455. beforeEach( async () => {
  456. editor = await VirtualTestEditor.create( {
  457. plugins: [ Paragraph, Typing, RestrictedEditingModeEditing ],
  458. restrictedEditing: {
  459. allowedCommands: [ 'allowed' ]
  460. }
  461. } );
  462. model = editor.model;
  463. editor.commands.add( 'allowed', buildFakeCommand( editor ) );
  464. setModelData( model, '<paragraph>[]foo bar baz</paragraph>' );
  465. firstParagraph = model.document.getRoot().getChild( 0 );
  466. model.change( writer => {
  467. writer.addMarker( 'restrictedEditingException:1', {
  468. range: writer.createRange(
  469. writer.createPositionAt( firstParagraph, 4 ),
  470. writer.createPositionAt( firstParagraph, 7 ) ),
  471. usingOperation: true,
  472. affectsData: true
  473. } );
  474. writer.setSelection( firstParagraph, 'end' );
  475. } );
  476. } );
  477. afterEach( async () => {
  478. await editor.destroy();
  479. } );
  480. it( 'should be disabled when caret is outside exception marker', () => {
  481. model.change( writer => {
  482. writer.setSelection( firstParagraph, 1 );
  483. } );
  484. expect( editor.commands.get( 'allowed' ).isEnabled ).to.be.false;
  485. } );
  486. it( 'should be enabled when caret is inside exception marker (not touching boundaries)', () => {
  487. model.change( writer => {
  488. writer.setSelection( firstParagraph, 5 );
  489. } );
  490. expect( editor.commands.get( 'allowed' ).isEnabled ).to.be.true;
  491. } );
  492. it( 'should be disabled when caret is inside other marker', () => {
  493. model.change( writer => {
  494. writer.addMarker( 'foo-bar:1', {
  495. range: writer.createRange(
  496. writer.createPositionAt( firstParagraph, 0 ),
  497. writer.createPositionAt( firstParagraph, 3 ) ),
  498. usingOperation: true,
  499. affectsData: true
  500. } );
  501. writer.setSelection( firstParagraph, 1 );
  502. } );
  503. expect( editor.commands.get( 'allowed' ).isEnabled ).to.be.false;
  504. } );
  505. it( 'should be enabled when caret is inside exception marker (start boundary)', () => {
  506. model.change( writer => {
  507. writer.setSelection( firstParagraph, 4 );
  508. } );
  509. expect( editor.commands.get( 'allowed' ).isEnabled ).to.be.true;
  510. } );
  511. it( 'should be enabled when caret is inside exception marker (end boundary)', () => {
  512. model.change( writer => {
  513. writer.setSelection( firstParagraph, 7 );
  514. } );
  515. expect( editor.commands.get( 'allowed' ).isEnabled ).to.be.true;
  516. } );
  517. it( 'should be disabled for multi-range selection (collapsed ranges)', () => {
  518. model.change( writer => {
  519. writer.setSelection( [
  520. writer.createRange(
  521. writer.createPositionAt( firstParagraph, 5 )
  522. ),
  523. writer.createRange(
  524. writer.createPositionAt( firstParagraph, 9 )
  525. )
  526. ] );
  527. } );
  528. expect( editor.commands.get( 'allowed' ).isEnabled ).to.be.false;
  529. } );
  530. it( 'should be disabled for non-collapsed selection that expands over exception marker', () => {
  531. model.change( writer => {
  532. writer.setSelection( writer.createRange(
  533. writer.createPositionAt( firstParagraph, 0 ),
  534. writer.createPositionAt( firstParagraph, 5 )
  535. ) );
  536. } );
  537. expect( editor.commands.get( 'allowed' ).isEnabled ).to.be.false;
  538. } );
  539. it( 'should be enabled for non-collapsed selection that is fully contained inside exception marker', () => {
  540. model.change( writer => {
  541. writer.setSelection( writer.createRange(
  542. writer.createPositionAt( firstParagraph, 5 ),
  543. writer.createPositionAt( firstParagraph, 6 )
  544. ) );
  545. } );
  546. expect( editor.commands.get( 'allowed' ).isEnabled ).to.be.true;
  547. } );
  548. it( 'should be enabled for non-collapsed selection inside exception marker (start position on marker boundary)', () => {
  549. model.change( writer => {
  550. writer.setSelection( writer.createRange(
  551. writer.createPositionAt( firstParagraph, 4 ),
  552. writer.createPositionAt( firstParagraph, 6 )
  553. ) );
  554. } );
  555. expect( editor.commands.get( 'allowed' ).isEnabled ).to.be.true;
  556. } );
  557. it( 'should be enabled for non-collapsed selection inside exception marker (end position on marker boundary)', () => {
  558. model.change( writer => {
  559. writer.setSelection( writer.createRange(
  560. writer.createPositionAt( firstParagraph, 5 ),
  561. writer.createPositionAt( firstParagraph, 7 )
  562. ) );
  563. } );
  564. expect( editor.commands.get( 'allowed' ).isEnabled ).to.be.true;
  565. } );
  566. it( 'should be enabled for non-collapsed selection is equal to exception marker', () => {
  567. model.change( writer => {
  568. writer.setSelection( writer.createRange(
  569. writer.createPositionAt( firstParagraph, 4 ),
  570. writer.createPositionAt( firstParagraph, 7 )
  571. ) );
  572. } );
  573. expect( editor.commands.get( 'allowed' ).isEnabled ).to.be.true;
  574. } );
  575. it( 'should be disabled for non-collapsed selection with more then one range', () => {
  576. model.change( writer => {
  577. writer.setSelection( [
  578. writer.createRange(
  579. writer.createPositionAt( firstParagraph, 5 ),
  580. writer.createPositionAt( firstParagraph, 6 )
  581. ),
  582. writer.createRange(
  583. writer.createPositionAt( firstParagraph, 8 ),
  584. writer.createPositionAt( firstParagraph, 9 )
  585. )
  586. ] );
  587. } );
  588. expect( editor.commands.get( 'allowed' ).isEnabled ).to.be.false;
  589. } );
  590. } );
  591. describe( 'integration', () => {
  592. let model, firstParagraph;
  593. beforeEach( async () => {
  594. editor = await VirtualTestEditor.create( { plugins: [ Paragraph, Typing, UndoEditing, RestrictedEditingModeEditing ] } );
  595. model = editor.model;
  596. setModelData( model, '<paragraph>[]foo bar baz</paragraph>' );
  597. firstParagraph = model.document.getRoot().getChild( 0 );
  598. model.change( writer => {
  599. writer.addMarker( 'restrictedEditingException:1', {
  600. range: writer.createRange(
  601. writer.createPositionAt( firstParagraph, 4 ),
  602. writer.createPositionAt( firstParagraph, 7 ) ),
  603. usingOperation: true,
  604. affectsData: true
  605. } );
  606. } );
  607. } );
  608. afterEach( async () => {
  609. await editor.destroy();
  610. } );
  611. describe( 'delete + undo', () => {
  612. it( 'should be enabled after data change (no selection change event on undo)', () => {
  613. model.change( writer => {
  614. writer.setSelection( firstParagraph, 7 );
  615. } );
  616. editor.execute( 'delete' );
  617. editor.execute( 'undo' );
  618. expect( editor.commands.get( 'delete' ).isEnabled ).to.be.true;
  619. } );
  620. } );
  621. } );
  622. class FakeCommand extends Command {
  623. refresh() {
  624. this.isEnabled = true;
  625. }
  626. }
  627. function buildFakeCommand( editor ) {
  628. return new FakeCommand( editor );
  629. }
  630. } );