/**
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
import CodeBlockEditing from '@ckeditor/ckeditor5-code-block/src/codeblockediting';
import Enter from '@ckeditor/ckeditor5-enter/src/enter';
import Input from '@ckeditor/ckeditor5-typing/src/input';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import ShiftEnter from '@ckeditor/ckeditor5-enter/src/shiftenter';
import UndoEditing from '@ckeditor/ckeditor5-undo/src/undoediting';
import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
import LinkEditing from '../src/linkediting';
import AutoLink from '../src/autolink';
describe( 'AutoLink', () => {
let editor;
it( 'should be named', () => {
expect( AutoLink.pluginName ).to.equal( 'AutoLink' );
} );
it( 'should be loaded without Enter & ShiftEnter features', async () => {
const editor = await ModelTestEditor.create( {
plugins: [ Paragraph, Input, LinkEditing, AutoLink ]
} );
await editor.destroy();
} );
describe( 'auto link behavior', () => {
let model;
beforeEach( async () => {
editor = await ModelTestEditor.create( {
plugins: [ Paragraph, Input, Enter, ShiftEnter, LinkEditing, AutoLink ]
} );
model = editor.model;
setData( model, '[]' );
} );
it( 'does nothing on typing normal text', () => {
simulateTyping( 'Cupcake ipsum dolor. Sit amet caramels. Pie jelly-o lemon drops fruitcake.' );
expect( getData( model ) ).to.equal(
'Cupcake ipsum dolor. Sit amet caramels. Pie jelly-o lemon drops fruitcake.[]'
);
} );
it( 'does not add linkHref attribute to a text link while typing', () => {
simulateTyping( 'https://www.cksource.com' );
expect( getData( model ) ).to.equal(
'https://www.cksource.com[]'
);
} );
it( 'adds linkHref attribute to a text link after space', () => {
simulateTyping( 'https://www.cksource.com ' );
expect( getData( model ) ).to.equal(
'<$text linkHref="https://www.cksource.com">https://www.cksource.com$text> []'
);
} );
it( 'does not add linkHref attribute if linkHref is not allowed', () => {
model.schema.addAttributeCheck( () => false ); // Disable all attributes.
simulateTyping( 'https://www.cksource.com ' );
expect( getData( model ) ).to.equal(
'https://www.cksource.com []'
);
} );
it( 'adds linkHref attribute to a text link after space (inside paragraph)', () => {
setData( model, 'Foo Bar [] Baz' );
simulateTyping( 'https://www.cksource.com ' );
expect( getData( model ) ).to.equal(
'Foo Bar <$text linkHref="https://www.cksource.com">https://www.cksource.com$text> [] Baz'
);
} );
it( 'adds linkHref attribute to a text link after a soft break', () => {
setData( model, 'https://www.cksource.com[]' );
editor.execute( 'shiftEnter' );
// TODO: should test with selection but master has a bug. See: https://github.com/ckeditor/ckeditor5/issues/7459.
expect( getData( model, { withoutSelection: true } ) ).to.equal(
'' +
'<$text linkHref="https://www.cksource.com">https://www.cksource.com$text>' +
'' +
''
);
} );
it( 'does not add linkHref attribute to a text link after double soft break', () => {
setData( model, 'https://www.cksource.com[]' );
editor.execute( 'shiftEnter' );
expect( getData( model ) ).to.equal(
'https://www.cksource.com[]'
);
} );
it( 'adds linkHref attribute to a text link on enter', () => {
setData( model, 'https://www.cksource.com[]' );
editor.execute( 'enter' );
expect( getData( model ) ).to.equal(
'' +
'<$text linkHref="https://www.cksource.com">https://www.cksource.com$text>' +
'' +
'[]'
);
} );
it( 'adds "mailto://" to link of detected email addresses', () => {
simulateTyping( 'newsletter@cksource.com ' );
expect( getData( model ) ).to.equal(
'<$text linkHref="mailto://newsletter@cksource.com">newsletter@cksource.com$text> []'
);
} );
// Some examples came from https://mathiasbynens.be/demo/url-regex.
describe( 'supported URL', () => {
const supportedURLs = [
'http://cksource.com',
'https://cksource.com',
'https://cksource.com:8080',
'http://www.cksource.com',
'hTtP://WwW.cKsOuRcE.cOm',
'www.cksource.com',
'http://foo.bar.cksource.com',
'http://www.cksource.com/some/path/index.html#abc',
'http://www.cksource.com/some/path/index.html?foo=bar',
'http://www.cksource.com/some/path/index.html?foo=bar#abc',
'http://www.cksource.com:8080/some/path/index.html?foo=bar#abc',
'http://www.cksource.com/some/path/index.html#abc?foo=bar',
'ftp://cksource.com',
'http://cksource.com/foo_bar',
'http://cksource.com/foo_bar/',
'http://cksource.com/foo_bar_(wikipedia)',
'http://cksource.com/foo_bar_(wikipedia)_(again)',
'http://www.cksource.com/wpstyle/?p=364',
'http://www.cksource.com/wpstyle/?bar=baz&inga=42&quux',
'http://userid:password@example.com:8080' +
'http://userid:password@example.com:8080/' +
'http://userid@cksource.com' +
'http://userid@cksource.com/' +
'http://userid@cksource.com:8080' +
'http://userid@cksource.com:8080/' +
'http://userid:password@cksource.com' +
'http://userid:password@cksource.com/' +
'http://🥳df.ws/123',
'http://🥳.ws/富',
'http://🥳.ws',
'http://🥳.ws/',
'http://cksource.com/blah_(wikipedia)#cite-1',
'http://cksource.com/blah_(wikipedia)_blah#cite-1',
'http://cksource.com/unicode_(🥳)_in_parens',
'http://cksource.com/(something)?after=parens',
'http://🥳.cksource.com/',
'http://code.cksource.com/woot/#&product=browser',
'http://j.mp',
'ftp://cksource.com/baz',
'http://cksource.com/?q=Test%20URL-encoded%20stuff',
'http://مثال.إختبار',
'http://例子.测试',
'http://उदाहरण.परीक्षा',
'http://1337.net',
'http://a.b-c.de'
];
for ( const supportedURL of supportedURLs ) {
it( `should detect "${ supportedURL }" as a valid URL`, () => {
simulateTyping( supportedURL + ' ' );
expect( getData( model ) ).to.equal(
`<$text linkHref="${ supportedURL }">${ supportedURL }$text> []` );
} );
}
} );
describe( 'invalid or supported URL', () => {
// Some examples came from https://mathiasbynens.be/demo/url-regex.
const unsupportedOrInvalid = [
'http://',
'http://.',
'http://..',
'http://../',
'http://🥳',
'http://?',
'http://??',
'http://??/',
'http://#',
'http://##',
'http://##/',
'//',
'//a',
'///a',
'///',
'http:///a',
'rdar://1234',
'h://test',
':// foo bar',
'ftps://foo.bar/',
'http://-error-.invalid/',
'http://localhost',
'http:/cksource.com',
'cksource.com',
'ww.cksource.com'
];
for ( const unsupportedURL of unsupportedOrInvalid ) {
it( `should not detect "${ unsupportedURL }" as a valid URL`, () => {
simulateTyping( unsupportedURL + ' ' );
expect( getData( model ) ).to.equal(
`${ unsupportedURL } []` );
} );
}
} );
} );
describe( 'Undo integration', () => {
let model;
beforeEach( async () => {
editor = await ModelTestEditor.create( {
plugins: [ Paragraph, Input, Enter, ShiftEnter, LinkEditing, AutoLink, UndoEditing ]
} );
model = editor.model;
setData( model, 'https://www.cksource.com[]' );
} );
it( 'should undo auto-linking (after space)', () => {
simulateTyping( ' ' );
editor.commands.execute( 'undo' );
expect( getData( model ) ).to.equal(
'https://www.cksource.com []'
);
} );
it( 'should undo auto-linking (after )', () => {
editor.execute( 'shiftEnter' );
editor.commands.execute( 'undo' );
expect( getData( model ) ).to.equal(
'https://www.cksource.com[]'
);
} );
it( 'should undo auto-linking (after enter)', () => {
editor.execute( 'enter' );
editor.commands.execute( 'undo' );
expect( getData( model ) ).to.equal(
'https://www.cksource.com' +
'[]'
);
} );
} );
describe( 'Code blocks integration', () => {
let model;
beforeEach( async () => {
editor = await ModelTestEditor.create( {
plugins: [ Paragraph, Input, Enter, ShiftEnter, LinkEditing, AutoLink, CodeBlockEditing ]
} );
model = editor.model;
} );
it( 'should be disabled inside code blocks (on space)', () => {
setData( model, 'some [] code' );
const plugin = editor.plugins.get( 'AutoLink' );
simulateTyping( 'www.cksource.com' );
expect( plugin.isEnabled ).to.be.false;
expect( getData( model, { withoutSelection: true } ) )
.to.equal( 'some www.cksource.com code' );
} );
it( 'should be disabled inside code blocks (on enter)', () => {
setData( model, 'some www.cksource.com[] code' );
const plugin = editor.plugins.get( 'AutoLink' );
editor.execute( 'enter' );
expect( plugin.isEnabled ).to.be.false;
expect( getData( model, { withoutSelection: true } ) ).to.equal(
'some www.cksource.com' +
' code'
);
} );
it( 'should be disabled inside code blocks (on shift-enter)', () => {
setData( model, 'some www.cksource.com[] code' );
const plugin = editor.plugins.get( 'AutoLink' );
editor.execute( 'shiftEnter' );
expect( plugin.isEnabled ).to.be.false;
expect( getData( model, { withoutSelection: true } ) ).to.equal(
'some www.cksource.com code'
);
} );
} );
function simulateTyping( text ) {
const letters = text.split( '' );
for ( const letter of letters ) {
editor.execute( 'input', { text: letter } );
}
}
} );