/** * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ /* bender-tags: model, composer */ import Document from '/ckeditor5/engine/model/document.js'; import Selection from '/ckeditor5/engine/model/selection.js'; import modifySelection from '/ckeditor5/engine/model/composer/modifyselection.js'; import { setData, stringify } from '/tests/engine/_utils/model.js'; describe( 'Delete utils', () => { let document; beforeEach( () => { document = new Document(); document.schema.registerItem( 'p', '$block' ); document.schema.registerItem( 'x', '$block' ); document.schema.registerItem( 'img', '$inline' ); document.schema.allow( { name: '$text', inside: '$root' } ); document.createRoot(); } ); describe( 'modifySelection', () => { describe( 'unit=character', () => { describe( 'within element', () => { test( 'does nothing on empty content', '[]', '[]' ); test( 'does nothing on empty content (with empty element)', '
[]
', '[]
' ); test( 'does nothing on empty content (backward)', '[]', '[]', { direction: 'backward' } ); test( 'does nothing on root boundary', 'foo[]
', 'foo[]
' ); test( 'does nothing on root boundary (backward)', '[]foo
', '[]foo
', { direction: 'backward' } ); test( 'extends one character forward', 'f[]oo
', 'f[o]o
' ); it( 'extends one character backward', () => { setData( document, 'fo[]o
', { lastRangeBackward: true } ); modifySelection( document.selection, { direction: 'backward' } ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( 'f[o]o
' ); expect( document.selection.isBackward ).to.true; } ); test( 'extends one character forward (non-collapsed)', 'f[o]obar
', 'f[oo]bar
' ); it( 'extends one character backward (non-collapsed)', () => { setData( document, 'foob[a]r
', { lastRangeBackward: true } ); modifySelection( document.selection, { direction: 'backward' } ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( 'foo[ba]r
' ); expect( document.selection.isBackward ).to.true; } ); test( 'extends to element boundary', 'fo[]o
', 'fo[o]
' ); it( 'extends to element boundary (backward)', () => { setData( document, 'f[]oo
' ); modifySelection( document.selection, { direction: 'backward' } ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( '[f]oo
' ); expect( document.selection.isBackward ).to.true; } ); test( 'shrinks forward selection (to collapsed)', 'foo[b]ar
', 'foo[]bar
', { direction: 'backward' } ); it( 'shrinks backward selection (to collapsed)', () => { setData( document, 'foo[b]ar
', { lastRangeBackward: true } ); modifySelection( document.selection ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( 'foob[]ar
' ); expect( document.selection.isBackward ).to.false; } ); test( 'extends one element forward', 'f[]oo
f[]oo
f[]xoo
f[x]oo
fo[]o
fo[]o
foo[]b̂ar
', 'foo[b̂]ar
' ); it( 'unicode support - combining mark backward', () => { setData( document, 'foob̂[]ar
' ); modifySelection( document.selection, { direction: 'backward' } ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( 'foo[b̂]ar
' ); expect( document.selection.isBackward ).to.true; } ); test( 'unicode support - combining mark multiple', 'fo[]o̻̐ͩbar
', 'fo[o̻̐ͩ]bar
' ); it( 'unicode support - combining mark multiple backward', () => { setData( document, 'foo̻̐ͩ[]bar
' ); modifySelection( document.selection, { direction: 'backward' } ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( 'fo[o̻̐ͩ]bar
' ); expect( document.selection.isBackward ).to.true; } ); test( 'unicode support - combining mark to the end', 'fo[]o̻̐ͩ
', 'fo[o̻̐ͩ]
' ); test( 'unicode support - surrogate pairs forward', '[]\uD83D\uDCA9
', '[\uD83D\uDCA9]
' ); it( 'unicode support - surrogate pairs backward', () => { setData( document, '\uD83D\uDCA9[]
' ); modifySelection( document.selection, { direction: 'backward' } ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( '[\uD83D\uDCA9]
' ); expect( document.selection.isBackward ).to.true; } ); } ); describe( 'beyond element', () => { test( 'extends over boundary of empty elements', '[]
', '[
]
' ); it( 'extends over boundary of empty elements (backward)', () => { setData( document, '[]
' ); modifySelection( document.selection, { direction: 'backward' } ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( '[
]
' ); expect( document.selection.isBackward ).to.true; } ); test( 'extends over boundary of non-empty elements', 'a[]
bcd
', 'a[
]bcd
' ); it( 'extends over boundary of non-empty elements (backward)', () => { setData( document, 'a
[]bcd
' ); modifySelection( document.selection, { direction: 'backward' } ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( 'a[
]bcd
' ); expect( document.selection.isBackward ).to.true; } ); test( 'extends over character after boundary', 'a[
]bcd
', 'a[
b]cd
' ); it( 'extends over character after boundary (backward)', () => { setData( document, 'abc[
]d
', { lastRangeBackward: true } ); modifySelection( document.selection, { direction: 'backward' } ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( 'ab[c
]d
' ); expect( document.selection.isBackward ).to.true; } ); test( 'extends over boundary when next element has nested elements', 'a[]
a[
]
a[
]
a[
a[]
bc', 'a[
]bc' ); it( 'extends over element when next node is a text (backward)', () => { setData( document, 'ab[]c
' ); modifySelection( document.selection, { direction: 'backward' } ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( 'ab[]c
' ); expect( document.selection.isBackward ).to.true; } ); it( 'shrinks over boundary of empty elements', () => { setData( document, '[
]
', { lastRangeBackward: true } ); modifySelection( document.selection ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( '[]
' ); expect( document.selection.isBackward ).to.false; } ); it( 'shrinks over boundary of empty elements (backward)', () => { setData( document, '[
]
' ); modifySelection( document.selection, { direction: 'backward' } ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( '[]
' ); expect( document.selection.isBackward ).to.false; } ); it( 'shrinks over boundary of non-empty elements', () => { setData( document, 'a[
]b
', { lastRangeBackward: true } ); modifySelection( document.selection ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( 'a
[]b
' ); expect( document.selection.isBackward ).to.false; } ); test( 'shrinks over boundary of non-empty elements (backward)', 'a[
]b
', 'a[]
b
', { direction: 'backward' } ); it( 'updates selection attributes', () => { setData( document, '<$text bold="true">foo$text>[b]
' ); modifySelection( document.selection, { direction: 'backward' } ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( '<$text bold="true">foo[]$text>b
' ); expect( document.selection.getAttribute( 'bold' ) ).to.equal( 'true' ); } ); } ); } ); describe( 'unit=codePoint', () => { test( 'does nothing on empty content', '[]', '[]', { unit: 'codePoint' } ); test( 'does nothing on empty content (with empty element)', '[]
', '[]
', { unit: 'codePoint' } ); test( 'extends one user-perceived character forward - latin letters', 'f[]oo
', 'f[o]o
', { unit: 'codePoint' } ); it( 'extends one user-perceived character backward - latin letters', () => { setData( document, 'fo[]o
' ); modifySelection( document.selection, { unit: 'codePoint', direction: 'backward' } ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( 'f[o]o
' ); expect( document.selection.isBackward ).to.true; } ); test( 'unicode support - combining mark forward', 'foo[]b̂ar
', 'foo[b]̂ar
', { unit: 'codePoint' } ); it.skip( 'unicode support - combining mark backward', () => { setData( document, 'foob[]̂ar
' ); modifySelection( document.selection, { unit: 'codePoint', direction: 'backward' } ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( 'foob[]̂̂ar
' ); expect( document.selection.isBackward ).to.true; } ); test( 'unicode support - combining mark multiple', 'fo[]o̻̐ͩbar
', 'fo[o]̻̐ͩbar
', { unit: 'codePoint' } ); test( 'unicode support - surrogate pairs forward', '[]\uD83D\uDCA9
', '[\uD83D\uDCA9]
', { unit: 'codePoint' } ); it( 'unicode support surrogate pairs backward', () => { setData( document, '\uD83D\uDCA9[]
' ); modifySelection( document.selection, { unit: 'codePoint', direction: 'backward' } ); expect( stringify( document.getRoot(), document.selection ) ).to.equal( '[\uD83D\uDCA9]
' ); expect( document.selection.isBackward ).to.true; } ); } ); } ); function test( title, input, output, options ) { it( title, () => { input = input.normalize(); output = output.normalize(); setData( document, input ); // Creating new instance of selection instead of operation on engine.model.Document#selection. // Document's selection will throw errors in some test cases (which are correct cases, but only for // non-document selections). const testSelection = Selection.createFromSelection( document.selection ); modifySelection( testSelection, options ); expect( stringify( document.getRoot(), testSelection ) ).to.equal( output ); } ); } } );