Преглед на файлове

Merge pull request #8218 from ckeditor/i/8140

Other (utils): Make custom errors more readable in the console. Closes #8140.

MINOR BREAKING CHANGE (utils): The `attachLinkToDocumentiation()` helper was removed. To log errors to the console with attached link to the documentation use `logWarning()` and `logError()`.
Piotrek Koszuliński преди 5 години
родител
ревизия
40801bae03

+ 3 - 5
packages/ckeditor5-core/src/plugincollection.js

@@ -7,9 +7,7 @@
  * @module core/plugincollection
  */
 
-/* globals console */
-
-import CKEditorError, { attachLinkToDocumentation } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
+import CKEditorError, { logError } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
@@ -207,7 +205,7 @@ export default class PluginCollection {
 			const errorId = 'plugincollection-plugin-not-found';
 
 			// Log the error, so it's more visible on the console. Hopefully, for better DX.
-			console.error( attachLinkToDocumentation( errorId ), { plugins: missingPlugins } );
+			logError( errorId, { plugins: missingPlugins } );
 
 			return Promise.reject( new CKEditorError( errorId, context, { plugins: missingPlugins } ) );
 		}
@@ -249,7 +247,7 @@ export default class PluginCollection {
 					 * @error plugincollection-load
 					 * @param {String} plugin The name of the plugin that could not be loaded.
 					 */
-					console.error( attachLinkToDocumentation( 'plugincollection-load' ), { plugin: PluginConstructor } );
+					logError( 'plugincollection-load', { plugin: PluginConstructor } );
 
 					throw err;
 				} );

+ 2 - 4
packages/ckeditor5-engine/src/conversion/upcasthelpers.js

@@ -7,13 +7,11 @@ import Matcher from '../view/matcher';
 import ConversionHelpers from './conversionhelpers';
 
 import { cloneDeep } from 'lodash-es';
-import { attachLinkToDocumentation } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
+import { logWarning } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 import priorities from '@ckeditor/ckeditor5-utils/src/priorities';
 import { isParagraphable, wrapInParagraph } from '../model/utils/autoparagraphing';
 
-/* global console */
-
 /**
  * Contains {@link module:engine/view/view view} to {@link module:engine/model/model model} converters for
  * {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher}.
@@ -349,7 +347,7 @@ export default class UpcastHelpers extends ConversionHelpers {
 		 *
 		 * @error upcast-helpers-element-to-marker-deprecated
 		 */
-		console.warn( attachLinkToDocumentation( 'upcast-helpers-element-to-marker-deprecated' ) );
+		logWarning( 'upcast-helpers-element-to-marker-deprecated' );
 
 		return this.add( upcastElementToMarker( config ) );
 	}

+ 3 - 8
packages/ckeditor5-image/src/imagestyle/utils.js

@@ -7,13 +7,11 @@
  * @module image/imagestyle/utils
  */
 
-/* globals console */
-
 import fullWidthIcon from '@ckeditor/ckeditor5-core/theme/icons/object-full-width.svg';
 import leftIcon from '@ckeditor/ckeditor5-core/theme/icons/object-left.svg';
 import centerIcon from '@ckeditor/ckeditor5-core/theme/icons/object-center.svg';
 import rightIcon from '@ckeditor/ckeditor5-core/theme/icons/object-right.svg';
-import { attachLinkToDocumentation } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
+import { logWarning } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 /**
  * Default image styles provided by the plugin that can be referred in the
@@ -120,12 +118,9 @@ function _normalizeStyle( style ) {
 			 * There is no such image style of given name.
 			 *
 			 * @error image-style-not-found
-		 	 * @param {String} name Name of a missing style name.
+			 * @param {String} name Name of a missing style name.
 			 */
