瀏覽代碼

Merge branch 'master' into t/ckeditor5/1151

Aleksander Nowodzinski 6 年之前
父節點
當前提交
509bcdd2ac

+ 32 - 6
packages/ckeditor5-utils/src/ckeditorerror.js

@@ -16,10 +16,27 @@ export const DOCUMENTATION_URL =
 /**
  * The CKEditor error class.
  *
- * All errors will be shortened during the minification process in order to reduce the code size.
- * Therefore, all error messages should be documented in the same way as those in {@link module:utils/log}.
+ * You should throw `CKEditorError` when:
  *
- * Read more in the {@link module:utils/log} module.
+ * * An unexpected situation occurred and the editor (most probably) will not work properly. Such exception will be handled
+ * by the {@link module:watchdog/watchdog~Watchdog watchdog} (if it is integrated),
+ * * If the editor is incorrectly integrated or the editor API is used in the wrong way. This way you will give
+ * feedback to the developer as soon as possible. Keep in mind that for common integration issues which should not
+ * stop editor initialization (like missing upload adapter, wrong name of a toolbar component) we use `console.warn()` with
+ * {@link module:utils/ckeditorerror~attachLinkToDocumentation `attachLinkToDocumentation()`}
+ * to improve developers experience and let them see the working editor as soon as possible.
+ *
+ *		/**
+ *		 * Error thrown when a plugin cannot be loaded due to JavaScript errors, lack of plugins with a given name, etc.
+ *		 *
+ *		 * @error plugin-load
+ *		 * @param pluginName The name of the plugin that could not be loaded.
+ *		 * @param moduleName The name of the module which tried to load this plugin.
+ *		 * /
+ *		throw new CKEditorError( 'plugin-load: It was not possible to load the "{$pluginName}" plugin in module "{$moduleName}', {
+ *			pluginName: 'foo',
+ *			moduleName: 'bar'
+ *		} );
  *
  * @extends Error
  */
@@ -27,8 +44,6 @@ export default class CKEditorError extends Error {
 	/**
 	 * Creates an instance of the CKEditorError class.
 	 *
-	 * Read more about error logging in the {@link module:utils/log} module.
-	 *
 	 * @param {String} message The error message in an `error-name: Error message.` format.
 	 * During the minification process the "Error message" part will be removed to limit the code size
 	 * and a link to this error documentation will be added to the `message`.
@@ -79,7 +94,18 @@ export default class CKEditorError extends Error {
 }
 
 /**
- * Attaches link to the documentation at the end of the error message.
+ * Attaches the link to the documentation at the end of the error message. Use whenever you log a warning or error on the
+ * console. It is also used by {@link module:utils/ckeditorerror~CKEditorError}.
+ *
+ *		 /**
+ *		  * There was a problem processing the configuration of the toolbar. The item with the given
+ *		  * name does not exist so it was omitted when rendering the toolbar.
+ *		  *
+ *		  * @error toolbarview-item-unavailable
+ *		  * @param {String} name The name of the component.
+ *		  * /
+ *		 console.warn( attachLinkToDocumentation(
+ *		 	'toolbarview-item-unavailable: The requested toolbar item is unavailable.' ), { name } );
  *
  * @param {String} message Message to be logged.
  * @returns {String}

+ 10 - 21
packages/ckeditor5-utils/src/dom/rect.js

@@ -10,7 +10,6 @@
 import isRange from './isrange';
 import isWindow from './iswindow';
 import getBorderWidths from './getborderwidths';
-import log from '../log';
 import isText from './istext';
 import { isElement } from 'lodash-es';
 
@@ -67,26 +66,16 @@ export default class Rect {
 		} );
 
 		if ( isElement( source ) || isSourceRange ) {
-			const sourceNode = isSourceRange ? source.startContainer : source;
-
-			if ( !sourceNode.ownerDocument || !sourceNode.ownerDocument.body.contains( sourceNode ) ) {
-				/**
-				 * The `Rect` class depends on `getBoundingClientRect` and `getClientRects` DOM methods.
-				 * If the {@link #constructor source} of a rect in an HTML element or a DOM range but it does
-				 * not belong to any rendered DOM tree, these methods will fail to obtain the geometry and
-				 * the rect instance makes little sense to the features using it.
-				 *
-				 * To get rid of this warning make sure the source passed to the constructor
-				 * is a descendant of `window.document.body`.
-				 *
-				 * @error rect-source-not-in-dom
-				 * @param {String} source The source of the Rect instance.
-				 */
-				log.warn(
-					'rect-source-not-in-dom: The source of this rect does not belong to any rendered DOM tree.',
-					{ source }
-				);
-			}
+			// The `Rect` class depends on `getBoundingClientRect` and `getClientRects` DOM methods. If the source
+			// of a rect in an HTML element or a DOM range but it does not belong to any rendered DOM tree, these methods
+			// will fail to obtain the geometry and the rect instance makes little sense to the features using it.
+			// To get rid of this warning make sure the source passed to the constructor is a descendant of `window.document.body`.
+			// @if CK_DEBUG // const sourceNode = isSourceRange ? source.startContainer : source;
+			// @if CK_DEBUG // if ( !sourceNode.ownerDocument || !sourceNode.ownerDocument.body.contains( sourceNode ) ) {
+			// @if CK_DEBUG // 	console.warn(
+			// @if CK_DEBUG // 		'rect-source-not-in-dom: The source of this rect does not belong to any rendered DOM tree.',
+			// @if CK_DEBUG // 		{ source } );
+			// @if CK_DEBUG // }
 
 			if ( isSourceRange ) {
 				copyRectProperties( this, Rect.getDomRangeRects( source )[ 0 ] );

+ 41 - 1
packages/ckeditor5-utils/src/env.js

@@ -55,7 +55,24 @@ const env = {
 	 * @static
 	 * @type {Boolean}
 	 */
-	isAndroid: isAndroid( userAgent )
+	isAndroid: isAndroid( userAgent ),
+
+	/**
+	 * Environment features information.
+	 *
+	 * @memberOf module:utils/env~env
+	 * @namespace
+	 */
+	features: {
+		/**
+		 * Indicates that the environment supports ES2018 Unicode property escapes — like `\p{P}` or `\p{L}`.
+		 * More information about unicode properties might be found
+		 * [in Unicode Standard Annex #44](https://www.unicode.org/reports/tr44/#GC_Values_Table).
+		 *
+		 * @type {Boolean}
+		 */
+		isRegExpUnicodePropertySupported: isRegExpUnicodePropertySupported()
+	}
 };
 
 export default env;
@@ -109,3 +126,26 @@ export function isSafari( userAgent ) {
 export function isAndroid( userAgent ) {
 	return userAgent.indexOf( 'android' ) > -1;
 }
+
+/**
+ * Checks if the current environment supports ES2018 Unicode properties like `\p{P}` or `\p{L}`.
+ * More information about unicode properties might be found
+ * [in Unicode Standard Annex #44](https://www.unicode.org/reports/tr44/#GC_Values_Table).
+ *
+ * @returns {Boolean}
+ */
+export function isRegExpUnicodePropertySupported() {
+	let isSupported = false;
+
+	// Feature detection for Unicode properties. Added in ES2018. Currently Firefox and Edge do not support it.
+	// See https://github.com/ckeditor/ckeditor5-mention/issues/44#issuecomment-487002174.
+
+	try {
+		// Usage of regular expression literal cause error during build (ckeditor/ckeditor5-dev#534).
+		isSupported = 'ć'.search( new RegExp( '[\\p{L}]', 'u' ) ) === 0;
+	} catch ( error ) {
+		// Firefox throws a SyntaxError when the group is unsupported.
+	}
+
+	return isSupported;
+}

+ 0 - 76
packages/ckeditor5-utils/src/log.js

@@ -1,76 +0,0 @@
-/**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
- */
-
-/* global console */
-
-/**
- * @module utils/log
- */
-
-import { attachLinkToDocumentation } from './ckeditorerror';
-
-/**
- * The logging module.
- *
- * This object features two functions that should be used across CKEditor code base to log errors and warnings.
- * Despite being an overridable interface for native `console.*` this module serves also the goal to limit the
- * code size of a minified CKEditor package. During minification process the messages will be shortened and
- * links to their documentation will be logged to the console.
- *
- * All errors and warning should be documented in the following way:
- *
- *		/**
- *		 * Error thrown when a plugin cannot be loaded due to JavaScript errors, lack of plugins with a given name, etc.
- *		 *
- *		 * @error plugin-load
- *		 * @param pluginName The name of the plugin that could not be loaded.
- *		 * @param moduleName The name of the module which tried to load this plugin.
- *		 * /
- *		log.error( 'plugin-load: It was not possible to load the "{$pluginName}" plugin in module "{$moduleName}', {
- *			pluginName: 'foo',
- *			moduleName: 'bar'
- *		} );
- *
- * ### Warning vs Error vs Throw
- *
- * * Whenever a potentially incorrect situation occurs, which does not directly lead to an incorrect behavior,
- * log a warning.
- * * Whenever an incorrect situation occurs, but the app may continue working (although perhaps incorrectly),
- * log an error.
- * * Whenever it's really bad and it does not make sense to continue working, throw a {@link module:utils/ckeditorerror~CKEditorError}.
- *
- * @namespace
- */
-const log = {
-	/**
-	 * Logs an error to the console.
-	 *
-	 * Read more about error logging in the {@link module:utils/log} module.
-	 *
-	 * @param {String} message The error message in an `error-name: Error message.` format.
-	 * During the minification process the "Error message" part will be removed to limit the code size
-	 * and a link to this error documentation will be logged to the console.
-	 * @param {Object} [data] Additional data describing the error.
-	 */
-	error( message, data ) {
-		console.error( attachLinkToDocumentation( message ), data );
-	},
-
-	/**
-	 * Logs a warning to the console.
-	 *
-	 * Read more about error logging in the {@link module:utils/log} module.
-	 *
-	 * @param {String} message The warning message in a `warning-name: Warning message.` format.
-	 * During the minification process the "Warning message" part will be removed to limit the code size
-	 * and a link to this error documentation will be logged to the console.
-	 * @param {Object} [data] Additional data describing the warning.
-	 */
-	warn( message, data ) {
-		console.warn( attachLinkToDocumentation( message ), data );
-	}
-};
-
-export default log;

+ 5 - 2
packages/ckeditor5-utils/src/version.js

@@ -9,8 +9,8 @@
 
 /* globals window, global */
 
-import log from './log';
 import { version } from 'ckeditor5/package.json';
+import CKEditorError from './ckeditorerror';
 
 const windowOrGlobal = typeof window === 'object' ? window : global;
 
@@ -138,7 +138,10 @@ if ( windowOrGlobal.CKEDITOR_VERSION ) {
 	 *
 	 * @error ckeditor-duplicated-modules
 	 */
-	log.error( 'ckeditor-duplicated-modules: Some CKEditor 5 modules are duplicated.' );
+	throw new CKEditorError(
+		'ckeditor-duplicated-modules: Some CKEditor 5 modules are duplicated.',
+		null
+	);
 } else {
 	windowOrGlobal.CKEDITOR_VERSION = version;
 }

+ 1 - 1
packages/ckeditor5-utils/tests/_utils-tests/utils.js

@@ -29,7 +29,7 @@ describe( 'utils - testUtils', () => {
 		} );
 
 		it( 'should create an observer', () => {
-			function Emitter() { }
+			function Emitter() {}
 			Emitter.prototype = EmitterMixin;
 
 			expect( observer ).to.be.instanceof( Emitter );

+ 4 - 4
packages/ckeditor5-utils/tests/collection.js

@@ -522,7 +522,7 @@ describe( 'Collection', () => {
 			sinon.assert.calledWithExactly( spy, callback, ctx );
 			expect( ret ).to.deep.equal( [ 'foo' ], 'ret value was forwarded' );
 
-			function callback() { }
+			function callback() {}
 		} );
 	} );
 
@@ -538,7 +538,7 @@ describe( 'Collection', () => {
 			sinon.assert.calledWithExactly( spy, callback, ctx );
 			expect( ret ).to.equal( needl, 'ret value was forwarded' );
 
-			function callback() { }
+			function callback() {}
 		} );
 	} );
 
@@ -555,7 +555,7 @@ describe( 'Collection', () => {
 			sinon.assert.calledWithExactly( spy, callback, ctx );
 			expect( ret ).to.deep.equal( [ needl ], 'ret value was forwarded' );
 
-			function callback() { }
+			function callback() {}
 		} );
 	} );
 
@@ -700,7 +700,7 @@ describe( 'Collection', () => {
 			} );
 
 			it( 'does not chain', () => {
-				const returned = collection.bindTo( new Collection() ).using( () => { } );
+				const returned = collection.bindTo( new Collection() ).using( () => {} );
 
 				expect( returned ).to.be.undefined;
 			} );

+ 55 - 78
packages/ckeditor5-utils/tests/dom/rect.js

@@ -3,17 +3,13 @@
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
-/* global window, document */
+/* global window, document, console */
 
 import Rect from '../../src/dom/rect';
-import log from '../../src/log';
-import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 
 describe( 'Rect', () => {
 	let geometry;
 
-	testUtils.createSinonSandbox();
-
 	beforeEach( () => {
 		geometry = {
 			top: 10,
@@ -24,7 +20,11 @@ describe( 'Rect', () => {
 			height: 20
 		};
 
-		testUtils.sinon.stub( log, 'warn' );
+		sinon.stub( console, 'warn' );
+	} );
+
+	afterEach( () => {
+		sinon.restore();
 	} );
 
 	describe( 'constructor()', () => {
@@ -38,7 +38,7 @@ describe( 'Rect', () => {
 		it( 'should accept HTMLElement', () => {
 			const element = document.createElement( 'div' );
 
-			testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
+			sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
 
 			assertRect( new Rect( element ), geometry );
 		} );
@@ -47,7 +47,7 @@ describe( 'Rect', () => {
 			const range = document.createRange();
 
 			range.selectNode( document.body );
-			testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
+			sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
 
 			assertRect( new Rect( range ), geometry );
 		} );
@@ -57,7 +57,7 @@ describe( 'Rect', () => {
 			const range = document.createRange();
 
 			range.collapse();
-			testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
+			sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
 
 			assertRect( new Rect( range ), geometry );
 		} );
@@ -69,8 +69,8 @@ describe( 'Rect', () => {
 
 			range.setStart( element, 0 );
 			range.collapse();
-			testUtils.sinon.stub( range, 'getClientRects' ).returns( [] );
-			testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
+			sinon.stub( range, 'getClientRects' ).returns( [] );
+			sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
 
 			const expectedGeometry = Object.assign( {}, geometry );
 			expectedGeometry.right = expectedGeometry.left;
@@ -80,10 +80,10 @@ describe( 'Rect', () => {
 		} );
 
 		it( 'should accept the window (viewport)', () => {
-			testUtils.sinon.stub( window, 'innerWidth' ).value( 1000 );
-			testUtils.sinon.stub( window, 'innerHeight' ).value( 500 );
-			testUtils.sinon.stub( window, 'scrollX' ).value( 100 );
-			testUtils.sinon.stub( window, 'scrollY' ).value( 200 );
+			sinon.stub( window, 'innerWidth' ).value( 1000 );
+			sinon.stub( window, 'innerHeight' ).value( 500 );
+			sinon.stub( window, 'scrollX' ).value( 100 );
+			sinon.stub( window, 'scrollY' ).value( 200 );
 
 			assertRect( new Rect( window ), {
 				top: 0,
@@ -124,10 +124,10 @@ describe( 'Rect', () => {
 				const range = document.createRange();
 				range.selectNode( iframeWindow.document.body );
 
-				testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
+				sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
 				assertRect( new Rect( range ), geometry );
 
-				testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
+				sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
 				assertRect( new Rect( element ), geometry );
 
 				iframe.remove();
@@ -161,29 +161,6 @@ describe( 'Rect', () => {
 
 			assertRect( geometry, sourceGeometry );
 		} );
-
-		it( 'should warn if the source does not belong to rendered DOM tree (HTML element)', () => {
-			const element = document.createElement( 'div' );
-
-			testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
-
-			const rect = new Rect( element );
-			sinon.assert.calledOnce( log.warn );
-			sinon.assert.calledWithExactly( log.warn, sinon.match( /^rect-source-not-in-dom/ ), { source: element } );
-			assertRect( rect, geometry );
-		} );
-
-		it( 'should warn if the source does not belong to rendered DOM tree (DOM Range)', () => {
-			const range = document.createRange();
-
-			range.collapse();
-			testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
-
-			const rect = new Rect( range );
-			sinon.assert.calledOnce( log.warn );
-			sinon.assert.calledWithExactly( log.warn, sinon.match( /^rect-source-not-in-dom/ ), { source: range } );
-			assertRect( rect, geometry );
-		} );
 	} );
 
 	describe( 'clone()', () => {
@@ -452,7 +429,7 @@ describe( 'Rect', () => {
 		} );
 
 		it( 'should not fail when the rect is for document#body', () => {
-			testUtils.sinon.stub( document.body, 'getBoundingClientRect' ).returns( {
+			sinon.stub( document.body, 'getBoundingClientRect' ).returns( {
 				top: 0,
 				right: 100,
 				bottom: 100,
@@ -482,7 +459,7 @@ describe( 'Rect', () => {
 				ancestor.appendChild( element );
 				iframeWindow.document.body.appendChild( ancestor );
 
-				testUtils.sinon.stub( ancestor, 'getBoundingClientRect' ).returns( {
+				sinon.stub( ancestor, 'getBoundingClientRect' ).returns( {
 					top: 0,
 					right: 50,
 					bottom: 50,
@@ -491,7 +468,7 @@ describe( 'Rect', () => {
 					height: 50
 				} );
 
-				testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( {
+				sinon.stub( element, 'getBoundingClientRect' ).returns( {
 					top: 0,
 					right: 100,
 					bottom: 100,
@@ -517,7 +494,7 @@ describe( 'Rect', () => {
 		} );
 
 		it( 'should return the visible rect (HTMLElement), partially cropped', () => {
-			testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( {
+			sinon.stub( element, 'getBoundingClientRect' ).returns( {
 				top: 0,
 				right: 100,
 				bottom: 100,
@@ -526,7 +503,7 @@ describe( 'Rect', () => {
 				height: 100
 			} );
 
-			testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
+			sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
 				top: 50,
 				right: 150,
 				bottom: 150,
@@ -546,7 +523,7 @@ describe( 'Rect', () => {
 		} );
 
 		it( 'should return the visible rect (HTMLElement), fully visible', () => {
-			testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( {
+			sinon.stub( element, 'getBoundingClientRect' ).returns( {
 				top: 0,
 				right: 100,
 				bottom: 100,
@@ -555,7 +532,7 @@ describe( 'Rect', () => {
 				height: 100
 			} );
 
-			testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
+			sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
 				top: 0,
 				right: 150,
 				bottom: 150,
@@ -578,7 +555,7 @@ describe( 'Rect', () => {
 			ancestorB.appendChild( ancestorA );
 			document.body.appendChild( ancestorB );
 
-			testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( {
+			sinon.stub( element, 'getBoundingClientRect' ).returns( {
 				top: 0,
 				right: 100,
 				bottom: 100,
@@ -587,7 +564,7 @@ describe( 'Rect', () => {
 				height: 100
 			} );
 
-			testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
+			sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
 				top: 50,
 				right: 100,
 				bottom: 100,
@@ -596,7 +573,7 @@ describe( 'Rect', () => {
 				height: 50
 			} );
 
-			testUtils.sinon.stub( ancestorB, 'getBoundingClientRect' ).returns( {
+			sinon.stub( ancestorB, 'getBoundingClientRect' ).returns( {
 				top: 0,
 				right: 150,
 				bottom: 100,
@@ -619,7 +596,7 @@ describe( 'Rect', () => {
 			range.setStart( ancestorA, 0 );
 			range.setEnd( ancestorA, 1 );
 
-			testUtils.sinon.stub( range, 'getClientRects' ).returns( [ {
+			sinon.stub( range, 'getClientRects' ).returns( [ {
 				top: 0,
 				right: 100,
 				bottom: 100,
@@ -628,7 +605,7 @@ describe( 'Rect', () => {
 				height: 100
 			} ] );
 
-			testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
+			sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
 				top: 50,
 				right: 150,
 				bottom: 150,
@@ -648,7 +625,7 @@ describe( 'Rect', () => {
 		} );
 
 		it( 'should return null if there\'s no visible rect', () => {
-			testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( {
+			sinon.stub( element, 'getBoundingClientRect' ).returns( {
 				top: 0,
 				right: 100,
 				bottom: 100,
@@ -657,7 +634,7 @@ describe( 'Rect', () => {
 				height: 100
 			} );
 
-			testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
+			sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
 				top: 150,
 				right: 200,
 				bottom: 200,
@@ -818,8 +795,8 @@ describe( 'Rect', () => {
 		it( 'should exclude scrollbars and borders of a HTMLElement', () => {
 			const element = document.createElement( 'div' );
 
-			testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
-			testUtils.sinon.stub( window, 'getComputedStyle' ).returns( {
+			sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
+			sinon.stub( window, 'getComputedStyle' ).returns( {
 				borderTopWidth: '5px',
 				borderRightWidth: '10px',
 				borderLeftWidth: '5px',
@@ -853,12 +830,12 @@ describe( 'Rect', () => {
 		} );
 
 		it( 'should exclude scrollbars from viewport\'s rect', () => {
-			testUtils.sinon.stub( window, 'innerWidth' ).value( 1000 );
-			testUtils.sinon.stub( window, 'innerHeight' ).value( 500 );
-			testUtils.sinon.stub( window, 'scrollX' ).value( 100 );
-			testUtils.sinon.stub( window, 'scrollY' ).value( 200 );
+			sinon.stub( window, 'innerWidth' ).value( 1000 );
+			sinon.stub( window, 'innerHeight' ).value( 500 );
+			sinon.stub( window, 'scrollX' ).value( 100 );
+			sinon.stub( window, 'scrollY' ).value( 200 );
 
-			testUtils.sinon.stub( document, 'documentElement' ).value( {
+			sinon.stub( document, 'documentElement' ).value( {
 				clientWidth: 990,
 				clientHeight: 490
 			} );
@@ -878,11 +855,11 @@ describe( 'Rect', () => {
 
 			// Mock the properties of the top window. Then make sure the ones
 			// from the child are used.
-			testUtils.sinon.stub( window, 'innerWidth' ).value( 1000 );
-			testUtils.sinon.stub( window, 'innerHeight' ).value( 500 );
-			testUtils.sinon.stub( window, 'scrollX' ).value( 100 );
-			testUtils.sinon.stub( window, 'scrollY' ).value( 200 );
-			testUtils.sinon.stub( document, 'documentElement' ).value( {
+			sinon.stub( window, 'innerWidth' ).value( 1000 );
+			sinon.stub( window, 'innerHeight' ).value( 500 );
+			sinon.stub( window, 'scrollX' ).value( 100 );
+			sinon.stub( window, 'scrollY' ).value( 200 );
+			sinon.stub( document, 'documentElement' ).value( {
 				clientWidth: 990,
 				clientHeight: 490
 			} );
@@ -890,12 +867,12 @@ describe( 'Rect', () => {
 			iframe.addEventListener( 'load', () => {
 				const iframeWindow = iframe.contentWindow;
 
-				testUtils.sinon.stub( iframeWindow, 'innerWidth' ).value( 500 );
-				testUtils.sinon.stub( iframeWindow, 'innerHeight' ).value( 250 );
-				testUtils.sinon.stub( iframeWindow, 'scrollX' ).value( 50 );
-				testUtils.sinon.stub( iframeWindow, 'scrollY' ).value( 100 );
+				sinon.stub( iframeWindow, 'innerWidth' ).value( 500 );
+				sinon.stub( iframeWindow, 'innerHeight' ).value( 250 );
+				sinon.stub( iframeWindow, 'scrollX' ).value( 50 );
+				sinon.stub( iframeWindow, 'scrollY' ).value( 100 );
 
-				testUtils.sinon.stub( iframeWindow.document, 'documentElement' ).value( {
+				sinon.stub( iframeWindow.document, 'documentElement' ).value( {
 					clientWidth: 480,
 					clientHeight: 230
 				} );
@@ -911,7 +888,7 @@ describe( 'Rect', () => {
 
 				// Safari fails because of "afterEach()" hook tries to restore values from removed element.
 				// We need to restore these values manually.
-				testUtils.sinon.restore();
+				sinon.restore();
 				iframe.remove();
 				done();
 			} );
@@ -925,7 +902,7 @@ describe( 'Rect', () => {
 			const range = document.createRange();
 
 			range.selectNode( document.body );
-			testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
+			sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );
 
 			const rects = Rect.getDomRangeRects( range );
 			expect( rects ).to.have.length( 1 );
@@ -938,7 +915,7 @@ describe( 'Rect', () => {
 			const secondGeometry = { top: 20, right: 80, bottom: 60, left: 40, width: 40, height: 40 };
 
 			range.collapse();
-			testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry, secondGeometry ] );
+			sinon.stub( range, 'getClientRects' ).returns( [ geometry, secondGeometry ] );
 
 			const rects = Rect.getDomRangeRects( range );
 			expect( rects ).to.have.length( 2 );
@@ -954,8 +931,8 @@ describe( 'Rect', () => {
 
 			range.setStart( element, 0 );
 			range.collapse();
-			testUtils.sinon.stub( range, 'getClientRects' ).returns( [] );
-			testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
+			sinon.stub( range, 'getClientRects' ).returns( [] );
+			sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
 
 			const expectedGeometry = Object.assign( {}, geometry );
 			expectedGeometry.right = expectedGeometry.left;
@@ -975,8 +952,8 @@ describe( 'Rect', () => {
 
 			range.setStart( textNode, 3 );
 			range.collapse();
-			testUtils.sinon.stub( range, 'getClientRects' ).returns( [] );
-			testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
+			sinon.stub( range, 'getClientRects' ).returns( [] );
+			sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );
 
 			const expectedGeometry = Object.assign( {}, geometry );
 			expectedGeometry.right = expectedGeometry.left;

+ 26 - 1
packages/ckeditor5-utils/tests/env.js

@@ -3,7 +3,7 @@
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
-import env, { isEdge, isMac, isGecko, isSafari, isAndroid } from '../src/env';
+import env, { isEdge, isMac, isGecko, isSafari, isAndroid, isRegExpUnicodePropertySupported } from '../src/env';
 
 function toLowerCase( str ) {
 	return str.toLowerCase();
@@ -44,6 +44,18 @@ describe( 'Env', () => {
 		} );
 	} );
 
+	describe( 'features', () => {
+		it( 'is an object', () => {
+			expect( env.features ).to.be.an( 'object' );
+		} );
+
+		describe( 'isRegExpUnicodePropertySupported', () => {
+			it( 'is a boolean', () => {
+				expect( env.features.isRegExpUnicodePropertySupported ).to.be.a( 'boolean' );
+			} );
+		} );
+	} );
+
 	describe( 'isMac()', () => {
 		it( 'returns true for macintosh UA strings', () => {
 			expect( isMac( 'macintosh' ) ).to.be.true;
@@ -169,4 +181,17 @@ describe( 'Env', () => {
 		} );
 		/* eslint-enable max-len */
 	} );
+
+	describe( 'isRegExpUnicodePropertySupported()', () => {
+		it( 'should detect accessibility of unicode properties', () => {
+			// Usage of regular expression literal cause error during build (ckeditor/ckeditor5-dev#534)
+			const testFn = () => ( new RegExp( '\\p{L}', 'u' ) ).test( 'ć' );
+
+			if ( isRegExpUnicodePropertySupported() ) {
+				expect( testFn() ).to.be.true;
+			} else {
+				expect( testFn ).to.throw();
+			}
+		} );
+	} );
 } );

+ 0 - 75
packages/ckeditor5-utils/tests/log.js

@@ -1,75 +0,0 @@
-/**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
- */
-
-/* globals console */
-
-import log from '../src/log';
-import { DOCUMENTATION_URL } from '../src/ckeditorerror';
-
-describe( 'log', () => {
-	let spy;
-
-	beforeEach( () => {
-		if ( spy ) {
-			spy.restore();
-		}
-	} );
-
-	describe( 'warn()', () => {
-		it( 'logs the message to the console using console.warn()', () => {
-			spy = sinon.stub( console, 'warn' );
-			const data = { bar: 1 };
-
-			log.warn( 'foo', data );
-
-			sinon.assert.calledOnce( spy );
-			sinon.assert.calledWith( spy, 'foo', data );
-
-			log.warn( 'bar' );
-			sinon.assert.calledTwice( spy );
-			sinon.assert.calledWith( spy, 'bar' );
-		} );
-
-		it( 'contains a link which leads to the documentation', () => {
-			spy = sinon.stub( console, 'warn' );
-
-			log.warn( 'model-schema-no-item: Specified item cannot be found.' );
-
-			const logMessage = 'model-schema-no-item: Specified item cannot be found. ' +
-				`Read more: ${ DOCUMENTATION_URL }#error-model-schema-no-item\n`;
-
-			sinon.assert.calledOnce( spy );
-			sinon.assert.calledWith( spy, logMessage );
-		} );
-	} );
-
-	describe( 'error()', () => {
-		it( 'logs the message to the console using console.error()', () => {
-			spy = sinon.stub( console, 'error' );
-			const data = { bar: 1 };
-
-			log.error( 'foo', data );
-
-			sinon.assert.calledOnce( spy );
-			sinon.assert.calledWith( spy, 'foo', data );
-
-			log.error( 'bar' );
-			sinon.assert.calledTwice( spy );
-			sinon.assert.calledWith( spy, 'bar' );
-		} );
-
-		it( 'contains a link which leads to the documentation', () => {
-			spy = sinon.stub( console, 'error' );
-
-			log.error( 'model-schema-no-item: Specified item cannot be found.' );
-
-			const logMessage = 'model-schema-no-item: Specified item cannot be found. ' +
-				`Read more: ${ DOCUMENTATION_URL }#error-model-schema-no-item\n`;
-
-			sinon.assert.calledOnce( spy );
-			sinon.assert.calledWith( spy, logMessage );
-		} );
-	} );
-} );