UNPKG

test-scribe

Version:

Your sidekick for analog machine development. Run machines interactively and generate automated test scripts based on the outcome.

149 lines (121 loc) 5.47 kB
/** * Wire up an angular module. * * @required {[type]} moduleName [description] * @required {[type]} def [description] * @optional {[type]} angular [description] * @optional {[type]} _ [description] */ function wireUp (moduleName, def, angular, _){ // Locate angular and lodash. if (typeof _ === 'undefined') { _ = window._; } if (typeof angular === 'undefined') { angular = window.angular; } // Validate if (!_.isString(moduleName)) { throw new Error ('`moduleName` is a required string. Should be a valid angular module name.'); } if (!_.isObject(def)) { throw new Error('Invalid definition.'); } // Handle dependencies var angularModuleDependencyNames; if (!_.isUndefined(def.dependencies)) { if (!_.isArray(def.dependencies)) { throw new Error('`dependencies` must be an array of strings; the names of other Angular modules this one depends on (this is where you put your `ngRoute` and your `negotiateKeyboardEvent` and your `ngTouch` and such).'); } angularModuleDependencyNames = _.cloneDeep(def.dependencies||[]); } // Set up the module angular.module(moduleName, angularModuleDependencyNames); var angularModule = angular.module(moduleName); // Handle afterRender var afterRenderFn = function (){}; if (!_.isUndefined(def.afterRender)) { if (!_.isFunction(def.afterRender)) { throw new Error('`afterRender` must be a function with `imports` (an object) as its first and only argument.'); } afterRenderFn = def.afterRender; } // Handle imports var angularProviderNames = []; if (!_.isUndefined(def.imports)) { if (!_.isArray(def.imports)) { throw new Error('`imports` must be an array of strings; the names of Angular providers to import (you know, for that Angular-compatible AMD definition thing).'); } angularProviderNames = angularProviderNames.concat(def.imports); } // TODO: if $scope is not provided as an import, cannot set `variables` or `functions` // Handle variables var scopeVars = {}; if (!_.isUndefined(def.variables)) { if (!_.isObject(def.variables)) { throw new Error('`variables` must be an object of example values for $scope variables.'); } // TODO: coerce/sanitize each initial variable _.extend(scopeVars, def.variables); } // Handle functions var scopeFnDefs = {}; if (!_.isUndefined(def.functions)) { if (!_.isObject(def.functions)) { throw new Error('`functions` must be an object of functions to attach to the $scope.'); } _.extend(scopeFnDefs, def.functions); } // Set up controller // // (TODO: at some point, make this so that you don't have to manually attach the // controller-- it should be automatically attached when you specify your ng-app.) angularModule.controller('afterRender', angularProviderNames.concat(function () { // TODO: should prbly do this on a method-by-method basis instead of doing it once // Build `imports` by grabbing arguments and munging it with `angularProviderNames` var imports = _.reduce(Array.prototype.slice.call(arguments), function (imports, instantiatedProvider, i){ var providerName = angularProviderNames[i]; imports[providerName] = instantiatedProvider; return imports; }, {}); // Always provide access to `_`, `$`, and `angular` (if they're not already there) imports.angular = _.isUndefined(imports.angular) ? angular : imports.angular; imports.$ = _.isUndefined(imports.angular.element) ? angular : imports.angular.element; imports._ = _.isUndefined(imports._) ? _ : imports._; if (imports.$scope) { // hack (expose $scope on window for convenience during development) SCOPE = imports.$scope; // Bind initial values to scope _.each(scopeVars, function (exampleVal, name){ // TODO: get base value for scope var examples and bind that to scope. // var initialVal = rttc.coerce(exampleVal); // imports.$scope[name] = initialVal; // For now, just take care of the most critical stuff. if (_.isArray(exampleVal)) { imports.$scope[name] = []; } else if (_.isObject(exampleVal)) { imports.$scope[name] = {}; } }); // Bind declared functions to scope _.each(scopeFnDefs, function (fnDef, name){ // TODO: validate each function // TODO: allow definition to be EITHER a machine def OR a simple function // TODO: if it's a machine def, make it callable as a function // args: ['identity'] // ^^^ todo: allow something like this to be passed in machine def to facilitate easier usage from angular HTML templates. Otherwise always expects a single argument-- "inputs". // Intercept function and attach to scope imports.$scope[name] = function interceptScopeFn(){ var inputs = Array.prototype.slice.call(arguments)[0] || {}; return fnDef(inputs, imports); }; // Note for fn implementation: // imports.setScope('currentMachine.isDefined', true); <== consider sending in something like that instead of a direct reference to $scope. // var isDefined = imports.getScope('currentMachine.isDefined'); <== consider sending in something like that instead of a direct reference to $scope. }); } // Run `afterRender` function afterRenderFn(imports); })); }