-			console.warn(
-				attachLinkToDocumentation( 'image-style-not-found' ),
-				{ name: styleName }
-			);
+			logWarning( 'image-style-not-found', { name: styleName } );
 
 			// Normalize the style anyway to prevent errors.
 			style = {

+ 2 - 1
packages/ckeditor5-image/tests/imagestyle/utils.js

@@ -109,7 +109,8 @@ describe( 'ImageStyle utils', () => {
 				sinon.assert.calledOnce( console.warn );
 				sinon.assert.calledWithExactly( console.warn,
 					sinon.match( /^image-style-not-found/ ),
-					{ name: 'foo' }
+					{ name: 'foo' },
+					sinon.match.string // Link to the documentation
 				);
 			} );
 		} );

+ 2 - 4
packages/ckeditor5-media-embed/src/mediaregistry.js

@@ -7,13 +7,11 @@
  * @module media-embed/mediaregistry
  */
 
-/* globals console */
-
 import mediaPlaceholderIcon from '../theme/icons/media-placeholder.svg';
 import TooltipView from '@ckeditor/ckeditor5-ui/src/tooltip/tooltipview';
 import IconView from '@ckeditor/ckeditor5-ui/src/icon/iconview';
 import Template from '@ckeditor/ckeditor5-ui/src/template';
-import { attachLinkToDocumentation } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
+import { logWarning } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 const mediaPlaceholderIconViewBox = '0 0 64 42';
 
@@ -48,7 +46,7 @@ export default class MediaRegistry {
 					 *
 					 * @error media-embed-no-provider-name
 					 */
-					console.warn( attachLinkToDocumentation( 'media-embed-no-provider-name' ), { provider } );
+					logWarning( 'media-embed-no-provider-name', { provider } );
 
 					return false;
 				}

+ 5 - 8
packages/ckeditor5-mention/src/mentionui.js

@@ -7,8 +7,6 @@
  * @module mention/mentionui
  */
 
-/* global console */
-
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
 import Collection from '@ckeditor/ckeditor5-utils/src/collection';
@@ -16,7 +14,7 @@ import clickOutsideHandler from '@ckeditor/ckeditor5-ui/src/bindings/clickoutsid
 import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
 import env from '@ckeditor/ckeditor5-utils/src/env';
 import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect';
-import CKEditorError, { attachLinkToDocumentation } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
+import CKEditorError, { logWarning } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import ContextualBalloon from '@ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon';
 import { debounce } from 'lodash-es';
 
@@ -156,11 +154,9 @@ export default class MentionUI extends Plugin {
 				 * See {@link module:mention/mention~MentionConfig}.
 				 *
 				 * @error mentionconfig-incorrect-marker
+				 * @param {String} marker Configured marker
 				 */
-				throw new CKEditorError(
-					'mentionconfig-incorrect-marker',
-					null
-				);
+				throw new CKEditorError( 'mentionconfig-incorrect-marker', null, { marker } );
 			}
 
 			const minimumCharacters = mentionDescription.minimumCharacters || 0;
@@ -348,8 +344,9 @@ export default class MentionUI extends Plugin {
 				 * not displayed at all.
 				 *
 				 * @error mention-feed-callback-error
+				 * @param {String} marker Configured marker for which error occurred.
 				 */
-				console.warn( attachLinkToDocumentation( 'mention-feed-callback-error' ) );
+				logWarning( 'mention-feed-callback-error', { marker } );
 			} );
 	}
 

+ 9 - 4
packages/ckeditor5-mention/tests/mentionui.js

