input.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
  6. import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
  7. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  8. import Bold from '@ckeditor/ckeditor5-basic-styles/src/boldengine';
  9. import Input from '../src/input';
  10. import Writer from '@ckeditor/ckeditor5-engine/src/model/writer';
  11. import ModelRange from '@ckeditor/ckeditor5-engine/src/model/range';
  12. import { downcastElementToElement } from '@ckeditor/ckeditor5-engine/src/conversion/downcast-converters';
  13. import { upcastElementToElement } from '@ckeditor/ckeditor5-engine/src/conversion/upcast-converters';
  14. import ViewText from '@ckeditor/ckeditor5-engine/src/view/text';
  15. import ViewElement from '@ckeditor/ckeditor5-engine/src/view/element';
  16. import ViewSelection from '@ckeditor/ckeditor5-engine/src/view/selection';
  17. import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
  18. import { getCode } from '@ckeditor/ckeditor5-utils/src/keyboard';
  19. import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  20. import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
  21. describe( 'Input feature', () => {
  22. let editor, model, modelRoot, view, viewDocument, viewRoot, listenter;
  23. testUtils.createSinonSandbox();
  24. before( () => {
  25. listenter = Object.create( EmitterMixin );
  26. return VirtualTestEditor
  27. .create( {
  28. plugins: [ Input, Paragraph, Bold ]
  29. } )
  30. .then( newEditor => {
  31. // Mock image feature.
  32. newEditor.model.schema.register( 'image', { allowWhere: '$text' } );
  33. newEditor.conversion.for( 'downcast' ).add( downcastElementToElement( {
  34. model: 'image',
  35. view: 'img'
  36. } ) );
  37. newEditor.conversion.for( 'upcast' ).add( upcastElementToElement( {
  38. view: 'img',
  39. model: 'image'
  40. } ) );
  41. editor = newEditor;
  42. model = editor.model;
  43. modelRoot = model.document.getRoot();
  44. view = editor.editing.view;
  45. viewDocument = view.document;
  46. viewRoot = viewDocument.getRoot();
  47. } );
  48. } );
  49. after( () => {
  50. return editor.destroy();
  51. } );
  52. beforeEach( () => {
  53. editor.setData( '<p>foobar</p>' );
  54. model.change( writer => {
  55. writer.setSelection(
  56. ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 0 ), 3, modelRoot.getChild( 0 ), 3 )
  57. );
  58. } );
  59. } );
  60. afterEach( () => {
  61. listenter.stopListening();
  62. } );
  63. describe( 'mutations handling', () => {
  64. it( 'should handle text mutation', () => {
  65. viewDocument.fire( 'mutations', [
  66. {
  67. type: 'text',
  68. oldText: 'foobar',
  69. newText: 'fooxbar',
  70. node: viewRoot.getChild( 0 ).getChild( 0 )
  71. }
  72. ] );
  73. expect( getModelData( model ) ).to.equal( '<paragraph>foox[]bar</paragraph>' );
  74. expect( getViewData( view ) ).to.equal( '<p>foox{}bar</p>' );
  75. } );
  76. it( 'should handle text mutation change', () => {
  77. viewDocument.fire( 'mutations', [
  78. {
  79. type: 'text',
  80. oldText: 'foobar',
  81. newText: 'foodar',
  82. node: viewRoot.getChild( 0 ).getChild( 0 )
  83. }
  84. ] );
  85. expect( getModelData( model ) ).to.equal( '<paragraph>food[]ar</paragraph>' );
  86. expect( getViewData( view ) ).to.equal( '<p>food{}ar</p>' );
  87. } );
  88. it( 'should handle text node insertion', () => {
  89. editor.setData( '<p></p>' );
  90. viewDocument.fire( 'mutations', [
  91. {
  92. type: 'children',
  93. oldChildren: [],
  94. newChildren: [ new ViewText( 'x' ) ],
  95. node: viewRoot.getChild( 0 )
  96. }
  97. ] );
  98. expect( getModelData( model ) ).to.equal( '<paragraph>x[]</paragraph>' );
  99. expect( getViewData( view ) ).to.equal( '<p>x{}</p>' );
  100. } );
  101. it( 'should apply selection attributes to the inserted text', () => {
  102. setModelData( model, '<paragraph>[]</paragraph>', {
  103. selectionAttributes: {
  104. bold: true,
  105. italic: true
  106. }
  107. } );
  108. viewDocument.fire( 'mutations', [
  109. {
  110. type: 'children',
  111. oldChildren: [],
  112. newChildren: [ new ViewText( 'x' ) ],
  113. node: viewRoot.getChild( 0 )
  114. }
  115. ] );
  116. expect( getModelData( model ) ).to.equal(
  117. '<paragraph><$text bold="true" italic="true">x[]</$text></paragraph>'
  118. );
  119. } );
  120. it( 'should handle multiple text mutations', () => {
  121. editor.setData( '<p>foo<strong>bar</strong></p>' );
  122. viewDocument.fire( 'mutations', [
  123. {
  124. type: 'text',
  125. oldText: 'foo',
  126. newText: 'foob',
  127. node: viewRoot.getChild( 0 ).getChild( 0 )
  128. },
  129. {
  130. type: 'text',
  131. oldText: 'bar',
  132. newText: 'ar',
  133. node: viewRoot.getChild( 0 ).getChild( 1 ).getChild( 0 )
  134. }
  135. ] );
  136. expect( getModelData( model ) ).to.equal( '<paragraph>foob[]<$text bold="true">ar</$text></paragraph>' );
  137. expect( getViewData( view ) ).to.equal( '<p>foob{}<strong>ar</strong></p>' );
  138. } );
  139. it( 'should handle multiple text node insertion', () => {
  140. editor.setData( '<p></p><p></p>' );
  141. viewDocument.fire( 'mutations', [
  142. {
  143. type: 'children',
  144. oldChildren: [],
  145. newChildren: [ new ViewText( 'x' ) ],
  146. node: viewRoot.getChild( 0 )
  147. },
  148. {
  149. type: 'children',
  150. oldChildren: [],
  151. newChildren: [ new ViewText( 'y' ) ],
  152. node: viewRoot.getChild( 1 )
  153. }
  154. ] );
  155. expect( getModelData( model ) ).to.equal( '<paragraph>x</paragraph><paragraph>y[]</paragraph>' );
  156. expect( getViewData( view ) ).to.equal( '<p>x</p><p>y{}</p>' );
  157. } );
  158. it( 'should do nothing when two nodes were inserted', () => {
  159. editor.setData( '<p></p>' );
  160. viewDocument.fire( 'mutations', [
  161. {
  162. type: 'children',
  163. oldChildren: [],
  164. newChildren: [ new ViewText( 'x' ), new ViewElement( 'img' ) ],
  165. node: viewRoot.getChild( 0 )
  166. }
  167. ] );
  168. expect( getModelData( model ) ).to.equal( '<paragraph>[]</paragraph>' );
  169. expect( getViewData( view ) ).to.equal( '<p>[]</p>' );
  170. } );
  171. it( 'should do nothing when two nodes were inserted and one removed', () => {
  172. viewDocument.fire( 'mutations', [
  173. {
  174. type: 'children',
  175. oldChildren: [ new ViewText( 'foobar' ) ],
  176. newChildren: [ new ViewText( 'x' ), new ViewElement( 'img' ) ],
  177. node: viewRoot.getChild( 0 )
  178. }
  179. ] );
  180. expect( getModelData( model ) ).to.equal( '<paragraph>foo[]bar</paragraph>' );
  181. expect( getViewData( view ) ).to.equal( '<p>foo{}bar</p>' );
  182. } );
  183. it( 'should handle multiple children in the node', () => {
  184. editor.setData( '<p>foo<img></img></p>' );
  185. viewDocument.fire( 'mutations', [
  186. {
  187. type: 'children',
  188. oldChildren: [ new ViewText( 'foo' ), viewRoot.getChild( 0 ).getChild( 1 ) ],
  189. newChildren: [ new ViewText( 'foo' ), viewRoot.getChild( 0 ).getChild( 1 ), new ViewText( 'x' ) ],
  190. node: viewRoot.getChild( 0 )
  191. }
  192. ] );
  193. expect( getModelData( model ) ).to.equal( '<paragraph>foo<image></image>x[]</paragraph>' );
  194. expect( getViewData( view ) ).to.equal( '<p>foo<img></img>x{}</p>' );
  195. } );
  196. it( 'should do nothing when node was removed', () => {
  197. viewDocument.fire( 'mutations', [
  198. {
  199. type: 'children',
  200. oldChildren: [ new ViewText( 'foobar' ) ],
  201. newChildren: [],
  202. node: viewRoot.getChild( 0 )
  203. }
  204. ] );
  205. expect( getModelData( model ) ).to.equal( '<paragraph>foo[]bar</paragraph>' );
  206. expect( getViewData( view ) ).to.equal( '<p>foo{}bar</p>' );
  207. } );
  208. it( 'should do nothing when element was inserted', () => {
  209. editor.setData( '<p></p>' );
  210. viewDocument.fire( 'mutations', [
  211. {
  212. type: 'children',
  213. oldChildren: [],
  214. newChildren: [ new ViewElement( 'img' ) ],
  215. node: viewRoot.getChild( 0 )
  216. }
  217. ] );
  218. expect( getModelData( model ) ).to.equal( '<paragraph>[]</paragraph>' );
  219. expect( getViewData( view ) ).to.equal( '<p>[]</p>' );
  220. } );
  221. it( 'should set model selection appropriately to view selection passed in mutations event', () => {
  222. // This test case emulates spellchecker correction.
  223. const viewSelection = new ViewSelection();
  224. viewSelection._setTo( viewRoot.getChild( 0 ).getChild( 0 ), 6 );
  225. viewDocument.fire( 'mutations',
  226. [ {
  227. type: 'text',
  228. oldText: 'foobar',
  229. newText: 'foodar',
  230. node: viewRoot.getChild( 0 ).getChild( 0 )
  231. } ],
  232. viewSelection
  233. );
  234. expect( getModelData( model ) ).to.equal( '<paragraph>foodar[]</paragraph>' );
  235. expect( getViewData( view ) ).to.equal( '<p>foodar{}</p>' );
  236. } );
  237. it( 'should use up to one insert and remove operations (spellchecker)', () => {
  238. // This test case emulates spellchecker correction.
  239. const viewSelection = new ViewSelection();
  240. viewSelection._setTo( viewRoot.getChild( 0 ).getChild( 0 ), 6 );
  241. testUtils.sinon.spy( Writer.prototype, 'insert' );
  242. testUtils.sinon.spy( Writer.prototype, 'remove' );
  243. viewDocument.fire( 'mutations',
  244. [ {
  245. type: 'text',
  246. oldText: 'foobar',
  247. newText: 'fxobxr',
  248. node: viewRoot.getChild( 0 ).getChild( 0 )
  249. } ],
  250. viewSelection
  251. );
  252. expect( Writer.prototype.insert.calledOnce ).to.be.true;
  253. expect( Writer.prototype.remove.calledOnce ).to.be.true;
  254. } );
  255. it( 'should place selection after when correcting to longer word (spellchecker)', () => {
  256. // This test case emulates spellchecker correction.
  257. editor.setData( '<p>Foo hous a</p>' );
  258. const viewSelection = new ViewSelection();
  259. viewSelection._setTo( viewRoot.getChild( 0 ).getChild( 0 ), 9 );
  260. viewDocument.fire( 'mutations',
  261. [ {
  262. type: 'text',
  263. oldText: 'Foo hous a',
  264. newText: 'Foo house a',
  265. node: viewRoot.getChild( 0 ).getChild( 0 )
  266. } ],
  267. viewSelection
  268. );
  269. expect( getModelData( model ) ).to.equal( '<paragraph>Foo house[] a</paragraph>' );
  270. expect( getViewData( view ) ).to.equal( '<p>Foo house{} a</p>' );
  271. } );
  272. it( 'should place selection after when correcting to shorter word (spellchecker)', () => {
  273. // This test case emulates spellchecker correction.
  274. editor.setData( '<p>Bar athat foo</p>' );
  275. const viewSelection = new ViewSelection();
  276. viewSelection._setTo( viewRoot.getChild( 0 ).getChild( 0 ), 8 );
  277. viewDocument.fire( 'mutations',
  278. [ {
  279. type: 'text',
  280. oldText: 'Bar athat foo',
  281. newText: 'Bar that foo',
  282. node: viewRoot.getChild( 0 ).getChild( 0 )
  283. } ],
  284. viewSelection
  285. );
  286. expect( getModelData( model ) ).to.equal( '<paragraph>Bar that[] foo</paragraph>' );
  287. expect( getViewData( view ) ).to.equal( '<p>Bar that{} foo</p>' );
  288. } );
  289. it( 'should place selection after when merging two words (spellchecker)', () => {
  290. // This test case emulates spellchecker correction.
  291. editor.setData( '<p>Foo hous e</p>' );
  292. const viewSelection = new ViewSelection();
  293. viewSelection._setTo( viewRoot.getChild( 0 ).getChild( 0 ), 9 );
  294. viewDocument.fire( 'mutations',
  295. [ {
  296. type: 'text',
  297. oldText: 'Foo hous e',
  298. newText: 'Foo house',
  299. node: viewRoot.getChild( 0 ).getChild( 0 )
  300. } ],
  301. viewSelection
  302. );
  303. expect( getModelData( model ) ).to.equal( '<paragraph>Foo house[]</paragraph>' );
  304. expect( getViewData( view ) ).to.equal( '<p>Foo house{}</p>' );
  305. } );
  306. it( 'should place non-collapsed selection after changing single character (composition)', () => {
  307. editor.setData( '<p>Foo house</p>' );
  308. const viewSelection = new ViewSelection();
  309. viewSelection._setTo( viewRoot.getChild( 0 ).getChild( 0 ), 8 );
  310. viewSelection._setFocus( viewRoot.getChild( 0 ).getChild( 0 ), 9 );
  311. viewDocument.fire( 'mutations',
  312. [ {
  313. type: 'text',
  314. oldText: 'Foo house',
  315. newText: 'Foo housa',
  316. node: viewRoot.getChild( 0 ).getChild( 0 )
  317. } ],
  318. viewSelection
  319. );
  320. expect( getModelData( model ) ).to.equal( '<paragraph>Foo hous[a]</paragraph>' );
  321. expect( getViewData( view ) ).to.equal( '<p>Foo hous{a}</p>' );
  322. } );
  323. it( 'should replace last &nbsp; with space', () => {
  324. model.change( writer => {
  325. writer.setSelection(
  326. ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 0 ), 6, modelRoot.getChild( 0 ), 6 )
  327. );
  328. } );
  329. viewDocument.fire( 'mutations', [
  330. {
  331. type: 'text',
  332. oldText: 'foobar',
  333. newText: 'foobar\u00A0',
  334. node: viewRoot.getChild( 0 ).getChild( 0 )
  335. }
  336. ] );
  337. expect( getModelData( model ) ).to.equal( '<paragraph>foobar []</paragraph>' );
  338. expect( getViewData( view ) ).to.equal( '<p>foobar {}</p>' );
  339. } );
  340. it( 'should replace first &nbsp; with space', () => {
  341. model.change( writer => {
  342. writer.setSelection(
  343. ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 0 ), 0, modelRoot.getChild( 0 ), 0 )
  344. );
  345. } );
  346. viewDocument.fire( 'mutations', [
  347. {
  348. type: 'text',
  349. oldText: 'foobar',
  350. newText: '\u00A0foobar',
  351. node: viewRoot.getChild( 0 ).getChild( 0 )
  352. }
  353. ] );
  354. expect( getModelData( model ) ).to.equal( '<paragraph> []foobar</paragraph>' );
  355. expect( getViewData( view ) ).to.equal( '<p> {}foobar</p>' );
  356. } );
  357. it( 'should replace all &nbsp; with spaces', () => {
  358. model.change( writer => {
  359. writer.setSelection(
  360. ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 0 ), 6, modelRoot.getChild( 0 ), 6 )
  361. );
  362. } );
  363. viewDocument.fire( 'mutations', [
  364. {
  365. type: 'text',
  366. oldText: 'foobar',
  367. newText: 'foobar\u00A0\u00A0\u00A0baz',
  368. node: viewRoot.getChild( 0 ).getChild( 0 )
  369. }
  370. ] );
  371. expect( getModelData( model ) ).to.equal( '<paragraph>foobar baz[]</paragraph>' );
  372. expect( getViewData( view ) ).to.equal( '<p>foobar baz{}</p>' );
  373. } );
  374. } );
  375. describe( 'keystroke handling', () => {
  376. it( 'should remove contents', () => {
  377. model.change( writer => {
  378. writer.setSelection(
  379. ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 0 ), 2, modelRoot.getChild( 0 ), 4 ) );
  380. } );
  381. listenter.listenTo( viewDocument, 'keydown', () => {
  382. expect( getModelData( model ) ).to.equal( '<paragraph>fo[]ar</paragraph>' );
  383. }, { priority: 'lowest' } );
  384. } );
  385. // #97
  386. it( 'should remove contents and merge blocks', () => {
  387. setModelData( model, '<paragraph>fo[o</paragraph><paragraph>b]ar</paragraph>' );
  388. listenter.listenTo( viewDocument, 'keydown', () => {
  389. expect( getModelData( model ) ).to.equal( '<paragraph>fo[]ar</paragraph>' );
  390. viewDocument.fire( 'mutations', [
  391. {
  392. type: 'text',
  393. oldText: 'foar',
  394. newText: 'foyar',
  395. node: viewRoot.getChild( 0 ).getChild( 0 )
  396. }
  397. ] );
  398. }, { priority: 'lowest' } );
  399. viewDocument.fire( 'keydown', { keyCode: getCode( 'y' ) } );
  400. expect( getModelData( model ) ).to.equal( '<paragraph>foy[]ar</paragraph>' );
  401. expect( getViewData( view ) ).to.equal( '<p>foy{}ar</p>' );
  402. } );
  403. it( 'should do nothing on arrow key', () => {
  404. model.change( writer => {
  405. writer.setSelection(
  406. ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 0 ), 2, modelRoot.getChild( 0 ), 4 ) );
  407. } );
  408. viewDocument.fire( 'keydown', { keyCode: getCode( 'arrowdown' ) } );
  409. expect( getModelData( model ) ).to.equal( '<paragraph>fo[ob]ar</paragraph>' );
  410. } );
  411. it( 'should do nothing on ctrl combinations', () => {
  412. model.change( writer => {
  413. writer.setSelection(
  414. ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 0 ), 2, modelRoot.getChild( 0 ), 4 ) );
  415. } );
  416. viewDocument.fire( 'keydown', { ctrlKey: true, keyCode: getCode( 'c' ) } );
  417. expect( getModelData( model ) ).to.equal( '<paragraph>fo[ob]ar</paragraph>' );
  418. } );
  419. it( 'should do nothing on non printable keys', () => {
  420. model.change( writer => {
  421. writer.setSelection(
  422. ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 0 ), 2, modelRoot.getChild( 0 ), 4 ) );
  423. } );
  424. viewDocument.fire( 'keydown', { keyCode: 16 } ); // Shift
  425. viewDocument.fire( 'keydown', { keyCode: 35 } ); // Home
  426. viewDocument.fire( 'keydown', { keyCode: 112 } ); // F1
  427. expect( getModelData( model ) ).to.equal( '<paragraph>fo[ob]ar</paragraph>' );
  428. } );
  429. // #69
  430. it( 'should do nothing on tab key', () => {
  431. model.change( writer => {
  432. writer.setSelection(
  433. ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 0 ), 2, modelRoot.getChild( 0 ), 4 ) );
  434. } );
  435. viewDocument.fire( 'keydown', { keyCode: 9 } ); // Tab
  436. expect( getModelData( model ) ).to.equal( '<paragraph>fo[ob]ar</paragraph>' );
  437. } );
  438. // #82
  439. it( 'should do nothing on composition start key', () => {
  440. model.change( writer => {
  441. writer.setSelection(
  442. ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 0 ), 2, modelRoot.getChild( 0 ), 4 ) );
  443. } );
  444. viewDocument.fire( 'keydown', { keyCode: 229 } );
  445. expect( getModelData( model ) ).to.equal( '<paragraph>fo[ob]ar</paragraph>' );
  446. } );
  447. it( 'should do nothing if selection is collapsed', () => {
  448. viewDocument.fire( 'keydown', { ctrlKey: true, keyCode: getCode( 'c' ) } );
  449. expect( getModelData( model ) ).to.equal( '<paragraph>foo[]bar</paragraph>' );
  450. } );
  451. it( 'should lock buffer if selection is not collapsed', () => {
  452. const buffer = editor.commands.get( 'input' )._buffer;
  453. const lockSpy = testUtils.sinon.spy( buffer, 'lock' );
  454. const unlockSpy = testUtils.sinon.spy( buffer, 'unlock' );
  455. model.change( writer => {
  456. writer.setSelection(
  457. ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 0 ), 2, modelRoot.getChild( 0 ), 4 ) );
  458. } );
  459. viewDocument.fire( 'keydown', { keyCode: getCode( 'y' ) } );
  460. expect( lockSpy.calledOnce ).to.be.true;
  461. expect( unlockSpy.calledOnce ).to.be.true;
  462. } );
  463. it( 'should not lock buffer on non printable keys', () => {
  464. const buffer = editor.commands.get( 'input' )._buffer;
  465. const lockSpy = testUtils.sinon.spy( buffer, 'lock' );
  466. const unlockSpy = testUtils.sinon.spy( buffer, 'unlock' );
  467. viewDocument.fire( 'keydown', { keyCode: 16 } ); // Shift
  468. viewDocument.fire( 'keydown', { keyCode: 35 } ); // Home
  469. viewDocument.fire( 'keydown', { keyCode: 112 } ); // F1
  470. expect( lockSpy.callCount ).to.be.equal( 0 );
  471. expect( unlockSpy.callCount ).to.be.equal( 0 );
  472. } );
  473. it( 'should not lock buffer on collapsed selection', () => {
  474. const buffer = editor.commands.get( 'input' )._buffer;
  475. const lockSpy = testUtils.sinon.spy( buffer, 'lock' );
  476. const unlockSpy = testUtils.sinon.spy( buffer, 'unlock' );
  477. viewDocument.fire( 'keydown', { keyCode: getCode( 'b' ) } );
  478. viewDocument.fire( 'keydown', { keyCode: getCode( 'a' ) } );
  479. viewDocument.fire( 'keydown', { keyCode: getCode( 'z' ) } );
  480. expect( lockSpy.callCount ).to.be.equal( 0 );
  481. expect( unlockSpy.callCount ).to.be.equal( 0 );
  482. } );
  483. it( 'should not modify document when input command is disabled and selection is collapsed', () => {
  484. setModelData( model, '<paragraph>foo[]bar</paragraph>' );
  485. editor.commands.get( 'input' ).isEnabled = false;
  486. viewDocument.fire( 'keydown', { keyCode: getCode( 'b' ) } );
  487. expect( getModelData( model ) ).to.equal( '<paragraph>foo[]bar</paragraph>' );
  488. } );
  489. it( 'should not modify document when input command is disabled and selection is non-collapsed', () => {
  490. setModelData( model, '<paragraph>fo[ob]ar</paragraph>' );
  491. editor.commands.get( 'input' ).isEnabled = false;
  492. viewDocument.fire( 'keydown', { keyCode: getCode( 'b' ) } );
  493. expect( getModelData( model ) ).to.equal( '<paragraph>fo[ob]ar</paragraph>' );
  494. } );
  495. } );
  496. } );