/** * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ /* bender-tags: view, browser-only */ import { move } from 'ckeditor5/engine/view/writer.js'; import ViewPosition from 'ckeditor5/engine/view/position.js'; import { stringify, parse } from 'ckeditor5/engine/dev-utils/view.js'; import ContainerElement from 'ckeditor5/engine/view/containerelement.js'; import AttributeElement from 'ckeditor5/engine/view/attributeelement.js'; import EmptyElement from 'ckeditor5/engine/view/emptyelement.js'; import Range from 'ckeditor5/engine/view/range.js'; import Position from 'ckeditor5/engine/view/position.js'; import CKEditorError from 'ckeditor5/utils/ckeditorerror.js'; describe( 'writer', () => { /** * Executes test using `parse` and `stringify` utils functions. Uses range delimiters `[]{}` to create and * test ranges. * * @param {String} input * @param {String} expectedResult * @param {String} expectedRemoved */ function test( source, destination, sourceAfterMove, destinationAfterMove ) { let { view: srcView, selection: srcSelection } = parse( source ); let { view: dstView, selection: dstSelection } = parse( destination ); const newRange = move( srcSelection.getFirstRange(), dstSelection.getFirstPosition() ); expect( stringify( dstView, newRange, { showType: true, showPriority: true } ) ).to.equal( destinationAfterMove ); expect( stringify( srcView, null, { showType: true, showPriority: true } ) ).to.equal( sourceAfterMove ); } describe( 'move', () => { it( 'should move single text node', () => { test( '[foobar]', '[]', '', '[foobar]' ); } ); it( 'should not leave empty text nodes', () => { test( '{foobar}', '[]', '', '[foobar]' ); } ); it( 'should move part of the text node', () => { test( 'f{oob}ar', '[]', 'far', '[oob]' ); } ); it( 'should support unicode', () => { test( 'நி{லை}க்கு', 'நி{}கு', 'நிக்கு', 'நி{லை}கு' ); } ); it( 'should move parts of nodes', () => { test( 'f{ooba}r', '[]qux', 'fr', '[ooba}qux' ); } ); it( 'should merge after moving #1', () => { test( '' + 'foo[bar]bazqux' + '', 'foo{}bazqux', 'foobazqux', '' + 'foo[bar]bazqux' + '' ); } ); it( 'should merge after moving #2', () => { test( '' + 'fo{obarba}zqux' + '', 'fo{}zqux', 'fozqux', '' + 'fo{obarba}zqux' + '' ); } ); it( 'should move part of the text node in document fragment', () => { test( 'fo{ob}ar', 'fo{}ar', 'foar', 'fo{ob}ar' ); } ); it( 'should correctly move text nodes inside same parent', () => { let { view, selection } = parse( '[a]bc' ); const newRange = move( selection.getFirstRange(), ViewPosition.createAt( view, 2 ) ); const expectedView = 'b[a}c'; expect( stringify( view, newRange, { showType: true } ) ).to.equal( expectedView ); } ); it( 'should move EmptyElement', () => { test( 'foo[]bar', 'baz{}quix', 'foobar', 'baz[]quix' ); } ); it( 'should throw if trying to move to EmptyElement', () => { const srcAttribute = new AttributeElement( 'b' ); const srcContainer = new ContainerElement( 'p', null, srcAttribute ); const srcRange = Range.createFromParentsAndOffsets( srcContainer, 0, srcContainer, 1 ); const dstEmpty = new EmptyElement( 'img' ); new ContainerElement( 'p', null, dstEmpty ); const dstPosition = new Position( dstEmpty, 0 ); expect( () => { move( srcRange, dstPosition ); } ).to.throw( CKEditorError, 'view-writer-cannot-break-empty-element' ); } ); } ); } );