@@ -66,19 +66,19 @@ describe( 'MentionUI', () => {
 	describe( 'init()', () => {
 		it( 'should throw if marker was not provided for feed', () => {
 			return createClassicTestEditor( { feeds: [ { feed: [ 'a' ] } ] } ).catch( error => {
-				assertCKEditorError( error, /mentionconfig-incorrect-marker/, null );
+				assertCKEditorError( error, /mentionconfig-incorrect-marker/, null, { marker: undefined } );
 			} );
 		} );
 
 		it( 'should throw if marker is empty string', () => {
 			return createClassicTestEditor( { feeds: [ { marker: '', feed: [ 'a' ] } ] } ).catch( error => {
-				assertCKEditorError( error, /mentionconfig-incorrect-marker/, null );
+				assertCKEditorError( error, /mentionconfig-incorrect-marker/, null, { marker: '' } );
 			} );
 		} );
 
 		it( 'should throw if marker is longer then 1 character', () => {
 			return createClassicTestEditor( { feeds: [ { marker: '$$', feed: [ 'a' ] } ] } ).catch( error => {
-				assertCKEditorError( error, /mentionconfig-incorrect-marker/, null );
+				assertCKEditorError( error, /mentionconfig-incorrect-marker/, null, { marker: '$$' } );
 			} );
 		} );
 	} );
@@ -1249,7 +1249,12 @@ describe( 'MentionUI', () => {
 						expect( panelView.isVisible, 'panel is hidden' ).to.be.false;
 						expect( editor.model.markers.has( 'mention' ), 'there is no marker' ).to.be.false;
 
-						sinon.assert.calledWithExactly( warnSpy, sinon.match( /^mention-feed-callback-error/ ) );
+						sinon.assert.calledWithExactly(
+							warnSpy,
+							sinon.match( /^mention-feed-callback-error/ ),
+							sinon.match( { marker: '#' } ),
+							sinon.match.string // Link to the documentation
+						);
 						sinon.assert.calledOnce( eventSpy );
 					} );
 			} );

+ 2 - 4
packages/ckeditor5-ui/src/toolbar/toolbarview.js

@@ -7,8 +7,6 @@
  * @module ui/toolbar/toolbarview
  */
 
-/* globals console */
-
 import View from '../view';
 import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
 import FocusCycler from '../focuscycler';
@@ -19,7 +17,7 @@ import preventDefault from '../bindings/preventdefault.js';
 import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect';
 import global from '@ckeditor/ckeditor5-utils/src/dom/global';
 import { createDropdown, addToolbarToDropdown } from '../dropdown/utils';
-import { attachLinkToDocumentation } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
+import { logWarning } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import verticalDotsIcon from '@ckeditor/ckeditor5-core/theme/icons/three-vertical-dots.svg';
 
 import '../../theme/components/toolbar/toolbar.css';
@@ -299,7 +297,7 @@ export default class ToolbarView extends View {
 				 * @error toolbarview-item-unavailable
 				 * @param {String} name The name of the component.
 				 */
-				console.warn( attachLinkToDocumentation( 'toolbarview-item-unavailable' ), { name } );
+				logWarning( 'toolbarview-item-unavailable', { name } );
 			}
 		} ).filter( item => item !== undefined ) );
 	}

+ 2 - 1
packages/ckeditor5-ui/tests/toolbar/toolbarview.js

@@ -421,7 +421,8 @@ describe( 'ToolbarView', () => {
 			sinon.assert.calledOnce( consoleWarnStub );
 			sinon.assert.calledWithExactly( consoleWarnStub,
 				sinon.match( /^toolbarview-item-unavailable/ ),
-				{ name: 'baz' }
+				sinon.match( { name: 'baz' } ),
+				sinon.match.string // Link to the documentation.
 			);
 		} );
 	} );

+ 3 - 3
packages/ckeditor5-upload/src/adapters/simpleuploadadapter.js

@@ -7,11 +7,11 @@
  * @module upload/adapters/simpleuploadadapter
  */
 
-/* globals XMLHttpRequest, FormData, console */
+/* globals XMLHttpRequest, FormData */
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import FileRepository from '../filerepository';
-import { attachLinkToDocumentation } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
+import { logWarning } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 /**
  * The Simple upload adapter allows uploading images to an application running on your server using
@@ -71,7 +71,7 @@ export default class SimpleUploadAdapter extends Plugin {
 			 *
 			 * @error simple-upload-adapter-missing-uploadurl
 			 */
