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

Added module test utilities for CommonJS format.

Szymon Kupś 10 лет назад
Родитель
Сommit
f4570c16c7
2 измененных файлов с 224 добавлено и 0 удалено
  1. 121 0
      tests/_utils/module__cjs.js
  2. 103 0
      tests/utils-tests/module__cjs.js

+ 121 - 0
tests/_utils/module__cjs.js

@@ -0,0 +1,121 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals require, process */
+
+'use strict';
+
+const mockery = require( 'mockery' );
+mockery.enable( {
+	warnOnReplace: false,
+	warnOnUnregistered: false
+} );
+const mocked = [];
+
+const path = require( 'path' );
+
+/**
+ * CommonJS tools related to CKEditor.
+ */
+const utils = {
+	/**
+	 * Helper for generating an absolute module path from a simplified name.
+	 *
+	 * Transforms:
+	 *
+	 * * `foo` -> `/path/dist/cjs/ckeditor5/foo/foo.js`
+	 * * `foo/bar` -> `/path/dist/cjs/ckeditor5/foo/bar.js`
+	 * * `/ckeditor5/foo.js` -> `/path/dist/cjs/ckeditor5/foo.js`
+	 *
+	 * @param {String} modulePath The simplified path.
+	 * @returns {String} The real path.
+	 */
+	getModulePath( modulePath ) {
+		// Do nothing – path is already absolute.
+		if ( modulePath.startsWith( '/' ) ) {
+			return path.join( process.cwd(), 'dist', 'cjs', modulePath );
+		}
+
+		if ( modulePath.indexOf( '/' ) < 0 ) {
+			modulePath = modulePath + '/' + modulePath;
+		}
+
+		return path.join( process.cwd(), 'dist', 'cjs', 'ckeditor5', modulePath + '.js' );
+	},
+
+	/**
+	 * Defines module in AMD style using CommonJS modules.
+	 *
+	 * This method uses {@link #getModulePath} to process module and dependency paths so you need to use
+	 * the simplified notation.
+	 *
+	 * For simplicity the dependencies passed to the `body` will be unwrapped
+	 * from the ES6 module object (so only the default export will be available). Also the returned value
+	 * will be automatically handled as a default export.
+	 *
+	 * See also module__amd.js file description.
+	 *
+	 * @param {String} path Path to the module.
+	 * @param {String[]} deps Dependencies of the module.
+	 * @param {Function} body Module body.
+	 */
+	define( path, deps, body ) {
+		if ( arguments.length == 2 ) {
+			body = deps;
+			deps = [];
+		}
+
+		deps = deps
+			.map( ( dependency ) => utils.getModulePath( dependency ) )
+			.map( ( dependency )  => {
+				// Checking if module is already mocked - if module was not mocked it might be a real module.
+				// Using require.resolve to check if module really exists without loading it ( it throws if module is
+				// not present). When module is not mocked and does not exist it will be undefined in module body.
+				try {
+					if ( mocked.indexOf( dependency ) < 0 ) {
+						// Test if required module exists without loading it.
+						require.resolve( dependency );
+					}
+				} catch ( e ) {
+					return;
+				}
+
+				// Return only default export.
+				return require( dependency ).default;
+			} );
+
+		mocked.push( utils.getModulePath( path ) );
+		mockery.registerMock( utils.getModulePath( path ), {
+			default: body.apply( this, deps )
+		} );
+	},
+
+	/**
+	 * Gets an object which holds the CKEditor modules. This function uses synchronous CommonJS `require()`
+	 * method, which means that after executing this method all modules are already loaded.
+	 *
+	 * This method uses {@link #getModulePath} to process module and dependency paths so you need to use
+	 * the simplified notation.
+	 *
+	 *		const modules = amdTestUtils.require( { editor: 'core/Editor' } );
+	 *
+	 *		// Later on, inside tests:
+	 *		const Editor = modules.editor;
+	 *
+	 * @params {Object} modules The object (`ref => modulePath`) with modules to be loaded.
+	 * @returns {Object} The object that will hold the loaded modules.
+	 */
+	require( modules ) {
+		const resolved = {};
+
+		for ( let name in modules ) {
+			resolved[ name ] = require( utils.getModulePath( modules[ name ] ) ).default;
+		}
+
+		return resolved;
+	}
+};
+
+export default utils;

