/**
* @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 setTimeout */
import MediaEmbed from '../src/mediaembed';
import AutoMediaEmbed from '../src/automediaembed';
import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import Link from '@ckeditor/ckeditor5-link/src/link';
import Undo from '@ckeditor/ckeditor5-undo/src/undo';
import Typing from '@ckeditor/ckeditor5-typing/src/typing';
import Image from '@ckeditor/ckeditor5-image/src/image';
import ImageCaption from '@ckeditor/ckeditor5-image/src/imagecaption';
import Table from '@ckeditor/ckeditor5-table/src/table';
import global from '@ckeditor/ckeditor5-utils/src/dom/global';
import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
describe( 'AutoMediaEmbed - integration', () => {
let editorElement, editor;
beforeEach( () => {
editorElement = global.document.createElement( 'div' );
global.document.body.appendChild( editorElement );
return ClassicTestEditor
.create( editorElement, {
plugins: [ Typing, Paragraph, Link, Image, ImageCaption, MediaEmbed, AutoMediaEmbed ]
} )
.then( newEditor => {
editor = newEditor;
} );
} );
afterEach( () => {
editorElement.remove();
return editor.destroy();
} );
it( 'should load Clipboard plugin', () => {
expect( editor.plugins.get( Clipboard ) ).to.instanceOf( Clipboard );
} );
it( 'should load Undo plugin', () => {
expect( editor.plugins.get( Undo ) ).to.instanceOf( Undo );
} );
it( 'has proper name', () => {
expect( AutoMediaEmbed.pluginName ).to.equal( 'AutoMediaEmbed' );
} );
describe( 'use fake timers', () => {
let clock;
beforeEach( () => {
clock = sinon.useFakeTimers();
} );
afterEach( () => {
clock.restore();
} );
it( 'replaces pasted text with media element after 100ms', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
expect( getData( editor.model ) ).to.equal(
'https://www.youtube.com/watch?v=H08tGjXNHO4[]'
);
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'[]'
);
} );
it( 'can undo auto-embeding', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
expect( getData( editor.model ) ).to.equal(
'https://www.youtube.com/watch?v=H08tGjXNHO4[]'
);
clock.tick( 100 );
editor.commands.execute( 'undo' );
expect( getData( editor.model ) ).to.equal(
'https://www.youtube.com/watch?v=H08tGjXNHO4[]'
);
} );
it( 'works for a full URL (https + "www" sub-domain)', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'[]'
);
} );
it( 'works for a full URL (https without "www" sub-domain)', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'https://youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'[]'
);
} );
it( 'works for a full URL (http + "www" sub-domain)', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'http://www.youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'[]'
);
} );
it( 'works for a full URL (http without "www" sub-domain)', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'http://youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'[]'
);
} );
it( 'works for a URL without protocol (with "www" sub-domain)', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'www.youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'[]'
);
} );
it( 'works for a URL without protocol (without "www" sub-domain)', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'[]'
);
} );
it( 'works fine if a media has no preview', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'https://twitter.com/ckeditor/status/1035181110140063749' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'[]'
);
} );
it( 'works for URL that was pasted as a link', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'[]'
);
} );
it( 'works for URL that contains some inline styles', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'[]'
);
} );
it( 'works for not collapsed selection inside single element', () => {
setData( editor.model, '[Foo]' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'[]'
);
} );
it( 'works for not collapsed selection over a few elements', () => {
setData( editor.model, 'Fo[oBa]r' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'Fo[]r'
);
} );
it( 'inserts media in-place (collapsed selection)', () => {
setData( editor.model, 'Foo []Bar' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'Foo ' +
'[]' +
'Bar'
);
} );
it( 'inserts media in-place (non-collapsed selection)', () => {
setData( editor.model, 'Foo [Bar] Baz' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'Foo ' +
'[]' +
' Baz'
);
} );
it( 'does nothing if a URL is invalid', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'https://youtube.com' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'https://youtube.com[]'
);
} );
it( 'does nothing if pasted two links as text', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4 https://www.youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'https://www.youtube.com/watch?v=H08tGjXNHO4 https://www.youtube.com/watch?v=H08tGjXNHO4[]'
);
} );
it( 'does nothing if pasted text contains a valid URL', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'Foo bar https://www.youtube.com/watch?v=H08tGjXNHO4 bar foo.' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'Foo bar https://www.youtube.com/watch?v=H08tGjXNHO4 bar foo.[]'
);
} );
it( 'does nothing if pasted more than single node', () => {
setData( editor.model, '[]' );
pasteHtml( editor,
'https://www.youtube.com/watch?v=H08tGjXNHO4 ' +
'https://www.youtube.com/watch?v=H08tGjXNHO4'
);
clock.tick( 100 );
expect( getData( editor.model, { withoutSelection: true } ) ).to.equal(
'https://www.youtube.com/watch?v=H08tGjXNHO4 ' +
'<$text linkHref="https://www.youtube.com/watch?v=H08tGjXNHO4">https://www.youtube.com/watch?v=H08tGjXNHO4$text>' +
''
);
} );
it( 'does nothing if pasted a paragraph with the url', () => {
setData( editor.model, '[]' );
pasteHtml( editor, '
https://www.youtube.com/watch?v=H08tGjXNHO4
' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'https://www.youtube.com/watch?v=H08tGjXNHO4[]'
);
} );
it( 'does nothing if pasted a block of content that looks like a URL', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'https://
youtube.com/watch?
v=H08tGjXNHO4
' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'https://' +
'youtube.com/watch?' +
'v=H08tGjXNHO4[]'
);
} );
it( 'does nothing if a URL is invalid (space inside URL)', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'youtube.com/watch?v=H08tGjXNHO4¶m=foo bar' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'youtube.com/watch?v=H08tGjXNHO4¶m=foo bar[]'
);
} );
// #47
it( 'does not transform a valid URL into a media if the element cannot be placed in the current position', () => {
setData( editor.model, 'Foo.[]' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'Foo.https://www.youtube.com/watch?v=H08tGjXNHO4[]'
);
} );
it( 'replaces a URL in media if pasted a link when other media element was selected', () => {
setData(
editor.model,
'[]'
);
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'[]'
);
} );
it( 'inserts a new media element if pasted a link when other media element was selected in correct place', () => {
setData(
editor.model,
'Foo. <$text linkHref="https://cksource.com">Bar$text>' +
'[]' +
'Bar.'
);
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'Foo. <$text linkHref="https://cksource.com">Bar$text>' +
'[]' +
'Bar.'
);
} );
it( 'does nothing if URL match to media but it was removed', () => {
return ClassicTestEditor
.create( editorElement, {
plugins: [ MediaEmbed, AutoMediaEmbed, Paragraph ],
mediaEmbed: {
removeProviders: [ 'youtube' ]
}
} )
.then( newEditor => {
setData( newEditor.model, '[]' );
pasteHtml( newEditor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
clock.tick( 100 );
expect( getData( newEditor.model ) ).to.equal(
'https://www.youtube.com/watch?v=H08tGjXNHO4[]'
);
editorElement.remove();
return newEditor.destroy();
} );
} );
it( 'works for URL with %-symbols', () => {
setData( editor.model, '[]' );
pasteHtml( editor, 'http://youtube.com/watch?v=H08tGjXNHO4%2' );
clock.tick( 100 );
expect( getData( editor.model ) ).to.equal(
'[]'
);
} );
} );
describe( 'use real timers', () => {
const characters = Array( 10 ).fill( 1 ).map( ( x, i ) => String.fromCharCode( 65 + i ) );
it( 'undo breaks the auto-media embed feature (undo was done before auto-media embed)', done => {
setData( editor.model, '[]' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
expect( getData( editor.model ) ).to.equal(
'https://www.youtube.com/watch?v=H08tGjXNHO4[]'
);
setTimeout( () => {
editor.commands.execute( 'undo' );
expect( getData( editor.model ) ).to.equal(
'[]'
);
done();
} );
} );
// Checking whether paste+typing calls the auto-media handler once.
it( 'pasting handler should be executed once', done => {
const autoMediaEmbedPlugin = editor.plugins.get( AutoMediaEmbed );
const autoMediaHandler = autoMediaEmbedPlugin._embedMediaBetweenPositions;
let counter = 0;
autoMediaEmbedPlugin._embedMediaBetweenPositions = function( ...args ) {
counter += 1;
return autoMediaHandler.apply( this, args );
};
setData( editor.model, '[]' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
simulateTyping( 'Foo. Bar.' );
setTimeout( () => {
autoMediaEmbedPlugin._embedMediaBetweenPositions = autoMediaHandler;
expect( counter ).to.equal( 1 );
done();
}, 100 );
} );
it( 'typing before pasted link during collaboration should not blow up', done => {
setData( editor.model, '[]' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
for ( let i = 0; i < 10; ++i ) {
const rootEl = editor.model.document.getRoot();
setTimeout( () => {
editor.model.enqueueChange( 'transparent', writer => {
writer.insertText( characters[ i ], writer.createPositionFromPath( rootEl, [ 0, i ] ) );
} );
}, i * 5 );
}
setTimeout( () => {
expect( getData( editor.model ) ).to.equal(
'[]ABCDEFGHIJ'
);
done();
}, 100 );
} );
it( 'typing after pasted link during collaboration should not blow up', done => {
setData( editor.model, '[]' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
for ( let i = 0; i < 10; ++i ) {
setTimeout( () => {
editor.model.enqueueChange( 'transparent', writer => {
writer.insertText( characters[ i ], editor.model.document.selection.getFirstPosition() );
} );
}, i * 5 );
}
setTimeout( () => {
expect( getData( editor.model ) ).to.equal(
'[]ABCDEFGHIJ'
);
done();
}, 100 );
} );
it( 'should insert the media element even if parent element where the URL was pasted has been deleted', done => {
setData( editor.model, 'Foo.Bar.[]' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
editor.model.enqueueChange( 'transparent', writer => {
writer.remove( writer.createRangeOn( editor.model.document.getRoot().getChild( 1 ) ) );
} );
setTimeout( () => {
expect( getData( editor.model ) ).to.equal(
'Foo.[]'
);
done();
}, 100 );
} );
it( 'should insert the media element even if new element appeared above the pasted URL', done => {
setData( editor.model, 'Foo.Bar.[]' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
editor.model.enqueueChange( 'transparent', writer => {
const paragraph = writer.createElement( 'paragraph' );
writer.insert( paragraph, writer.createPositionAfter( editor.model.document.getRoot().getChild( 0 ) ) );
writer.setSelection( paragraph, 'in' );
} );
for ( let i = 0; i < 10; ++i ) {
setTimeout( () => {
editor.model.enqueueChange( 'transparent', writer => {
writer.insertText( characters[ i ], editor.model.document.selection.getFirstPosition() );
} );
}, i * 5 );
}
setTimeout( () => {
expect( getData( editor.model ) ).to.equal(
'Foo.' +
'ABCDEFGHIJ' +
'Bar.' +
'[]'
);
done();
}, 100 );
} );
it( 'should insert the media element even if new element appeared below the pasted URL', done => {
setData( editor.model, 'Foo.Bar.[]' );
pasteHtml( editor, 'https://www.youtube.com/watch?v=H08tGjXNHO4' );
editor.model.enqueueChange( 'transparent', writer => {
const paragraph = writer.createElement( 'paragraph' );
writer.insert( paragraph, writer.createPositionAfter( editor.model.document.getRoot().getChild( 1 ) ) );
writer.setSelection( paragraph, 'in' );
} );
for ( let i = 0; i < 10; ++i ) {
setTimeout( () => {
editor.model.enqueueChange( 'transparent', writer => {
writer.insertText( characters[ i ], editor.model.document.selection.getFirstPosition() );
} );
}, i * 5 );
}
setTimeout( () => {
expect( getData( editor.model ) ).to.equal(
'Foo.' +
'Bar.' +
'[]' +
'ABCDEFGHIJ'
);
done();
}, 100 );
} );
} );
it( 'should detach LiveRange', async () => {
const editor = await ClassicTestEditor.create( editorElement, {
plugins: [ Typing, Paragraph, Link, Image, ImageCaption, Table, MediaEmbed, AutoMediaEmbed ]
} );
setData(
editor.model,
'' +
'' +
'[foo]' +
'[bar]' +
'' +
'
'
);
pasteHtml( editor, '' );
expect( getData( editor.model, { withoutSelection: true } ) ).to.equal(
'' +
'' +
'one' +
'two' +
'' +
'
'
);
expect( () => {
editor.setData( '' );
} ).not.to.throw();
await editor.destroy();
} );
function simulateTyping( text ) {
// While typing, every character is an atomic change.
text.split( '' ).forEach( character => {
editor.execute( 'input', {
text: character
} );
} );
}
function pasteHtml( editor, html ) {
editor.editing.view.document.fire( 'paste', {
dataTransfer: createDataTransfer( { 'text/html': html } ),
stopPropagation() {},
preventDefault() {}
} );
}
function createDataTransfer( data ) {
return {
getData( type ) {
return data[ type ];
}
};
}
} );