-			console.warn( attachLinkToDocumentation( 'simple-upload-adapter-missing-uploadurl' ) );
+			logWarning( 'simple-upload-adapter-missing-uploadurl' );
 
 			return;
 		}

+ 2 - 4
packages/ckeditor5-upload/src/filerepository.js

@@ -7,12 +7,10 @@
  * @module upload/filerepository
  */
 
-/* globals console */
-
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 
 import PendingActions from '@ckeditor/ckeditor5-core/src/pendingactions';
-import CKEditorError, { attachLinkToDocumentation } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
+import CKEditorError, { logWarning } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
 import Collection from '@ckeditor/ckeditor5-utils/src/collection';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
@@ -172,7 +170,7 @@ export default class FileRepository extends Plugin {
 			 *
 			 * @error filerepository-no-upload-adapter
 			 */
-			console.warn( attachLinkToDocumentation( 'filerepository-no-upload-adapter' ) );
+			logWarning( 'filerepository-no-upload-adapter' );
 
 			return null;
 		}

+ 2 - 1
packages/ckeditor5-upload/tests/filerepository.js

@@ -178,7 +178,8 @@ describe( 'FileRepository', () => {
 			sinon.assert.calledOnce( consoleWarnStub );
 			sinon.assert.calledWithExactly(
 				consoleWarnStub,
-				sinon.match( /^filerepository-no-upload-adapter/ )
+				sinon.match( /^filerepository-no-upload-adapter/ ),
+				sinon.match.string // Link to the documentation
 			);
 		} );
 

+ 62 - 23
packages/ckeditor5-utils/src/ckeditorerror.js

@@ -7,6 +7,8 @@
  * @module utils/ckeditorerror
  */
 
+/* globals console */
+
 /**
  * URL to the documentation with error codes.
  */
@@ -22,9 +24,10 @@ export const DOCUMENTATION_URL =
  * 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.
