Advertisement
Guest User

Untitled

a guest
Sep 22nd, 2013
189
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * testr.js 1.3.2
  3.  * https://www.github.com/mattfysh/testr.js
  4.  * Distributed under the MIT license
  5.  */
  6.  
  7. var testr, define, require;
  8.  
  9. (function() {
  10.     var version = '1.3.2',
  11.         origRequire = require,
  12.         origDefine = define,
  13.         cjsRequireRegExp = /require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
  14.         noop = function() {},
  15.         moduleMap = {},
  16.         pluginPaths = {},
  17.         config = {
  18.             root: './'
  19.         },
  20.         running = false,
  21.         requireCount = 0,
  22.         requireLoadedCount = 0,
  23.         lazy,
  24.         complete;
  25.  
  26.     // type detection
  27.     function isArray(a) {
  28.         return Object.prototype.toString.call(a) == '[object Array]';
  29.     }
  30.     function isObject(o) {
  31.         return typeof o === 'object' && !isArray(o);
  32.     }
  33.  
  34.     // deep copy
  35.     function deepCopy(src) {
  36.         var tgt = isObject(src) ? {} : [];
  37.         each(src, function(val, key) {
  38.             tgt[key] = (isArray(val) || isObject(val)) ? deepCopy(val) : val;
  39.         });
  40.         return tgt;
  41.     }
  42.  
  43.     // each
  44.     function each(items, callback) {
  45.         if (!items) {
  46.             return;
  47.         } else if (isArray(items)) {
  48.             for (var i = 0; i < items.length; i += 1) {
  49.                 callback(items[i], i);
  50.             }
  51.         } else {
  52.             for (var prop in items) {
  53.                 if (items.hasOwnProperty(prop)) {
  54.                     callback(items[prop], prop);
  55.                 }
  56.             }
  57.         }
  58.     }
  59.  
  60.     // normalize paths
  61.     function normalize(path, contextReq) {
  62.         var baseUrl = require.toUrl('.').replace(/\.$/, '');
  63.         if (path.indexOf('!') === -1) {
  64.             // regular path
  65.             return contextReq(path);
  66.         } else {
  67.             // plugin
  68.             path = path.split('!');
  69.             if (path[1]) {
  70.                 path[1] = contextReq.toUrl(path[1]).substring(baseUrl.length);
  71.             }
  72.             return path.join('!');
  73.         }
  74.     }
  75.  
  76.     // override require
  77.     require = function(deps, callback) {
  78.         if (typeof deps === 'string') {
  79.             // requesting internal or plugin module
  80.             return origRequire(deps);
  81.         } else if (running) {
  82.             // lazy loading modules async
  83.             setTimeout(function() {
  84.                 var actuals = [];
  85.                 each(deps, function(depName) {
  86.                     actuals.push(lazy(depName));
  87.                 });
  88.                 callback.apply(null, actuals);
  89.             }, 0);
  90.         } else {
  91.             // calls made to load modules, before tests are executed
  92.             var cfg = deps.deps ? deps : {deps: deps};
  93.             if (!cfg.context) {
  94.                 delete cfg.baseUrl;
  95.             }
  96.            
  97.             requireCount++;
  98.            
  99.             var thisRequire = origRequire(cfg, cfg.deps, function() {
  100.                 cfg.callback && cfg.callback();
  101.                 if (++requireLoadedCount === requireCount) {
  102.                     // all requires have finished loading, execute testr callback
  103.                     complete();
  104.                 }
  105.             }, function(err) {
  106.                 // force failures to finish loading, by defining them as empty modules
  107.                 var failed = err.requireModules[0];
  108.                 thisRequire.undef(failed);
  109.                 origDefine(failed, {});
  110.                 thisRequire([failed]);
  111.             });
  112.         }
  113.     };
  114.  
  115.     // override define
  116.     define = function() {
  117.         var args = [].slice.call(arguments),
  118.             factory = args.pop(),
  119.             deps = args.pop(),
  120.             name = args.pop(),
  121.             depPaths = ['require', 'module'],
  122.             extractedPaths = [],
  123.             pluginLocs = [],
  124.             exportsLocs = [],
  125.             requireLocs = [],
  126.             wrap = !deps && typeof factory === 'function',
  127.             defineArgs;
  128.  
  129.         // account for signature variation
  130.         if (typeof deps === 'string') {
  131.             name = deps;
  132.             deps = [];
  133.         }
  134.  
  135.         // process the dependency ids
  136.         each(deps, function(path, index) {
  137.             if (path.indexOf('!') > -1) {
  138.                 pluginPaths[path.split('!')[0]] = true;
  139.                 pluginLocs.push(index);
  140.             } else if (path === 'exports') {
  141.                 exportsLocs.push(index);
  142.             } else if (path === 'require') {
  143.                 requireLocs.push(index);
  144.             }
  145.             depPaths.push(path);
  146.         });
  147.  
  148.         // find cjs wrapped require calls
  149.         if (!deps) {
  150.             factory.toString().replace(cjsRequireRegExp, function (match, dep) {
  151.                 extractedPaths.push(dep);
  152.             });
  153.         }
  154.        
  155.  
  156.         // rewrite the function that requirejs executes when defining the module
  157.         function trojan(contextReq, module) {
  158.             var offset = 2,
  159.                 deps = [].slice.call(arguments, offset),
  160.                 autoDeps = [],
  161.                 ignore = false;
  162.  
  163.             // determine if the module should be ignored
  164.             each(config.ignore, function(ignoreMod) {
  165.                 if (module.id === ignoreMod) {
  166.                     ignore = true;
  167.                 }
  168.             });
  169.  
  170.             if (!module || pluginPaths[module.id] || ignore) {
  171.                 // jquery or plugin, give requirejs the real module
  172.                 return (typeof factory === 'function') ? factory.apply(null, deps) : factory;
  173.             }
  174.  
  175.             // find dependencies which are stored in requirejs, replace with path
  176.             each(deps, function(dep, i) {
  177.                 if (typeof dep !== 'string') {
  178.                     deps[i] = depPaths[i + offset];
  179.                 }
  180.             });
  181.  
  182.             // alter plugin storage
  183.             each(pluginLocs, function(loc) {
  184.                 // normalize path names
  185.                 var path = depPaths[loc + offset];
  186.                 deps[loc] = normalize(path, contextReq);
  187.             });
  188.  
  189.             // alter exports deps
  190.             each(exportsLocs, function(loc) {
  191.                 deps[loc] = 'exports';
  192.             });
  193.  
  194.             // alter require deps
  195.             each(requireLocs, function(loc) {
  196.                 deps[loc] = 'require';
  197.             });
  198.  
  199.             // save the module
  200.             moduleMap[module.id] = {
  201.                 factory: factory,
  202.                 deps: wrap ? ['require', 'exports'] : deps,
  203.                 require: contextReq
  204.             };
  205.  
  206.             if (module.uri.indexOf('./' + config.stubUrl) === 0) {
  207.                 // stub has been saved to module map, no further processing needed
  208.                 return;
  209.             }
  210.  
  211.             // auto load associated files
  212.             if (config.stubUrl) {
  213.                 autoDeps.push(config.stubUrl + '/' + module.id + '.stub');
  214.             }
  215.             if (config.specUrl) {
  216.                 autoDeps.push(config.specUrl + '/' + module.id + '.spec');
  217.             }
  218.             if (autoDeps.length) {
  219.                 require({
  220.                     context: module.id,
  221.                     baseUrl: config.root,
  222.                     deps: autoDeps
  223.                 });
  224.             }
  225.            
  226.             // define the module as its path name, used by dependants
  227.             return module.id;
  228.         }
  229.  
  230.         // hook back into the loader with modified dependancy paths
  231.         // to trigger dependency loading, and execute the trojan
  232.         defineArgs = [depPaths.concat(extractedPaths), trojan];
  233.         if (name) { defineArgs.unshift(name); }
  234.         origDefine.apply(null, defineArgs);
  235.         if (name) { require([name]); } // force requirejs to load the module immediately and call the trojan
  236.     };
  237.  
  238.     // copy original function properties
  239.     each(origRequire, function(val, key) {
  240.         require[key] = val;
  241.     });
  242.     each(origDefine, function(val, key) {
  243.         define[key] = val;
  244.     });
  245.  
  246.     // suppress 404 errors
  247.     origRequire.onError = function(err) {
  248.         if (err.requireType !== 'scripterror') {
  249.             throw err;
  250.         }
  251.     };
  252.  
  253.     // create new modules with the factory
  254.     function buildModule(moduleName, stubs, useExternal, subject, whitelistExceptions) {
  255.         var depModules = [],
  256.             exports = {},
  257.             mustBeStubbed = config.whitelist && config.whitelist.length,
  258.             moduleDef, externalStub, factory, deps, contextReq,
  259.             getModule = function(depName) {
  260.                 return stubs && stubs[depName] || buildModule(depName, stubs, useExternal, false, whitelistExceptions);
  261.             };
  262.  
  263.         // expose getModule method
  264.         lazy = getModule;
  265.  
  266.         // get external stub
  267.         externalStub = !subject && useExternal && moduleMap[config.stubUrl + '/' + moduleName + '.stub'];
  268.  
  269.         // throw error if module must be stubbed
  270.         if (mustBeStubbed && !subject && !externalStub) {
  271.             each(config.whitelist, function(allowedActual) {
  272.                 if (moduleName === allowedActual) {
  273.                     mustBeStubbed = false;
  274.                 }
  275.             });
  276.             if (mustBeStubbed) {
  277.                 whitelistExceptions.push(moduleName);
  278.                 return {};
  279.             }
  280.         }
  281.  
  282.         // get module definition from map
  283.         moduleDef = externalStub || moduleMap[moduleName];
  284.         if (!moduleDef) {
  285.             // module may be stored in requirejs, e.g. plugin-loaded dependencies
  286.             try {
  287.                 return require(moduleName);
  288.             } catch(e) {
  289.                 throw new Error('module has not been loaded: ' + moduleName);
  290.             }
  291.         }
  292.  
  293.         // shortcuts
  294.         factory = moduleDef.factory;
  295.         deps = moduleDef.deps;
  296.         contextReq = moduleDef.require;
  297.  
  298.         // normalize stubs object paths on first call
  299.         if (subject) {
  300.             each(stubs, function(stub, path) {
  301.                 var nPath = normalize(path, contextReq);
  302.                 if (nPath !== path) {
  303.                     stubs[nPath] = stub;
  304.                     delete stubs[path];
  305.                 }
  306.             });
  307.         }
  308.  
  309.         // load up dependencies
  310.         each(deps, function(dep) {
  311.             // determine what to pass to the factory
  312.             if (dep == 'exports') {
  313.                 dep = exports;
  314.             } else if (dep === 'require') {
  315.                 dep = function(path) {
  316.                     var module = contextReq(path);
  317.                     if (typeof module === 'string' && path.indexOf('!') === -1) {
  318.                         // module defined by testr.js, normalise path and build it
  319.                         module = getModule(module);
  320.                     }
  321.                     return module;
  322.                 };
  323.             } else {
  324.                 dep = getModule(dep);
  325.             }
  326.  
  327.             // add dependency to array
  328.             depModules.push(dep);
  329.         });
  330.  
  331.         if (typeof factory !== 'function') {
  332.             // return clean copy of module object
  333.             return deepCopy(factory);
  334.         } else {
  335.             // return clean instance of module
  336.             return factory.apply(exports, depModules) || exports;
  337.         }
  338.     }
  339.  
  340.     // testr API
  341.     testr = function(moduleName, stubs, useExternal) {
  342.         var whitelistExceptions = [],
  343.             module, plural;
  344.  
  345.         // first call to set running state
  346.         running = true;
  347.  
  348.         // check module name
  349.         if (typeof moduleName !== 'string') {
  350.             throw Error('module name must be a string');
  351.         }
  352.  
  353.         // check stubs
  354.         if (!useExternal && typeof stubs === 'boolean') {
  355.             useExternal = stubs;
  356.             stubs = {};
  357.         } else if (stubs && !isObject(stubs)) {
  358.             throw Error('stubs must be given as an object');
  359.         }
  360.  
  361.         // build the module under test
  362.         module = buildModule(moduleName, stubs, useExternal, true, whitelistExceptions);
  363.  
  364.         // throw error if not all required stubs provided
  365.         if (whitelistExceptions.length) {
  366.             plural = (whitelistExceptions.length > 1) ? 's' : '';
  367.             throw Error('module' + plural + ' must be stubbed: ' + whitelistExceptions.join(', '));
  368.         }
  369.  
  370.         // return the module
  371.         return module;
  372.     };
  373.  
  374.     // testr config
  375.     testr.config = function(userConfig) {
  376.         each(userConfig, function(val, key) {
  377.             if (val) {
  378.                 config[key] = val;
  379.             } else {
  380.                 delete config[key];
  381.             }
  382.         });
  383.     };
  384.  
  385.     // restore function
  386.     testr.restore = function() {
  387.         window.define = origDefine;
  388.         window.require = origRequire;
  389.     }
  390.  
  391.     // attach version
  392.     testr.version = version;
  393.  
  394.     // kick off
  395.     testr.run = function(rconf, callback) {
  396.         complete = callback;
  397.         origRequire({baseUrl: config.root + config.baseUrl});
  398.         require([rconf]);
  399.     };
  400.  
  401. }());
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement