it ends up in wrong node. We have to manually move it to correct node.
setSelection( [ 1, 0 ], [ 1, 0 ] );
output( 'foo[]bar' );
editor.execute( 'undo' );
output( 'foobar[]' );
undoDisabled();
} );
} );
// Restoring selection in those examples may be completely off.
describe( 'multiple enters, deletes and typing', () => {
function split( path ) {
setSelection( path.slice(), path.slice() );
editor.execute( 'enter' );
}
function merge( path ) {
const selPath = path.slice();
selPath.push( 0 );
setSelection( selPath, selPath.slice() );
editor.execute( 'delete' );
}
function type( path, text ) {
setSelection( path.slice(), path.slice() );
editor.execute( 'input', { text } );
}
function remove( path ) {
setSelection( path.slice(), path.slice() );
editor.execute( 'delete' );
}
it( 'split, split, split', () => {
input( '12345678' );
split( [ 0, 3 ] );
output( '123[]45678' );
split( [ 1, 4 ] );
output( '1234567[]8' );
split( [ 1, 2 ] );
output( '12345[]678' );
editor.execute( 'undo' );
output( '1234567[]8' );
editor.execute( 'undo' );
output( '12345678[]' );
editor.execute( 'undo' );
output( '12345678[]' );
undoDisabled();
editor.execute( 'redo' );
output( '123[]45678' );
editor.execute( 'redo' );
output( '1234567[]8' );
editor.execute( 'redo' );
output( '12345[]678' );
redoDisabled();
} );
it( 'merge, merge, merge', () => {
input( '12345678' );
merge( [ 1 ] );
output( '123[]45678' );
merge( [ 2 ] );
output( '1234567[]8' );
merge( [ 1 ] );
output( '12345[]678' );
editor.execute( 'undo' );
output( '12345678[]' );
editor.execute( 'undo' );
output( '12345678[]' );
editor.execute( 'undo' );
output( '12345[]678' );
undoDisabled();
editor.execute( 'redo' );
output( '12345[]678' );
editor.execute( 'redo' );
output( '12345678[]' );
editor.execute( 'redo' );
output( '12345678[]' );
redoDisabled();
} );
it( 'split, merge, split, merge (same position)', () => {
input( '12345678' );
split( [ 0, 3 ] );
output( '123[]45678' );
merge( [ 1 ] );
output( '123[]45678' );
split( [ 0, 3 ] );
output( '123[]45678' );
merge( [ 1 ] );
output( '123[]45678' );
editor.execute( 'undo' );
output( '12345678[]' );
editor.execute( 'undo' );
output( '12345678[]' );
editor.execute( 'undo' );
output( '12345678[]' );
editor.execute( 'undo' );
output( '12345678[]' );
undoDisabled();
editor.execute( 'redo' );
output( '123[]45678' );
editor.execute( 'redo' );
output( '12345678[]' );
editor.execute( 'redo' );
output( '123[]45678' );
editor.execute( 'redo' );
output( '12345678[]' );
redoDisabled();
} );
it( 'split, split, split, merge, merge, merge', () => {
input( '12345678' );
split( [ 0, 3 ] );
output( '123[]45678' );
split( [ 1, 4 ] );
output( '1234567[]8' );
split( [ 1, 2 ] );
output( '12345[]678' );
merge( [ 1 ] );
output( '123[]45678' );
merge( [ 2 ] );
output( '1234567[]8' );
merge( [ 1 ] );
output( '12345[]678' );
editor.execute( 'undo' );
output( '12345678[]' );
editor.execute( 'undo' );
output( '12345678[]' );
editor.execute( 'undo' );
output( '12345[]678' );
editor.execute( 'undo' );
output( '1234567[]8' );
editor.execute( 'undo' );
output( '12345678[]' );
editor.execute( 'undo' );
output( '12345678[]' );
undoDisabled();
editor.execute( 'redo' );
output( '123[]45678' );
editor.execute( 'redo' );
output( '1234567[]8' );
editor.execute( 'redo' );
output( '12345[]678' );
editor.execute( 'redo' );
output( '12345[]678' );
editor.execute( 'redo' );
output( '12345678[]' );
editor.execute( 'redo' );
output( '12345678[]' );
redoDisabled();
} );
it( 'split, split, merge, split, merge (different order)', () => {
input( '12345678' );
split( [ 0, 3 ] );
output( '123[]45678' );
split( [ 1, 2 ] );
output( '12345[]678' );
merge( [ 1 ] );
output( '123[]45678' );
split( [ 1, 1 ] );
output( '123456[]78' );
merge( [ 1 ] );
output( '12345[]678' );
editor.execute( 'undo' );
output( '123456[]78' );
editor.execute( 'undo' );
output( '12345678[]' );
editor.execute( 'undo' );
output( '12345[]678' );
editor.execute( 'undo' );
output( '12345678[]' );
editor.execute( 'undo' );
output( '12345678[]' );
undoDisabled();
editor.execute( 'redo' );
output( '123[]45678' );
editor.execute( 'redo' );
output( '12345[]678' );
editor.execute( 'redo' );
output( '12345[]678' );
editor.execute( 'redo' );
output( '123456[]78' );
editor.execute( 'redo' );
output( '123456[]78' );
redoDisabled();
} );
it( 'split, remove, split, merge, merge', () => {
input( '12345678' );
split( [ 0, 3 ] );
output( '123[]45678' );
remove( [ 1, 4 ] );
remove( [ 1, 3 ] );
output( '12345[]8' );
split( [ 1, 1 ] );
output( '1234[]58' );
merge( [ 1 ] );
output( '123[]458' );
merge( [ 1 ] );
output( '1234[]58' );
editor.execute( 'undo' );
output( '123458[]' );
editor.execute( 'undo' );
output( '1234[]58' );
editor.execute( 'undo' );
output( '123458[]' );
editor.execute( 'undo' );
output( '1234567[]8' );
editor.execute( 'undo' );
output( '12345678[]' );
undoDisabled();
editor.execute( 'redo' );
output( '123[]45678' );
editor.execute( 'redo' );
output( '123458[]' );
editor.execute( 'redo' );
output( '1234[]58' );
editor.execute( 'redo' );
output( '1234[]58' );
editor.execute( 'redo' );
output( '123458[]' );
redoDisabled();
} );
it( 'split, typing, split, merge, merge', () => {
input( '12345678' );
split( [ 0, 3 ] );
output( '123[]45678' );
type( [ 1, 4 ], 'x' );
type( [ 1, 5 ], 'y' );
output( '1234567xy[]8' );
split( [ 1, 2 ] );
output( '12345[]67xy8' );
merge( [ 1 ] );
output( '123[]4567xy8' );
merge( [ 1 ] );
output( '12345[]67xy8' );
editor.execute( 'undo' );
output( '1234567xy8[]' );
editor.execute( 'undo' );
output( '12345[]67xy8' );
editor.execute( 'undo' );
output( '1234567xy8[]' );
editor.execute( 'undo' );
output( '1234567[]8' );
editor.execute( 'undo' );
output( '12345678[]' );
undoDisabled();
editor.execute( 'redo' );
output( '123[]45678' );
editor.execute( 'redo' );
output( '1234567xy8[]' );
editor.execute( 'redo' );
output( '12345[]67xy8' );
editor.execute( 'redo' );
output( '12345[]67xy8' );
editor.execute( 'redo' );
output( '1234567xy8[]' );
redoDisabled();
} );
} );
describe( 'other reported cases', () => {
// ckeditor5-engine#t/1051
it( 'rename leaks to other elements on undo #1', () => {
input( '[]FooBar' );
doc.batch().rename( root.getChild( 0 ), 'paragraph' );
output( '[]FooBar' );
doc.batch().split( Position.createAt( root.getChild( 0 ), 1 ) );
output( '[]FooBar' );
doc.batch().merge( Position.createAt( root, 2 ) );
output( '[]FooBar' );
editor.execute( 'undo' );
output( '[]FooBar' );
editor.execute( 'undo' );
output( '[]FooBar' );
editor.execute( 'undo' );
output( '[]FooBar' );
} );
// Similar issue that bases on the same error as above, however here we first merge (above we first split).
it( 'rename leaks to other elements on undo #2', () => {
input( '[]FooBar' );
doc.batch().rename( root.getChild( 0 ), 'heading2' );
output( '[]FooBar' );
doc.batch().merge( Position.createAt( root, 1 ) );
output( '[]FooBar' );
editor.execute( 'undo' );
output( '[]FooBar' );
editor.execute( 'undo' );
output( '[]FooBar' );
} );
// Reverse issue, this time first operation is merge and then rename.
it( 'merge, rename, undo, undo is correct', () => {
input( '[]FooBar' );
doc.batch().merge( Position.createAt( root, 1 ) );
output( '[]FooBar' );
doc.batch().rename( root.getChild( 0 ), 'heading2' );
output( '[]FooBar' );
editor.execute( 'undo' );
output( '[]FooBar' );
editor.execute( 'undo' );
output( '[]FooBar' );
} );
// ckeditor5-engine#t/1053
it( 'wrap, split, undo, undo is correct', () => {
input( '[]FooBar' );
doc.batch().wrap( Range.createIn( root ), 'div' );
output( '' );
doc.batch().split( new Position( root, [ 0, 0, 1 ] ) );
output( '' );
editor.execute( 'undo' );
output( '' );
editor.execute( 'undo' );
output( '[]FooBar' );
} );
// ckeditor5-engine#t/1055
it( 'selection attribute setting: split, bold, merge, undo, undo, undo', () => {
input( 'Foo[]
Bar
' );
editor.execute( 'enter' );
output( 'Foo
[]
Bar
' );
editor.execute( 'bold' );
output( 'Foo
<$text bold="true">[]$text>
Bar
' );
editor.execute( 'forwardDelete' );
output( 'Foo
[]Bar
' );
editor.execute( 'undo' );
output( 'Foo
<$text bold="true">[]$text>
Bar
' );
editor.execute( 'undo' );
output( 'Foo
[]
Bar
' );
editor.execute( 'undo' );
output( 'Foo[]
Bar
' );
} );
} );
describe( 'pasting', () => {
function pasteHtml( editor, html ) {
editor.editing.view.fire( 'paste', {
dataTransfer: createDataTransfer( { 'text/html': html } ),
preventDefault() {}
} );
}
function createDataTransfer( data ) {
return {
getData( type ) {
return data[ type ];
}
};
}
// ckeditor5-engine#t/1065
it( 'undo paste into non empty element should not throw and be correct', () => {
input( 'Foo[]' );
pasteHtml( editor, 'a
b
' );
output( 'Fooab[]' );
pasteHtml( editor, 'c
d
' );
output( 'Fooabcd[]' );
editor.execute( 'undo' );
output( 'Fooab[]' );
editor.execute( 'undo' );
output( 'Foo[]' );
} );
} );
describe( 'other edge cases', () => {
it( 'deleteContent between two nodes', () => {
input( 'fo[ob]ar' );
editor.data.deleteContent( doc.selection, doc.batch() );
output( 'fo[]ar' );
editor.execute( 'undo' );
output( 'fo[ob]ar' );
} );
// Related to ckeditor5-engine#891 and ckeditor5-list#51.
it( 'change attribute of removed node then undo and redo', () => {
input( '' );
const gy = doc.graveyard;
const p = doc.getRoot().getChild( 0 );
doc.enqueueChanges( () => {
doc.batch().remove( p );
doc.batch().setAttribute( p, 'bold', true );
} );
editor.execute( 'undo' );
editor.execute( 'redo' );
expect( p.root ).to.equal( gy );
expect( p.getAttribute( 'bold' ) ).to.be.true;
} );
} );
} );