+ * stop editor initialization (like missing upload adapter, wrong name of a toolbar component) we use
+ * {@link module:utils/ckeditorerror~logWarning `logWarning()`} and
+ * {@link module:utils/ckeditorerror~logError `logError()`}
+ * to improve developers experience and let them see the a 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.
@@ -56,11 +59,7 @@ export default class CKEditorError extends Error {
 	 * data object will also be later available under the {@link #data} property.
 	 */
 	constructor( errorName, context, data ) {
-		let message = attachLinkToDocumentation( errorName );
-
-		if ( data ) {
-			message += ' ' + JSON.stringify( data );
-		}
+		const message = `${ errorName }${ ( data ? ` ${ JSON.stringify( data ) }` : '' ) }${ getLinkToDocumentationMessage( errorName ) }`;
 
 		super( message );
 
@@ -92,11 +91,12 @@ export default class CKEditorError extends Error {
 	}
 
 	/**
-	 * A utility that ensures the the thrown error is a {@link module:utils/ckeditorerror~CKEditorError} one.
+	 * A utility that ensures that the thrown error is a {@link module:utils/ckeditorerror~CKEditorError} one.
 	 * It is useful when combined with the {@link module:watchdog/watchdog~Watchdog} feature, which can restart the editor in case
 	 * of a {@link module:utils/ckeditorerror~CKEditorError} error.
 	 *
-	 * @param {Error} err An error.
+	 * @static
+	 * @param {Error} err The error to rethrow.
 	 * @param {Object} context An object connected through properties with the editor instance. This context will be used
 	 * by the watchdog to verify which editor should be restarted.
 	 */
@@ -125,21 +125,60 @@ export default class CKEditorError extends Error {
 }
 
 /**
- * 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}.
+ * Logs a warning to the console with a properly formatted message and adds a link to the documentation.
+ * Use whenever you want to log a warning to the console.
+ *
+ *		/**
+ *		 * 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.
+ *		 * /
+ *		logWarning( 'toolbarview-item-unavailable', { name } );
+ *
+ * See also {@link module:utils/ckeditorerror~CKEditorError} for an explanation when to throw an error and when to log
+ * a warning or an error to the console.
+ *
+ * @param {String} errorName Error name to be logged.
+ * @param {Object} [data] Additional data to be logged.
+ * @returns {String}
+ */
+export function logWarning( errorName, data ) {
+	console.warn( ...formatConsoleArguments( errorName, data ) );
+}
+
+/**
+ * Logs an error to the console with properly formatted message and adds a link to the documentation.
+ * Use whenever you want to log a error to the console.
+ *
+ *		/**
+ *		 * 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.
+ *		 * /
+ *		 logError( 'toolbarview-item-unavailable', { name } );
  *
- *		 /**
- *		  * 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' ), { name } );
+ * **Note**: In most cases logging a warning using {@link module:utils/ckeditorerror~logWarning} is enough.
  *
- * @param {String} errorName Error name to be linked.
+ * See also {@link module:utils/ckeditorerror~CKEditorError} for an explanation when to use each method.
+ *
+ * @param {String} errorName Error name to be logged.
+ * @param {Object} [data] Additional data to be logged.
  * @returns {String}
  */
-export function attachLinkToDocumentation( errorName ) {
-	return errorName + ` Read more: ${ DOCUMENTATION_URL }#error-${ errorName }\n`;
+export function logError( errorName, data ) {
+	console.error( ...formatConsoleArguments( errorName, data ) );
+}
+
+function getLinkToDocumentationMessage( errorName ) {
+	return `\nRead more: ${ DOCUMENTATION_URL }#error-${ errorName }`;
+}
+
+function formatConsoleArguments( errorName, data ) {
+	const documentationMessage = getLinkToDocumentationMessage( errorName );
+
+	return data ? [ errorName, data, documentationMessage ] : [ errorName, documentationMessage ];
 }

+ 71 - 9
packages/ckeditor5-utils/tests/ckeditorerror.js

@@ -5,8 +5,11 @@
 
 /* eslint-disable ckeditor5-rules/ckeditor-error-message */
 
-import { default as CKEditorError, DOCUMENTATION_URL } from '../src/ckeditorerror';
+/* global console */
+
+import { default as CKEditorError, DOCUMENTATION_URL, logError, logWarning } from '../src/ckeditorerror';
 import { expectToThrowCKEditorError } from './_utils/utils';
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 
 describe( 'CKEditorError', () => {
 	it( 'inherits from Error', () => {
@@ -35,7 +38,7 @@ describe( 'CKEditorError', () => {
 
 		expect( error ).to.have.property(
 			'message',
-			`foo Read more: ${ DOCUMENTATION_URL }#error-foo\n {"bar":1}`
+			`foo {"bar":1}\nRead more: ${ DOCUMENTATION_URL }#error-foo`
 		);
 		expect( error ).to.have.property( 'data', data );
 	} );
@@ -64,7 +67,7 @@ describe( 'CKEditorError', () => {
 
 		expect( error ).to.have.property(
 			'message',
-			`foo Read more: ${ DOCUMENTATION_URL }#error-foo\n {"bar":"a","bom":{"x":1},"bim":10}`
+			`foo {"bar":"a","bom":{"x":1},"bim":10}\nRead more: ${ DOCUMENTATION_URL }#error-foo`
 		);
 		expect( error ).to.have.property( 'data', data );
 	} );
@@ -72,18 +75,15 @@ describe( 'CKEditorError', () => {
 	it( 'contains a link which leads to the documentation', () => {
 		const error = new CKEditorError( 'model-schema-no-item', null );
 
-		const errorMessage = 'model-schema-no-item' +
-			` Read more: ${ DOCUMENTATION_URL }#error-model-schema-no-item\n`;
+		const errorMessage = `model-schema-no-item\nRead more: ${ DOCUMENTATION_URL }#error-model-schema-no-item`;
 
 		expect( error ).to.have.property( 'message', errorMessage );
 	} );
 
-	it( 'link to documentation is added before the additional data message', () => {
+	it( 'link to documentation is added after the additional data message', () => {
 		const error = new CKEditorError( 'model-schema-no-item', null, { foo: 1, bar: 2 } );
 
-		const errorMessage = 'model-schema-no-item ' +
-			`Read more: ${ DOCUMENTATION_URL }#error-model-schema-no-item\n ` +
-			'{"foo":1,"bar":2}';
+		const errorMessage = `model-schema-no-item {"foo":1,"bar":2}\nRead more: ${ DOCUMENTATION_URL }#error-model-schema-no-item`;
 
 		expect( error ).to.have.property( 'message', errorMessage );
 	} );
@@ -117,4 +117,66 @@ describe( 'CKEditorError', () => {
 			}, /foo/, context );
 		} );
 	} );
+
+	describe( 'logWarning()', () => {
+		beforeEach( () => {
+			testUtils.sinon.stub( console, 'warn' );
+		} );
+
+		afterEach( () => {
+			console.warn.restore();
+		} );
+
+		it( 'should log warning with data and link to the documentation', () => {
+			logWarning( 'foo', { name: 'foo' } );
+
+			sinon.assert.calledOnce( console.warn );
+			sinon.assert.calledWithExactly( console.warn,
+				sinon.match( 'foo' ),
+				{ name: 'foo' },
+				`\nRead more: ${ DOCUMENTATION_URL }#error-foo`
+			);
+		} );
+
+		it( 'should log warning without data and with a link to the documentation', () => {
+			logWarning( 'foo' );
+
+			sinon.assert.calledOnce( console.warn );
+			sinon.assert.calledWithExactly( console.warn,
+				sinon.match( 'foo' ),
+				`\nRead more: ${ DOCUMENTATION_URL }#error-foo`
+			);
+		} );
+	} );
+
+	describe( 'logError()', () => {
+		beforeEach( () => {
+			testUtils.sinon.stub( console, 'error' );
+		} );
+
+		afterEach( () => {
+			console.error.restore();
+		} );
+
+		it( 'should log error with data and link to the documentation', () => {
+			logError( 'foo', { name: 'foo' } );
+
+			sinon.assert.calledOnce( console.error );
+			sinon.assert.calledWithExactly( console.error,
+				sinon.match( 'foo' ),
+				{ name: 'foo' },
+				`\nRead more: ${ DOCUMENTATION_URL }#error-foo`
+			);
+		} );
+
+		it( 'should log error without data and with a link to the documentation', () => {
+			logError( 'foo' );
+
+			sinon.assert.calledOnce( console.error );
+			sinon.assert.calledWithExactly( console.error,
+				sinon.match( 'foo' ),
+				`\nRead more: ${ DOCUMENTATION_URL }#error-foo`
+			);
+		} );
+	} );
 } );

+ 2 - 6
packages/ckeditor5-widget/src/widgettoolbarrepository.js

@@ -7,8 +7,6 @@
  * @module widget/widgettoolbarrepository
  */
 
-/* global console */
-
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import ContextualBalloon from '@ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon';
 import ToolbarView from '@ckeditor/ckeditor5-ui/src/toolbar/toolbarview';
@@ -17,7 +15,7 @@ import {
 	isWidget,
 	centeredBalloonPositionForLongWidgets
 } from './utils';
-import CKEditorError, { attachLinkToDocumentation } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
+import CKEditorError, { logWarning } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 /**
  * Widget toolbar repository plugin. A central point for registering widget toolbars. This plugin handles the whole
@@ -134,9 +132,7 @@ export default class WidgetToolbarRepository extends Plugin {
 			 *
 			 * @error widget-toolbar-no-items
 			 */
-			console.warn(
-				attachLinkToDocumentation( 'widget-toolbar-no-items' ), { toolbarId }
-			);
+			logWarning( 'widget-toolbar-no-items', { toolbarId } );
 
 			return;
 		}