8
0
Просмотр исходного кода

Refactored configuration class.

Oskar Wrobel 9 лет назад
Родитель
Сommit
a348824b0b
2 измененных файлов с 302 добавлено и 181 удалено
  1. 118 82
      packages/ckeditor5-utils/src/config.js
  2. 184 99
      packages/ckeditor5-utils/tests/config.js

+ 118 - 82
packages/ckeditor5-utils/src/config.js

@@ -5,18 +5,16 @@
 
 
 'use strict';
 'use strict';
 
 
-import ObservableMixin from './observablemixin.js';
 import isObject from './lib/lodash/isObject.js';
 import isObject from './lib/lodash/isObject.js';
 import isPlainObject from './lib/lodash/isPlainObject.js';
 import isPlainObject from './lib/lodash/isPlainObject.js';
-import mix from './mix.js';
 
 
 /**
 /**
  * Handles a configuration dictionary.
  * Handles a configuration dictionary.
  *
  *
  * @memberOf utils
  * @memberOf utils
- * @mixes utils.ObservableMixin
  */
  */
 export default class Config {
 export default class Config {
+
 	/**
 	/**
 	 * Creates an instance of the {@link Config} class.
 	 * Creates an instance of the {@link Config} class.
 	 *
 	 *
@@ -24,8 +22,16 @@ export default class Config {
 	 */
 	 */
 	constructor( configurations ) {
 	constructor( configurations ) {
 		if ( configurations ) {
 		if ( configurations ) {
-			this.set( configurations );
+			this._set( configurations );
 		}
 		}
+
+		/**
+		 * Store for whole configuration. Configuration is hide it private property to be not accessible
+		 * directly from Config instance to keep consistent API.
+		 *
+		 * @private
+		 * @member {ConfigSubset} utils.config#_config
+		 */
 	}
 	}
 
 
 	/**
 	/**
@@ -66,10 +72,81 @@ export default class Config {
 	 * @param {*} [value=null] The configuration value. Used if a name is passed to nameOrConfigurations.
 	 * @param {*} [value=null] The configuration value. Used if a name is passed to nameOrConfigurations.
 	 */
 	 */
 	set( name, value ) {
 	set( name, value ) {
-		// Just pass the call to the original set() in case of an object. It'll deal with recursing through the
-		// object and calling set( name, value ) again for each property.
+		this._set( name, value );
+	}
+
+	/**
+	 * Does exactly the same as {@link utils.Config#set} with one exception - passed configuration extends
+	 * existing one, but does not overwrite already defined values.
+	 *
+	 * @param {String|Object} name The configuration name or an object from which take properties as
+	 * configuration entries. Configuration names are case-insensitive.
+	 * @param {*} [value=null] The configuration value. Used if a name is passed to nameOrConfigurations.
+	 */
+	define( name, value ) {
+		this._set( name, value, true );
+	}
+
+	/**
+	 * Gets the value for a configuration entry.
+	 *
+	 *		config.get( 'name' );
+	 *
+	 * Deep configurations can be retrieved by separating each part with a dot.
+	 *
+	 *		config.get( 'toolbar.collapsed' );
+	 *
+	 * @param {String} name The configuration name. Configuration names are case-insensitive.
+	 * @returns {*} The configuration value or `undefined` if the configuration entry was not found.
+	 */
+	get( name ) {
+		// The target for this configuration is, for now, this object.
+		let source = this;
+
+		// Whole configuration is stored in private property.
+		if ( !( source instanceof ConfigSubset ) ) {
+			// Configuration is empty.
+			if ( !source._config ) {
+				return;
+			}
+
+			source = source._config;
+		}
+
+		// The configuration name should be split into parts if it has dots. E.g. `resize.width` -> [`resize`, `width`].
+		const parts = name.toLowerCase().split( '.' );
+
+		// Take the name of the configuration out of the parts. E.g. `resize.width` -> `width`.
+		name = parts.pop();
+
+		// Retrieves the source for this configuration recursively.
+		for ( let i = 0; i < parts.length; i++ ) {
+			// The target will always be an instance of Config.
+			if ( !( source[ parts[ i ] ] instanceof ConfigSubset ) ) {
+				source = null;
+				break;
+			}
+
+			source = source[ parts[ i ] ];
+		}
+
+		// Always returns undefined for non existing configuration
+		return source ? source[ name ] : undefined;
+	}
+
+	/**
+	 * Converts and saves passed configuration.
+	 *
+	 * @private
+	 * @param {String|Object} name The configuration name or an object from which take properties as
+	 * configuration entries. Configuration names are case-insensitive.
+	 * @param {*} [value=null] The configuration value. Used if a name is passed to nameOrConfigurations.
+	 * @param {Boolean} [isDefine=false] Define if passed configuration should overwrite existing one.
+	 */
+	_set( name, value, isDefine ) {
+		// In case of an object, iterate through it and call set( name, value ) again for each property.
 		if ( isObject( name ) ) {
 		if ( isObject( name ) ) {
-			ObservableMixin.set.apply( this, arguments );
+			this._setObject( name, isDefine );
 
 
 			return;
 			return;
 		}
 		}
@@ -77,6 +154,16 @@ export default class Config {
 		// The target for this configuration is, for now, this object.
 		// The target for this configuration is, for now, this object.
 		let target = this;
 		let target = this;
 
 
+		// If we are at the top of the configuration tree, hide configuration in private property
+		// to prevent of getting properties directly from config, to keep consistent API.
+		if ( !( target instanceof ConfigSubset ) ) {
+			if ( !target._config ) {
+				target._config = new ConfigSubset();
+			}
+
+			target = target._config;
+		}
+
 		// The configuration name should be split into parts if it has dots. E.g: `resize.width`.
 		// The configuration name should be split into parts if it has dots. E.g: `resize.width`.
 		const parts = name.toLowerCase().split( '.' );
 		const parts = name.toLowerCase().split( '.' );
 
 
@@ -86,8 +173,8 @@ export default class Config {
 		// Retrieves the final target for this configuration recursively.
 		// Retrieves the final target for this configuration recursively.
 		for ( let i = 0; i < parts.length; i++ ) {
 		for ( let i = 0; i < parts.length; i++ ) {
 			// The target will always be an instance of Config.
 			// The target will always be an instance of Config.
-			if ( !( target[ parts[ i ] ] instanceof Config ) ) {
-				target.set( parts[ i ], new Config() );
+			if ( !( target[ parts[ i ] ] instanceof ConfigSubset ) ) {
+				target.set( parts[ i ], new ConfigSubset() );
 			}
 			}
 
 
 			target = target[ parts[ i ] ];
 			target = target[ parts[ i ] ];
@@ -96,14 +183,20 @@ export default class Config {
 		// Values set as pure objects will be treated as Config subsets.
 		// Values set as pure objects will be treated as Config subsets.
 		if ( isPlainObject( value ) ) {
 		if ( isPlainObject( value ) ) {
 			// If the target is an instance of Config (a deep config subset).
 			// If the target is an instance of Config (a deep config subset).
-			if ( target[ name ] instanceof Config ) {
+			if ( target[ name ] instanceof ConfigSubset ) {
 				// Amend the target with the value, instead of replacing it.
 				// Amend the target with the value, instead of replacing it.
-				target[ name ].set( value );
+				target[ name ]._setObject( value, isDefine );
 
 
 				return;
 				return;
 			}
 			}
 
 
-			value = new Config( value );
+			value = new ConfigSubset( value );
+		}
+
+		// Do nothing if there is already defined configuration for this name
+		// and configuration is set as default.
+		if ( isDefine && typeof target[ name ] != 'undefined' ) {
+			return;
 		}
 		}
 
 
 		// Values will never be undefined.
 		// Values will never be undefined.
@@ -111,81 +204,24 @@ export default class Config {
 			value = null;
 			value = null;
 		}
 		}
 
 
-		// Call the original set() on the target.
-		ObservableMixin.set.call( target, name, value );
+		target[ name ] = value;
 	}
 	}
 
 
 	/**
 	/**
-	 * Gets the value for a configuration entry.
-	 *
-	 *		config.get( 'name' );
-	 *
-	 * Deep configurations can be retrieved by separating each part with a dot.
+	 * Iterate through passed object and call set method with object key and value for each property.
 	 *
 	 *
-	 *		config.get( 'toolbar.collapsed' );
-	 *
-	 * @param {String} name The configuration name. Configuration names are case-insensitive.
-	 * @returns {*} The configuration value or `undefined` if the configuration entry was not found.
+	 * @private
+	 * @param {Object} configuration Configuration data set
+	 * @param {Boolean} isDefine Defines if passed configuration is default configuration or not
 	 */
 	 */
-	get( name ) {
-		// The target for this configuration is, for now, this object.
-		let source = this;
-
-		// The configuration name should be split into parts if it has dots. E.g. `resize.width` -> [`resize`, `width`]
-		const parts = name.toLowerCase().split( '.' );
-
-		// Take the name of the configuration from the parts. E.g. `resize.width` -> `width`
-		name = parts.pop();
-
-		// Retrieves the source for this configuration recursively.
-		for ( let i = 0; i < parts.length; i++ ) {
-			// The target will always be an instance of Config.
-			if ( !( source[ parts[ i ] ] instanceof Config ) ) {
-				source = null;
-				break;
-			}
-
-			source = source[ parts[ i ] ];
-		}
-
-		// Try to retrieve it from the source object.
-		if ( source && ( typeof source[ name ] != 'undefined' ) ) {
-			return source[ name ];
-		}
-
-		// If not found, take it from the definition.
-		if ( this.definition ) {
-			return this.definition[ name ];
-		}
-	}
-
-	/**
-	 * Defines the name and default value for configurations. It accepts the same parameters as the
-	 * {@link Config#set set()} method.
-	 *
-	 * On first call, the {@link Config#definition definition} property is created to hold all defined
-	 * configurations.
-	 *
-	 * This method is supposed to be called by plugin developers to setup plugin's configurations. It would be
-	 * rarely used for other needs.
-	 *
-	 * @param {String|Object} name The configuration name or an object from which take properties as
-	 * configuration entries.
-	 * @param {*} [value] The configuration value. Used if a name is passed to nameOrConfigurations. If undefined,
-	 * the configuration is set to `null`.
-	 */
-	define( name, value ) {
-		if ( !this.definition ) {
-			/**
-			 * TODO
-			 *
-			 * @type {Config}
-			 */
-			this.definition = new Config();
-		}
-
-		this.definition.set( name, value );
+	_setObject( configuration, isDefine ) {
+		Object.keys( configuration ).forEach( ( key ) => {
+			this._set( key, configuration[ key ], isDefine );
+		}, this );
 	}
 	}
 }
 }
 
 
-mix( Config, ObservableMixin );
+/**
+ * Helper class to recognize if current configuration is nested or the top.
+ */
+class ConfigSubset extends Config {}

+ 184 - 99
packages/ckeditor5-utils/tests/config.js

@@ -26,13 +26,16 @@ beforeEach( () => {
 
 
 describe( 'constructor', () => {
 describe( 'constructor', () => {
 	it( 'should set configurations', () => {
 	it( 'should set configurations', () => {
-		expect( config ).to.have.property( 'creator' ).to.equal( 'inline' );
-		expect( config ).to.have.property( 'language' ).to.equal( 'pl' );
-		expect( config ).to.have.property( 'resize' ).to.have.property( 'minheight' ).to.equal( 300 );
-		expect( config ).to.have.property( 'resize' ).to.have.property( 'maxheight' ).to.equal( 800 );
-		expect( config ).to.have.property( 'resize' ).to.have.property( 'icon' )
-			.to.have.property( 'path' ).to.equal( 'xyz' );
-		expect( config ).to.have.property( 'toolbar' ).to.equal( 'top' );
+		expect( config.get( 'creator' ) ).to.equal( 'inline' );
+		expect( config.get( 'language' ) ).to.equal( 'pl' );
+		expect( config.get( 'resize' ) ).to.eql( {
+			minheight: 300,
+			maxheight: 800,
+			icon: {
+				path: 'xyz'
+			}
+		} );
+		expect( config.get( 'toolbar' ) ).to.equal( 'top' );
 	} );
 	} );
 
 
 	it( 'should work with no parameters', () => {
 	it( 'should work with no parameters', () => {
@@ -43,8 +46,8 @@ describe( 'constructor', () => {
 
 
 describe( 'set', () => {
 describe( 'set', () => {
 	it( 'should create Config instances for objects', () => {
 	it( 'should create Config instances for objects', () => {
-		expect( config.resize ).to.be.an.instanceof( Config );
-		expect( config.resize.icon ).to.be.an.instanceof( Config );
+		expect( config.get( 'resize' ) ).to.be.an.instanceof( Config );
+		expect( config.get( 'resize.icon' ) ).to.be.an.instanceof( Config );
 	} );
 	} );
 
 
 	it( 'should set configurations when passing objects', () => {
 	it( 'should set configurations when passing objects', () => {
@@ -55,32 +58,53 @@ describe( 'set', () => {
 			}
 			}
 		} );
 		} );
 
 
-		expect( config )
-			.to.have.property( 'option1' ).to.equal( 1 );
-
-		expect( config )
-			.to.have.property( 'option2' )
-			.to.have.property( 'suboption21' ).to.equal( 21 );
+		expect( config.get( 'option1' ) ).to.equal( 1 );
+		expect( config.get( 'option2.suboption21' ) ).to.equal( 21 );
 	} );
 	} );
 
 
 	it( 'should set configurations when passing name and value', () => {
 	it( 'should set configurations when passing name and value', () => {
 		config.set( 'something', 'anything' );
 		config.set( 'something', 'anything' );
 
 
-		expect( config ).to.have.property( 'something' ).to.equal( 'anything' );
+		expect( config.get( 'something' ) ).to.equal( 'anything' );
 	} );
 	} );
 
 
 	it( 'should set configurations when passing name.with.deep and value', () => {
 	it( 'should set configurations when passing name.with.deep and value', () => {
 		config.set( 'color.red', 'f00' );
 		config.set( 'color.red', 'f00' );
 		config.set( 'background.color.blue', '00f' );
 		config.set( 'background.color.blue', '00f' );
 
 
-		expect( config )
-			.to.have.property( 'color' )
-			.to.have.property( 'red' ).to.equal( 'f00' );
+		expect( config.get( 'color.red' ) ).to.equal( 'f00' );
+		expect( config.get( 'background.color.blue' ) ).to.equal( '00f' );
+	} );
 
 
-		expect( config )
-			.to.have.property( 'background' )
-			.to.have.property( 'color' )
-			.to.have.property( 'blue' ).to.equal( '00f' );
+	it( 'should replace a simple entry with a Config instance', () => {
+		config.set( 'test', 1 );
+		config.set( 'test', {
+			prop: 1
+		} );
+
+		expect( config.get( 'test' ) ).to.be.an.instanceof( Config );
+		expect( config.get( 'test.prop' ) ).to.equal( 1 );
+	} );
+
+	it( 'should replace a simple entry with a Config instance when passing an object', () => {
+		config.set( 'test', 1 );
+		config.set( {
+			test: {
+				prop: 1
+			}
+		} );
+
+		expect( config.get( 'test' ) ).to.be.an.instanceof( Config );
+		expect( config.get( 'test.prop' ) ).to.equal( 1 );
+	} );
+
+	it( 'should replace a simple entry with a Config instance when passing a name.with.deep', () => {
+		config.set( 'test.prop', 1 );
+		config.set( 'test.prop.value', 1 );
+
+		expect( config.get( 'test' ) ).to.be.an.instanceof( Config );
+		expect( config.get( 'test.prop' ) ).to.be.an.instanceof( Config );
+		expect( config.get( 'test.prop.value' ) ).to.equal( 1 );
 	} );
 	} );
 
 
 	it( 'should override and expand deep configurations', () => {
 	it( 'should override and expand deep configurations', () => {
@@ -95,64 +119,154 @@ describe( 'set', () => {
 			}
 			}
 		} );
 		} );
 
 
-		expect( config ).to.have.property( 'resize' );
-		expect( config.resize ).to.have.property( 'minheight' ).to.equal( 400 );
-		expect( config.resize ).to.have.property( 'maxheight' ).to.equal( 800 );	// Not touched
-		expect( config.resize ).to.have.property( 'hidden' ).to.equal( true );
+		expect( config.get( 'resize' ) ).to.be.eql( {
+			minheight: 400,		// Overridden
+			maxheight: 800,		// The same
+			hidden: true,		// Expanded
+			icon: {
+				path: 'abc',	// Overridden
+				url: true		// Expanded
+			}
+		} );
+	} );
+
+	it( 'should override and expand Config instance when passing an object', () => {
+		config.set( 'resize', {
+			minHeight: 400,		// Override
+			hidden: true,		// Expand
+			icon: {
+				path: 'abc',	// Override
+				url: true		// Expand
+			}
+		} );
 
 
-		expect( config.resize ).to.have.property( 'icon' );
-		expect( config.resize.icon ).to.have.property( 'path' ).to.equal( 'abc' );
-		expect( config.resize.icon ).to.have.property( 'url' ).to.equal( true );
+		expect( config.get( 'resize' ) ).to.be.eql( {
+			minheight: 400,		// Overridden
+			maxheight: 800,		// The same
+			hidden: true,		// Expanded
+			icon: {
+				path: 'abc',	// Overridden
+				url: true		// Expanded
+			}
+		} );
 	} );
 	} );
 
 
-	it( 'should replace a simple entry with a Config instance', () => {
-		config.set( 'test', 1 );
-		config.set( 'test', {
-			prop: 1
+	it( 'should not create Config instances for non-pure objects', () => {
+		function SomeClass() {}
+
+		config.set( 'date', new Date() );
+		config.set( {
+			instance: new SomeClass()
 		} );
 		} );
 
 
-		expect( config.test ).to.be.an.instanceof( Config );
-		expect( config.test.prop ).to.equal( 1 );
+		expect( config.get( 'date' ) ).to.be.an.instanceof( Date );
+		expect( config.get( 'instance' ) ).to.be.an.instanceof( SomeClass );
 	} );
 	} );
 
 
-	it( 'should replace a simple entry with a Config instance when passing an object', () => {
-		config.set( 'test', 1 );
+	it( 'should set `null` for undefined value', () => {
+		config.set( 'test' );
+
+		expect( config.get( 'test' ) ).to.be.null();
+	} );
+} );
+
+describe( 'define', () => {
+	it( 'should set configurations when passing objects', () => {
 		config.set( {
 		config.set( {
-			test: {
-				prop: 1
+			option1: 1,
+			option2: {
+				subOption21: 21
 			}
 			}
 		} );
 		} );
 
 
-		expect( config.test ).to.be.an.instanceof( Config );
-		expect( config.test.prop ).to.equal( 1 );
+		expect( config.get( 'option1' ) ).to.equal( 1 );
+		expect( config.get( 'option2.suboption21' ) ).to.equal( 21 );
 	} );
 	} );
 
 
-	it( 'should replace a simple entry with a Config instance when passing a name.with.deep', () => {
-		config.set( 'test.prop', 1 );
-		config.set( 'test.prop.value', 1 );
+	it( 'should set configurations when passing name and value', () => {
+		config.set( 'something', 'anything' );
 
 
-		expect( config.test ).to.be.an.instanceof( Config );
-		expect( config.test.prop ).to.be.an.instanceof( Config );
-		expect( config.test.prop.value ).to.equal( 1 );
+		expect( config.get( 'something' ) ).to.equal( 'anything' );
+	} );
+
+	it( 'should set configurations when passing name.with.deep and value', () => {
+		config.set( 'color.red', 'f00' );
+		config.set( 'background.color.blue', '00f' );
+
+		expect( config.get( 'color.red' ) ).to.equal( 'f00' );
+		expect( config.get( 'background.color.blue' ) ).to.equal( '00f' );
+	} );
+
+	it( 'should not replace already defined values', () => {
+		config.define( 'language', 'en' );
+		config.define( 'resize.minHeight', 400 );
+		config.define( 'resize.icon', 'some value' );
+
+		expect( config.get( 'language' ) ).to.equal( 'pl' );
+		expect( config.get( 'resize.icon' ) ).to.be.instanceof( Config );
+		expect( config.get( 'resize.minheight' ) ).to.equal( 300 );
+	} );
+
+	it( 'should expand but not override deep configurations', () => {
+		config.define( {
+			resize: {
+				minHeight: 400,		// Override
+				hidden: true,		// Expand
+				icon: {
+					path: 'abc',	// Override
+					url: true		// Expand
+				}
+			}
+		} );
+
+		expect( config.get( 'resize' ) ).to.be.eql( {
+			minheight: 300,		// The same
+			maxheight: 800,		// The same
+			hidden: true,		// Expanded
+			icon: {
+				path: 'xyz',	// The same
+				url: true		// Expanded
+			}
+		} );
+	} );
+
+	it( 'should expand but not override Config instance when passing an object', () => {
+		config.define( 'resize', {
+			minHeight: 400,		// Override
+			hidden: true,		// Expand
+			icon: {
+				path: 'abc',	// Override
+				url: true		// Expand
+			}
+		} );
+
+		expect( config.get( 'resize' ) ).to.be.eql( {
+			minheight: 300,		// The same
+			maxheight: 800,		// The same
+			hidden: true,		// Expanded
+			icon: {
+				path: 'xyz',	// The same
+				url: true		// Expanded
+			}
+		} );
 	} );
 	} );
 
 
 	it( 'should not create Config instances for non-pure objects', () => {
 	it( 'should not create Config instances for non-pure objects', () => {
 		function SomeClass() {}
 		function SomeClass() {}
 
 
-		config.set( 'date', new Date() );
-		config.set( {
+		config.define( 'date', new Date() );
+		config.define( {
 			instance: new SomeClass()
 			instance: new SomeClass()
 		} );
 		} );
 
 
-		expect( config.date ).to.be.an.instanceof( Date );
-		expect( config.instance ).to.be.an.instanceof( SomeClass );
+		expect( config.get( 'date' ) ).to.be.an.instanceof( Date );
+		expect( config.get( 'instance' ) ).to.be.an.instanceof( SomeClass );
 	} );
 	} );
 
 
 	it( 'should set `null` for undefined value', () => {
 	it( 'should set `null` for undefined value', () => {
-		config.set( 'test' );
+		config.define( 'test' );
 
 
-		expect( config.test ).to.be.null;
-		expect( config.get( 'test' ) ).to.be.null;
+		expect( config.get( 'test' ) ).to.be.null();
 	} );
 	} );
 } );
 } );
 
 
@@ -169,13 +283,13 @@ describe( 'get', () => {
 	it( 'should retrieve a subset of the configuration', () => {
 	it( 'should retrieve a subset of the configuration', () => {
 		let resizeConfig = config.get( 'resize' );
 		let resizeConfig = config.get( 'resize' );
 
 
-		expect( resizeConfig ).to.have.property( 'minheight' ).to.equal( 300 );
-		expect( resizeConfig ).to.have.property( 'maxheight' ).to.equal( 800 );
-		expect( resizeConfig ).to.have.property( 'icon' ).to.have.property( 'path' ).to.equal( 'xyz' );
+		expect( resizeConfig.get( 'minheight' ) ).equal( 300 );
+		expect( resizeConfig.get( 'maxheight' ) ).to.equal( 800 );
+		expect( resizeConfig.get( 'icon' ) ).to.be.instanceof( Config );
 
 
 		let iconConfig = resizeConfig.get( 'icon' );
 		let iconConfig = resizeConfig.get( 'icon' );
 
 
-		expect( iconConfig ).to.have.property( 'path' ).to.equal( 'xyz' );
+		expect( iconConfig.get( 'path' ) ).to.equal( 'xyz' );
 	} );
 	} );
 
 
 	it( 'should retrieve values case-insensitively', () => {
 	it( 'should retrieve values case-insensitively', () => {
@@ -189,50 +303,21 @@ describe( 'get', () => {
 		expect( config.get( 'invalid' ) ).to.be.undefined;
 		expect( config.get( 'invalid' ) ).to.be.undefined;
 	} );
 	} );
 
 
-	it( 'should return undefined for non existing deep configuration', () => {
-		expect( config.get( 'resize.invalid.value' ) ).to.be.undefined;
-	} );
-} );
-
-describe( 'define', () => {
-	it( 'should create the definition property', () => {
-		expect( config ).to.not.have.property( 'definition' );
-
-		config.define( 'test', 1 );
-
-		expect( config ).to.have.property( 'definition' );
-	} );
-
-	it( 'should set configurations in the definition property', () => {
-		config.define( 'test1', 1 );
-
-		// This is for Code Coverage to ensure that it works when `definition` is already defined.
-		config.define( 'test2', 2 );
-
-		expect( config.definition ).to.have.property( 'test1' ).to.equal( 1 );
-		expect( config.definition ).to.have.property( 'test2' ).to.equal( 2 );
-	} );
-
-	it( 'should set configurations passed as object in the definition property', () => {
-		config.define( {
-			test: 1
-		} );
+	it( 'should return undefined for empty configuration', () => {
+		config = new Config();
 
 
-		expect( config.definition ).to.have.property( 'test' ).to.equal( 1 );
+		expect( config.get( 'invalid' ) ).to.be.undefined;
+		expect( config.get( 'deep.invalid' ) ).to.be.undefined;
 	} );
 	} );
 
 
-	it( 'should not define main config properties but still be retrieved with get()', () => {
-		config.define( 'test', 1 );
-
-		expect( config ).to.not.have.property( 'test' );
-		expect( config.get( 'test' ) ).to.equal( 1 );
+	it( 'should return undefined for non existing deep configuration', () => {
+		expect( config.get( 'resize.invalid.value' ) ).to.be.undefined;
 	} );
 	} );
 
 
-	it( 'should be overridden by set()', () => {
-		config.define( 'test', 1 );
-		config.set( 'test', 2 );
-
-		expect( config ).to.have.property( 'test' ).to.equal( 2 );
-		expect( config.get( 'test' ) ).to.equal( 2 );
+	it( 'should not be possible to retrieve value directly from config object', () => {
+		expect( config.creator ).to.be.undefined;
+		expect( () => {
+			config.resize.maxheight;
+		} ).to.throw();
 	} );
 	} );
 } );
 } );