+ 103 - 0
tests/utils-tests/module__cjs.js

@@ -0,0 +1,103 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* global require, process */
+
+'use strict';
+
+import testUtils from '/tests/_utils/utils.js';
+import moduleTestUtils from '/tests/_utils/module.js';
+
+testUtils.createSinonSandbox();
+
+const path = require( 'path' );
+const cjsDir = path.join( process.cwd(), 'dist', 'cjs' );
+
+describe( 'module utilities', () => {
+	const getModulePath = moduleTestUtils.getModulePath;
+
+	describe( 'getModulePath()', () => {
+		it( 'generates absolute path from a plugin name', () => {
+			const modulePath = getModulePath( 'foo' );
+
+			expect( modulePath ).to.equal( path.join( cjsDir,  '/ckeditor5/foo/foo.js' ) );
+		} );
+
+		it( 'generates an absolute path from a simple path', () => {
+			const modulePath = getModulePath( 'core/editor' );
+
+			expect( modulePath ).to.equal( path.join( cjsDir, '/ckeditor5/core/editor.js' ) );
+		} );
+
+		it( 'does not process an absolute path', () => {
+			const modulePath = getModulePath( '/foo/bar/bom.js' );
+
+			expect( modulePath ).to.equal( path.join( cjsDir, '/foo/bar/bom.js' ) );
+		} );
+	} );
+
+	describe( 'define()', () => {
+		it( 'defines a module using mockery', () => {
+			const mockery = require( 'mockery' );
+			const spy = testUtils.sinon.spy( mockery, 'registerMock' );
+
+			moduleTestUtils.define( 'test1', [ '/ckeditor.js', 'bar' ],  () => {}  );
+
+			expect( spy.calledOnce ).to.be.true;
+			expect( spy.args[ 0 ][ 0 ] ).to.equal( getModulePath( 'test1' ) );
+		} );
+
+		it( 'works with module name and body', () => {
+			const mockery = require( 'mockery' );
+			const spy = testUtils.sinon.spy( mockery, 'registerMock' );
+			const bodySpy = testUtils.sinon.spy( () => {} );
+
+			moduleTestUtils.define( 'test1', bodySpy );
+
+			expect( spy.calledOnce ).to.be.true;
+			expect( spy.args[ 0 ][ 0 ] ).to.equal( getModulePath( 'test1' ) );
+			expect( bodySpy.calledOnce ).to.be.true;
+
+			// No dependencies are passed - check if no arguments were passed to the function.
+			expect( bodySpy.args[ 0 ].length ).to.equal( 0 );
+		} );
+
+		// Note: this test only checks whether CommonJS version of `define()` doesn't totally fail when creating a
+		// circular dependency. The value of dependencies are not available anyway inside the
+		// amdTestUtils.define() callbacks because we lose the late-binding by immediately mapping modules to
+		// their default exports.
+		it( 'works with circular dependencies', () => {
+			moduleTestUtils.define( 'test-circular-a', [ 'test-circular-b' ], () => {
+				return 'a';
+			} );
+
+			moduleTestUtils.define( 'test-circular-b', [ 'test-circular-a' ], () => {
+				return 'b';
+			} );
+
+			const a = require( moduleTestUtils.getModulePath( 'test-circular-a' ) );
+			expect( a ).to.have.property( 'default', 'a' );
+
+			const b = require( moduleTestUtils.getModulePath( 'test-circular-b' ) );
+			expect( b ).to.have.property( 'default', 'b' );
+		} );
+	} );
+
+	describe( 'require', () => {
+		it( 'creates object with loaded modules', () => {
+			// Create first module using mockery library.
+			const mockery = require( 'mockery' );
+			mockery.registerMock( moduleTestUtils.getModulePath( 'module1' ), { default: 'foo' } );
+
+			// Create second module using define.
+			moduleTestUtils.define( 'module2', () => 'bar' );
+
+			const loadedModules = moduleTestUtils.require( { module1: 'module1',  module2: 'module2' } );
+
+			expect( loadedModules.module1 ).to.equal( 'foo' );
+			expect( loadedModules.module2 ).to.equal( 'bar' );
+		} );
+	} );
+} );