| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984 |
- /**
- * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
- */
- /* global document */
- import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
- import DomEmitterMixin from '@ckeditor/ckeditor5-utils/src/dom/emittermixin';
- import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
- import EventInfo from '@ckeditor/ckeditor5-utils/src/eventinfo';
- import TwoStepCaretMovement from '../src/twostepcaretmovement';
- import Position from '@ckeditor/ckeditor5-engine/src/model/position';
- import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
- import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
- import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
- import '@ckeditor/ckeditor5-core/tests/_utils/assertions/attribute';
- describe( 'TwoStepCaretMovement()', () => {
- let editor, model, emitter, selection, view, plugin;
- let preventDefaultSpy, evtStopSpy;
- testUtils.createSinonSandbox();
- beforeEach( () => {
- emitter = Object.create( DomEmitterMixin );
- return VirtualTestEditor.create( { plugins: [ TwoStepCaretMovement ] } ).then( newEditor => {
- editor = newEditor;
- model = editor.model;
- selection = model.document.selection;
- view = editor.editing.view;
- plugin = editor.plugins.get( TwoStepCaretMovement );
- preventDefaultSpy = sinon.spy().named( 'preventDefault' );
- evtStopSpy = sinon.spy().named( 'evt.stop' );
- editor.model.schema.extend( '$text', {
- allowAttributes: [ 'a', 'b', 'c' ],
- allowIn: '$root'
- } );
- model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );
- editor.conversion.for( 'upcast' ).elementToAttribute( { view: 'a', model: 'a' } );
- editor.conversion.for( 'upcast' ).elementToAttribute( { view: 'b', model: 'b' } );
- editor.conversion.for( 'upcast' ).elementToAttribute( { view: 'c', model: 'c' } );
- editor.conversion.elementToElement( { model: 'paragraph', view: 'p' } );
- plugin.registerAttribute( 'a' );
- } );
- } );
- afterEach( () => {
- return editor.destroy();
- } );
- describe( 'moving right', () => {
- it( 'should do nothing for unrelated attribute (at the beginning)', () => {
- setData( model, '[]<$text c="true">foo</$text>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 0 },
- '→',
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 1 }
- ] );
- } );
- it( 'should do nothing for unrelated attribute (at the end)', () => {
- setData( model, '<$text c="true">foo[]</$text>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 }
- ] );
- } );
- it( 'should "enter" the text with attribute in two steps', () => {
- setData( model, '<$text c="true">foo[]</$text><$text a="true" b="true">bar</$text>' );
- testTwoStepCaretMovement( [
- // Gravity is not overridden, caret is at the beginning of the text but is "outside" of the text.
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- // Gravity is overridden, caret movement is blocked, selection at the beginning but "inside" the text.
- { selectionAttributes: [ 'a', 'b' ], isGravityOverridden: true, preventDefault: 1, evtStop: 1 },
- '→',
- // Caret movement was not blocked this time (still once) so everything works normally.
- { selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1 }
- ] );
- } );
- it( 'should "leave" the text with attribute in two steps', () => {
- setData( model, '<$text a="true" b="true">bar[]</$text><$text c="true">foo</$text>' );
- testTwoStepCaretMovement( [
- // Gravity is not overridden, caret is at the end of the text but is "inside" of the text.
- { selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- // Gravity is overridden, caret movement is blocked, selection at the end but "outside" the text.
- { selectionAttributes: [ 'c' ], isGravityOverridden: true, preventDefault: 1, evtStop: 1 },
- '→',
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1 }
- ] );
- } );
- it( 'should use two-steps movement when between nodes with the same attribute but different value', () => {
- setData( model, '<$text a="1">bar[]</$text><$text a="2">foo</$text>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 3 },
- '→',
- // <$text a="1">bar</$text><$text a="2">[]foo</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 1, evtStop: 1, caretPosition: 3 },
- '→',
- // <$text a="1">bar</$text><$text a="2">f[]oo</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: 4 }
- ] );
- } );
- // https://github.com/ckeditor/ckeditor5/issues/937
- it( 'should not require two-steps between unrelated attributes inside the initial attribute', () => {
- setData( model, '<$text a="1">fo[]o</$text><$text a="1" b="2">bar</$text><$text a="1">baz</$text>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- { selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- { selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- { selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 }
- ] );
- } );
- // https://github.com/ckeditor/ckeditor5-engine/issues/1301
- it( 'should handle passing through the only character in the block', () => {
- setData( model, '<$text a="1">[]x</$text>' );
- testTwoStepCaretMovement( [
- // <$text a="1">[]x</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 0 },
- '→',
- // <$text a="1">x[]</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 1 },
- '→',
- // <$text a="1">x</$text>[]
- { selectionAttributes: [], isGravityOverridden: true, preventDefault: 1, evtStop: 1, caretPosition: 1 },
- '→',
- // Stays at <$text a="1">x</$text>[]
- { selectionAttributes: [], isGravityOverridden: true, preventDefault: 1, evtStop: 1, caretPosition: 1 }
- ] );
- } );
- // https://github.com/ckeditor/ckeditor5-engine/issues/1301
- it( 'should handle passing through the only character in the block (no attribute in the initial selection)', () => {
- setData( model, '[]<$text a="1">x</$text>' );
- model.change( writer => writer.removeSelectionAttribute( 'a' ) );
- testTwoStepCaretMovement( [
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- { selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 1, evtStop: 1 },
- '→',
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1 },
- '→',
- { selectionAttributes: [], isGravityOverridden: true, preventDefault: 2, evtStop: 2 },
- '→',
- { selectionAttributes: [], isGravityOverridden: true, preventDefault: 2, evtStop: 2 }
- ] );
- } );
- // https://github.com/ckeditor/ckeditor5-engine/issues/1301
- it( 'should handle passing through the only-child with an attribute (multiple characters)', () => {
- setData( model, '[]<$text a="1">xyz</$text>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- // <$text a="1">x{}yz</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- // <$text a="1">xy{}z</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- // <$text a="1">xyz{}</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- // <$text a="1">xyz</$text>{}
- { selectionAttributes: [], isGravityOverridden: true, preventDefault: 1, evtStop: 1 },
- '→',
- // <$text a="1">xyz</$text>{}
- { selectionAttributes: [], isGravityOverridden: true, preventDefault: 1, evtStop: 1 }
- ] );
- } );
- it( 'should handle leaving an attribute followed by another block', () => {
- setData( model, '<paragraph><$text a="1">foo[]</$text></paragraph><paragraph><$text b="1">bar</$text></paragraph>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- // <paragraph><$text a="1">bar</$text>[]</paragraph><paragraph>foo</paragraph>
- { selectionAttributes: [], isGravityOverridden: true, preventDefault: 1, evtStop: 1 },
- '→',
- // <paragraph><$text a="1">bar</$text></paragraph><paragraph>f[]oo</paragraph>
- { selectionAttributes: [ 'b' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1 },
- '→',
- // <paragraph><$text a="1">bar</$text></paragraph><paragraph>fo[]o</paragraph>
- { selectionAttributes: [ 'b' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1 }
- ] );
- } );
- } );
- describe( 'moving left', () => {
- it( 'should "enter" the text with attribute in two steps', () => {
- setData( model, '<$text>foo</$text><$text a="true" b="true">bar</$text><$text c="true">b[]iz</$text>' );
- testTwoStepCaretMovement( [
- // Gravity is not overridden, caret is a one character after the and of the text.
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- // Caret movement was not blocked but the gravity is overridden.
- { selectionAttributes: [ 'c' ], isGravityOverridden: true, preventDefault: 0, evtStop: 0 },
- '←',
- { selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1 },
- '←',
- { selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1 }
- ] );
- } );
- it( 'should "leave" the text with attribute in two steps', () => {
- setData( model, '<$text c="true">foo</$text><$text a="true" b="true">b[]ar</$text>' );
- testTwoStepCaretMovement( [
- // Gravity is not overridden, caret is a one character after the beginning of the text.
- { selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- // Caret movement was not blocked.
- { selectionAttributes: [ 'a', 'b' ], isGravityOverridden: true, preventDefault: 0, evtStop: 0 },
- '←',
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1 },
- '←',
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1 }
- ] );
- } );
- it( 'should do nothing for unrelated attribute (at the beginning)', () => {
- setData( model, '<$text c="true">[]foo</$text>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 }
- ] );
- } );
- it( 'should do nothing for unrelated attribute (at the end)', () => {
- setData( model, '<$text c="true">foo</$text>[]' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 }
- ] );
- } );
- it( 'should do nothing when caret is at the beginning of block element', () => {
- setData( model, '[]foo', { lastRangeBackward: true } );
- testTwoStepCaretMovement( [
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 0, evtStop: 0 }
- ] );
- } );
- it( 'should require two-steps movement when caret goes between text node with the same attribute but different value', () => {
- setData( model, '<$text a="2">foo</$text><$text a="1">b[]ar</$text>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 4 },
- '←',
- // <$text a="2">foo</$text><$text a="1">[]bar</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 0, evtStop: 0, caretPosition: 3 },
- '←',
- // <$text a="2">foo[]</$text><$text a="1">bar</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: 3 }
- ] );
- expect( selection ).to.have.attribute( 'a', 2 );
- testTwoStepCaretMovement( [
- '←',
- // <$text a="2">fo[]o</$text><$text a="1">bar</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: 2 }
- ] );
- } );
- // https://github.com/ckeditor/ckeditor5/issues/937
- it( 'should not require two-steps between unrelated attributes inside the initial attribute', () => {
- setData( model, '<$text a="1">foo</$text><$text a="1" b="2">bar</$text><$text a="1">b[]az</$text>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- { selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- { selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- { selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 }
- ] );
- } );
- // https://github.com/ckeditor/ckeditor5-engine/issues/1301
- it( 'should handle passing through the only-child with an attribute (single character)', () => {
- setData( model, '<$text a="1">x</$text>[]' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 1 },
- '←',
- // <$text a="1">{}x</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 0, evtStop: 0, caretPosition: 0 },
- '←',
- // {}<$text a="1">x</$text> (because it's a first-child)
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: 0 },
- '←',
- // {}<$text a="1">x</$text>
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: 0 }
- ] );
- } );
- // https://github.com/ckeditor/ckeditor5-engine/issues/1301
- it( 'should handle passing through the only character in the block (no attribute in the initial selection)', () => {
- setData( model, '<$text a="1">x</$text>[]' );
- model.change( writer => writer.removeSelectionAttribute( 'a' ) );
- testTwoStepCaretMovement( [
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1 },
- '←',
- { selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 1, evtStop: 1 },
- '←',
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 2, evtStop: 2 },
- '←',
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 2, evtStop: 2 }
- ] );
- } );
- // https://github.com/ckeditor/ckeditor5-engine/issues/1301
- it( 'should handle passing through the only-child with an attribute (single character, text before)', () => {
- setData( model, 'abc<$text a="1">x</$text>[]' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- // abc<$text a="1">{}x</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 0, evtStop: 0 },
- '←',
- // abc{}<$text a="1">x</$text> (because it's a first-child)
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1 },
- '←',
- // abc{}<$text a="1">x</$text>
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1 }
- ] );
- } );
- // https://github.com/ckeditor/ckeditor5-engine/issues/1301
- it( 'should handle passing through the only-child with an attribute (multiple characters)', () => {
- setData( model, '<$text a="1">xyz</$text>[]' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- // <$text a="1">xy{}z</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- // <$text a="1">x{}yz</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- // <$text a="1">{}xyz</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 0, evtStop: 0 },
- '←',
- // {}<$text a="1">xyz</$text>
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1 },
- '←',
- // {}<$text a="1">xyz</$text>
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1 }
- ] );
- } );
- it( 'should handle leaving an attribute preceded by another block', () => {
- setData( model, '<paragraph><$text b="1">foo</$text></paragraph><paragraph><$text a="1">[]bar</$text></paragraph>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: [ 1, 0 ] },
- '←',
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: [ 1, 0 ] },
- '←',
- { selectionAttributes: [ 'b' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: [ 0, 3 ] }
- ] );
- } );
- } );
- describe( 'moving and typing around the attribute', () => {
- it( 'should handle typing after the attribute', () => {
- setData( model, '<$text a="1">x[]</$text>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 1 },
- 'y',
- // <$text a="1">xy[]</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 2 },
- '→',
- // <$text a="1">xy</$text>[]
- { selectionAttributes: [], isGravityOverridden: true, preventDefault: 1, evtStop: 1, caretPosition: 2 },
- 'z',
- // <$text a="1">xy</$text>z[]
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: 3 },
- '←',
- // <$text a="1">xy</$text>[]z
- { selectionAttributes: [], isGravityOverridden: true, preventDefault: 1, evtStop: 1, caretPosition: 2 },
- '←',
- // <$text a="1">xy[]</$text>z
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 2, evtStop: 2, caretPosition: 2 },
- 'w',
- // <$text a="1">xyw[]</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 2, evtStop: 2, caretPosition: 3 }
- ] );
- } );
- it( 'should handle typing before the attribute', () => {
- setData( model, '<$text a="1">[]x</$text>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- // []<$text a="1">x</$text>
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1 },
- 'z',
- // z[]<$text a="1">x</$text>
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1 },
- 'x',
- // zx[]<$text a="1">x</$text>
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1 },
- '→',
- // zx<$text a="1">[]x</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 2, evtStop: 2 },
- 'a',
- // zx<$text a="1">a[]x</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 2, evtStop: 2 }
- ] );
- } );
- // https://github.com/ckeditor/ckeditor5-engine/issues/1346
- // https://github.com/ckeditor/ckeditor5/issues/946
- it( 'should correctly re-renter the attribute', () => {
- setData( model, 'fo[]o <$text a="1">bar</$text>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- // foo[] <$text a="1">bar</$text>
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- // foo []<$text a="1">bar</$text>
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- // foo <$text a="1">[]bar</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 1, evtStop: 1 },
- '→',
- // foo <$text a="1">b[]ar</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1 },
- '←',
- // foo <$text a="1">[]bar</$text>
- { selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 1, evtStop: 1 },
- '←',
- // foo []<$text a="1">bar</$text>
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 2, evtStop: 2 },
- '←',
- // foo[] <$text a="1">bar</$text>
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 2, evtStop: 2 },
- '←',
- // fo[]o <$text a="1">bar</$text>
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 2, evtStop: 2 }
- ] );
- } );
- // https://github.com/ckeditor/ckeditor5/issues/922
- it( 'should not lose the new attribute when typing (after)', () => {
- setData( model, '<$text a="1">x[]</$text>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- // <$text a="1">x</$text>[]
- { selectionAttributes: [], isGravityOverridden: true, preventDefault: 1, evtStop: 1 }
- ] );
- model.change( writer => {
- writer.setSelectionAttribute( 'b', 1 );
- } );
- // <$text a="1">x</$text><$text b="1">[]</$text>
- expect( selection ).to.have.property( 'isGravityOverridden', true );
- expect( getSelectionAttributesArray( selection ) ).to.have.members( [ 'b' ] );
- model.change( writer => {
- writer.insertText( 'yz', selection.getAttributes(), selection.getFirstPosition() );
- } );
- // <$text a="1">x</$text><$text b="1">yz[]</$text>
- expect( selection ).to.have.property( 'isGravityOverridden', false );
- expect( getSelectionAttributesArray( selection ) ).to.have.members( [ 'b' ] );
- } );
- // https://github.com/ckeditor/ckeditor5/issues/922
- it( 'should not lose the new attribute when typing (before)', () => {
- setData( model, '<$text a="1">[]x</$text>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- // []<$text a="1">x</$text>
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1 }
- ] );
- model.change( writer => {
- writer.setSelectionAttribute( 'b', 1 );
- } );
- // <$text b="1">[]</$text><$text a="1">x</$text>
- expect( selection ).to.have.property( 'isGravityOverridden', false );
- expect( getSelectionAttributesArray( selection ) ).to.have.members( [ 'b' ] );
- model.change( writer => {
- writer.insertText( 'yz', selection.getAttributes(), selection.getFirstPosition() );
- } );
- // <$text b="1">yz[]</$text><$text a="1">x</$text>
- expect( selection ).to.have.property( 'isGravityOverridden', false );
- expect( getSelectionAttributesArray( selection ) ).to.have.members( [ 'b' ] );
- } );
- } );
- describe( 'multiple attributes', () => {
- beforeEach( () => {
- plugin.registerAttribute( 'c' );
- } );
- it( 'should work with the two-step caret movement (moving right)', () => {
- setData( model, 'fo[]o<$text a="true">foo</$text><$text a="true" c="true">bar</$text><$text c="true">baz</$text>qux' );
- testTwoStepCaretMovement( [
- // fo[]o<$text a="true">foo</$text><$text a="true" c="true">bar</$text><$text c="true">baz</$text>qux
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- // foo[]<$text a="true">foo</$text><$text a="true" c="true">bar</$text><$text c="true">baz</$text>qux
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- // foo<$text a="true">[]foo</$text><$text a="true" c="true">bar</$text><$text c="true">baz</$text>qux
- { selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 1, evtStop: 1 },
- '→',
- '→',
- '→',
- // foo<$text a="true">foo[]</$text><$text a="true" c="true">bar</$text><$text c="true">baz</$text>qux
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1 },
- '→',
- // foo<$text a="true">foo</$text><$text a="true" c="true">[]bar</$text><$text c="true">baz</$text>qux
- { selectionAttributes: [ 'a', 'c' ], isGravityOverridden: true, preventDefault: 2, evtStop: 2 },
- '→',
- '→',
- '→',
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar[]</$text><$text c="true">baz</$text>qux
- { selectionAttributes: [ 'a', 'c' ], isGravityOverridden: false, preventDefault: 2, evtStop: 2 },
- '→',
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar</$text><$text c="true">[]baz</$text>qux
- { selectionAttributes: [ 'c' ], isGravityOverridden: true, preventDefault: 3, evtStop: 3 },
- '→',
- '→',
- '→',
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar</$text><$text c="true">baz[]</$text>qux
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 3, evtStop: 3 },
- '→',
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar</$text><$text c="true">baz</$text>[]qux
- { selectionAttributes: [], isGravityOverridden: true, preventDefault: 4, evtStop: 4 },
- '→',
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar</$text><$text c="true">baz</$text>q[]ux
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 4, evtStop: 4 }
- ] );
- } );
- it( 'should work with the two-step caret movement (moving left)', () => {
- setData( model, 'foo<$text a="true">foo</$text><$text a="true" c="true">bar</$text><$text c="true">baz</$text>q[]ux' );
- testTwoStepCaretMovement( [
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar</$text><$text c="true">baz</$text>q[]ux
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '←',
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar</$text><$text c="true">baz</$text>[]qux
- { selectionAttributes: [], isGravityOverridden: true, preventDefault: 0, evtStop: 0 },
- '←',
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar</$text><$text c="true">baz[]</$text>qux
- { selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1 },
- '←',
- '←',
- '←',
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar</$text><$text c="true">[]baz</$text>qux
- { selectionAttributes: [ 'c' ], isGravityOverridden: true, preventDefault: 1, evtStop: 1 },
- '←',
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar[]</$text><$text c="true">baz</$text>qux
- { selectionAttributes: [ 'a', 'c' ], isGravityOverridden: false, preventDefault: 2, evtStop: 2 },
- '←',
- '←',
- '←',
- // foo<$text a="true">foo</$text><$text a="true" c="true">[]bar</$text><$text c="true">baz</$text>qux
- { selectionAttributes: [ 'a', 'c' ], isGravityOverridden: true, preventDefault: 2, evtStop: 2 },
- '←',
- // foo<$text a="true">foo[]</$text><$text a="true" c="true">bar</$text><$text c="true">baz</$text>qux
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 3, evtStop: 3 },
- '←',
- '←',
- '←',
- // foo<$text a="true">[]foo</$text><$text a="true" c="true">bar</$text><$text c="true">baz</$text>qux
- { selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 3, evtStop: 3 },
- '←',
- // foo[]<$text a="true">foo</$text><$text a="true" c="true">bar</$text><$text c="true">baz</$text>qux
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 4, evtStop: 4 },
- '←',
- // fo[]o<$text a="true">foo</$text><$text a="true" c="true">bar</$text><$text c="true">baz</$text>qux
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 4, evtStop: 4 }
- ] );
- } );
- describe( 'when two elements ends at the same position', () => {
- it( 'moving the caret in should take 2 steps', () => {
- setData( model, 'foo<$text a="true">foo</$text><$text a="true" c="true">bar</$text>q[]ux' );
- testTwoStepCaretMovement( [
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar</$text>q[]ux
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 10 },
- '←',
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar</$text>[]qux
- { selectionAttributes: [], isGravityOverridden: true, preventDefault: 0, evtStop: 0, caretPosition: 9 },
- '←',
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar[]</$text>qux
- { selectionAttributes: [ 'a', 'c' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: 9 },
- '←',
- // foo<$text a="true">foo</$text><$text a="true" c="true">ba[]r</$text>qux
- { selectionAttributes: [ 'a', 'c' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: 8 }
- ] );
- } );
- it( 'moving the caret out should take 2 steps', () => {
- setData( model, 'foo<$text a="true">foo</$text><$text a="true" c="true">ba[]r</$text>qux' );
- testTwoStepCaretMovement( [
- // foo<$text a="true">foo</$text><$text a="true" c="true">ba[]r</$text>qux
- { selectionAttributes: [ 'a', 'c' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 8 },
- '→',
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar[]</$text>qux
- { selectionAttributes: [ 'a', 'c' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 9 },
- '→',
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar</$text>[]qux
- { selectionAttributes: [], isGravityOverridden: true, preventDefault: 1, evtStop: 1, caretPosition: 9 },
- '→',
- // foo<$text a="true">foo</$text><$text a="true" c="true">bar</$text>q[]ux
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: 10 }
- ] );
- } );
- } );
- describe( 'when two elements starts at the same position', () => {
- it( 'moving the caret in should take 2 steps', () => {
- setData( model, 'fo[]o<$text a="true" c="true">bar</$text><$text a="true">baz</$text>qux' );
- testTwoStepCaretMovement( [
- // fo[]o<$text a="true" c="true">bar</$text><$text a="true">baz</$text>qux
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 2 },
- '→',
- // foo[]<$text a="true" c="true">bar</$text><$text a="true">baz</$text>qux
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 3 },
- '→',
- // foo<$text a="true" c="true">[]bar</$text><$text a="true">baz</$text>qux
- { selectionAttributes: [ 'a', 'c' ], isGravityOverridden: true, preventDefault: 1, evtStop: 1, caretPosition: 3 },
- '→',
- // foo<$text a="true" c="true">b[]ar</$text><$text a="true">baz</$text>qux
- { selectionAttributes: [ 'a', 'c' ], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: 4 }
- ] );
- } );
- it( 'moving the caret out should take 2 steps', () => {
- setData( model, 'foo<$text a="true" c="true">b[]ar</$text><$text a="true">baz</$text>qux' );
- testTwoStepCaretMovement( [
- // foo<$text a="true" c="true">b[]ar</$text><$text a="true">baz</$text>qux
- { selectionAttributes: [ 'a', 'c' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 4 },
- '←',
- // foo<$text a="true" c="true">[]bar</$text><$text a="true">baz</$text>qux
- { selectionAttributes: [ 'a', 'c' ], isGravityOverridden: true, preventDefault: 0, evtStop: 0, caretPosition: 3 },
- '←',
- // foo[]<$text a="true" c="true">bar</$text><$text a="true">baz</$text>qux
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: 3 },
- '←',
- // fo[]o<$text a="true" c="true">bar</$text><$text a="true">baz</$text>qux
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: 2 }
- ] );
- } );
- } );
- } );
- describe( 'mouse', () => {
- it( 'should not override gravity when selection is placed at the beginning of text', () => {
- setData( model, '<$text a="true">[]foo</$text>' );
- expect( selection ).to.have.property( 'isGravityOverridden', false );
- } );
- it( 'should not override gravity when selection is placed at the end of text', () => {
- setData( model, '<$text a="true">foo[]</$text>' );
- expect( selection ).to.have.property( 'isGravityOverridden', false );
- } );
- } );
- it( 'should listen with the high+1 priority on view.document#keydown', () => {
- const highestPrioritySpy = sinon.spy().named( 'highestPrioritySpy' );
- const highPrioritySpy = sinon.spy().named( 'highPrioritySpy' );
- const normalPrioritySpy = sinon.spy().named( 'normalPrioritySpy' );
- setData( model, '<$text c="true">foo[]</$text><$text a="true" b="true">bar</$text>' );
- emitter.listenTo( view.document, 'keydown', highestPrioritySpy, { priority: 'highest' } );
- emitter.listenTo( view.document, 'keydown', highPrioritySpy, { priority: 'high' } );
- emitter.listenTo( view.document, 'keydown', normalPrioritySpy, { priority: 'normal' } );
- fireKeyDownEvent( {
- keyCode: keyCodes.arrowright,
- preventDefault: preventDefaultSpy
- } );
- expect( highestPrioritySpy ).to.be.calledOnce;
- expect( preventDefaultSpy ).to.be.calledImmediatelyAfter( highestPrioritySpy );
- expect( highPrioritySpy ).not.to.be.called;
- expect( normalPrioritySpy ).not.to.be.called;
- } );
- it( 'should do nothing when key other then arrow left and right is pressed', () => {
- setData( model, '<$text a="true">foo[]</$text>' );
- expect( () => {
- fireKeyDownEvent( { keyCode: keyCodes.arrowup } );
- } ).to.not.throw();
- } );
- it( 'should do nothing for non-collapsed selection', () => {
- setData( model, '<$text c="true">fo[o]</$text><$text a="true" b="true">bar</$text>' );
- fireKeyDownEvent( { keyCode: keyCodes.arrowright } );
- expect( selection ).to.have.property( 'isGravityOverridden', false );
- } );
- it( 'should do nothing when shift key is pressed', () => {
- setData( model, '<$text c="true">foo</$text><$text a="true" b="true">b[]ar</$text>' );
- fireKeyDownEvent( {
- keyCode: keyCodes.arrowleft,
- shiftKey: true
- } );
- expect( selection ).to.have.property( 'isGravityOverridden', false );
- } );
- it( 'should do nothing when alt key is pressed', () => {
- setData( model, '<$text c="true">foo</$text><$text a="true" b="true">b[]ar</$text>' );
- fireKeyDownEvent( {
- keyCode: keyCodes.arrowleft,
- altKey: true
- } );
- expect( selection ).to.have.property( 'isGravityOverridden', false );
- } );
- it( 'should do nothing when ctrl key is pressed', () => {
- setData( model, '<$text c="true">foo</$text><$text a="true" b="true">b[]ar</$text>' );
- fireKeyDownEvent( {
- keyCode: keyCodes.arrowleft,
- ctrlKey: true
- } );
- expect( selection ).to.have.property( 'isGravityOverridden', false );
- } );
- it( 'should do nothing when the not a direct selection change but at the attribute boundary', () => {
- setData( model, '<$text a="true">foo[]</$text>bar' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0 },
- '→',
- { selectionAttributes: [], isGravityOverridden: true, preventDefault: 1, evtStop: 1 }
- ] );
- // Simulate an external text insertion BEFORE the user selection to trigger #change:range.
- model.enqueueChange( 'transparent', writer => {
- writer.insertText( 'x', selection.getFirstPosition().getShiftedBy( -2 ) );
- } );
- expect( selection ).to.have.property( 'isGravityOverridden', true );
- expect( getSelectionAttributesArray( selection ) ).to.have.members( [] );
- } );
- describe( 'right–to–left content', () => {
- it( 'should use the opposite helper methods (RTL content direction)', () => {
- // let model;
- return VirtualTestEditor
- .create( {
- plugins: [ TwoStepCaretMovement ],
- language: {
- content: 'he'
- }
- } )
- .then( newEditor => {
- model = newEditor.model;
- selection = model.document.selection;
- view = newEditor.editing.view;
- newEditor.model.schema.extend( '$text', {
- allowAttributes: [ 'a', 'b', 'c' ],
- allowIn: '$root'
- } );
- model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );
- newEditor.conversion.for( 'upcast' ).elementToAttribute( { view: 'a', model: 'a' } );
- newEditor.conversion.for( 'upcast' ).elementToAttribute( { view: 'b', model: 'b' } );
- newEditor.conversion.for( 'upcast' ).elementToAttribute( { view: 'c', model: 'c' } );
- newEditor.conversion.elementToElement( { model: 'paragraph', view: 'p' } );
- newEditor.plugins.get( TwoStepCaretMovement ).registerAttribute( 'a' );
- return newEditor;
- } )
- .then( newEditor => {
- setData( model, '<$text>לזה[]</$text><$text a="true">שיוצג</$text>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 3 },
- '←',
- { selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 1, evtStop: 1, caretPosition: 3 }
- ], 'rtl' );
- preventDefaultSpy.resetHistory();
- evtStopSpy.resetHistory();
- setData( model, '<$text>לזה</$text><$text a="true">ש[]יוצג</$text>' );
- testTwoStepCaretMovement( [
- { selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0, evtStop: 0, caretPosition: 4 },
- '→',
- { selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 0, evtStop: 0, caretPosition: 3 },
- '→',
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: 3 },
- '→',
- { selectionAttributes: [], isGravityOverridden: false, preventDefault: 1, evtStop: 1, caretPosition: 2 }
- ], 'rtl' );
- return newEditor.destroy();
- } );
- } );
- } );
- const keyMap = {
- '→': 'arrowright',
- '←': 'arrowleft'
- };
- function fireKeyDownEvent( options ) {
- const eventInfo = new EventInfo( view.document, 'keydown' );
- const eventData = new DomEventData( view.document, {
- target: document.body
- }, options );
- sinon.stub( eventInfo, 'stop' ).callsFake( evtStopSpy );
- view.document.fire( eventInfo, eventData );
- }
- function getSelectionAttributesArray( selection ) {
- return Array.from( selection.getAttributeKeys() );
- }
- function testTwoStepCaretMovement( scenario, rtl ) {
- for ( const step of scenario ) {
- if ( typeof step == 'string' ) {
- // An arrow key pressed. Fire the view event and update the model selection.
- if ( keyMap[ step ] ) {
- let preventDefaultCalled;
- fireKeyDownEvent( {
- keyCode: keyCodes[ keyMap[ step ] ],
- preventDefault: () => {
- preventDefaultSpy();
- preventDefaultCalled = true;
- }
- } );
- const position = selection.getFirstPosition();
- if ( !preventDefaultCalled ) {
- if ( step == '→' && !rtl || step == '←' && rtl ) {
- model.change( writer => {
- if ( position.isAtEnd ) {
- const nextBlock = position.parent.nextSibling;
- if ( nextBlock ) {
- writer.setSelection( Position._createAt( nextBlock, 0 ) );
- }
- } else {
- writer.setSelection( selection.getFirstPosition().getShiftedBy( 1 ) );
- }
- } );
- } else if ( step == '←' && !rtl || step == '→' && rtl ) {
- model.change( writer => {
- if ( position.isAtStart ) {
- const previousBlock = position.parent.previousSibling;
- if ( previousBlock ) {
- writer.setSelection( Position._createAt( previousBlock, 'end' ) );
- }
- } else {
- writer.setSelection( selection.getFirstPosition().getShiftedBy( -1 ) );
- }
- } );
- }
- }
- }
- // A regular key pressed. Type some text in the model.
- else {
- model.change( writer => {
- writer.insertText( step, selection.getAttributes(), selection.getFirstPosition() );
- } );
- }
- }
- // If not a key, then it's an assertion.
- else {
- const stepIndex = scenario.indexOf( step );
- const stepString = `in step #${ stepIndex }`;
- let caretPosition = step.caretPosition;
- if ( caretPosition !== undefined ) {
- // Normalize position
- caretPosition = Array.isArray( step.caretPosition ) ? caretPosition : [ caretPosition ];
- expect( selection.getFirstPosition(), `in step #${ stepIndex }, selection's first position` )
- .to.have.deep.property( 'path', caretPosition );
- }
- expect( getSelectionAttributesArray( selection ), `${ stepString }, selection's attributes` )
- .to.have.members( step.selectionAttributes );
- expect( selection, `${ stepString }, selection's gravity` )
- .to.have.property( 'isGravityOverridden', step.isGravityOverridden );
- expect( preventDefaultSpy, stepString ).to.have.callCount( step.preventDefault );
- expect( evtStopSpy, stepString ).to.have.callCount( step.evtStop );
- }
- }
- }
- } );
|