├── run-tests.js ├── test ├── integration │ ├── exclude │ │ ├── lib │ │ │ ├── mod1.js │ │ │ └── mod2.js │ │ ├── res │ │ │ ├── file1.json │ │ │ └── file2.json │ │ ├── buster.js │ │ └── test │ │ │ └── test.js │ ├── test-filter │ │ ├── args.txt │ │ ├── buster.js │ │ └── test.js │ ├── expect │ │ ├── config.js │ │ ├── test │ │ │ ├── test-browser.js │ │ │ ├── test-browser-amd.js │ │ │ └── test-node.js │ │ ├── src │ │ │ └── mod.js │ │ ├── buster.js │ │ └── lib │ │ │ └── require.js │ ├── match-data-attribute │ │ ├── buster.js │ │ └── test.js │ ├── sinon-match │ │ ├── buster.js │ │ └── test.js │ ├── browser │ │ ├── buster.js │ │ ├── capture-server-test-helper.js │ │ ├── capture-server-wiring-test.js │ │ └── browser-wiring-test.js │ └── backend-proxy │ │ ├── buster.js │ │ └── some-tests.js └── buster-test.js ├── doc └── samples │ ├── buster-test.css │ ├── buster-test.js │ ├── minimal-scaffold.html │ ├── test-case.html │ ├── strftime.html │ ├── strftime-test.js │ ├── spec.html │ ├── strftime.js │ ├── spec.js │ └── test-case.js ├── .gitignore ├── bin ├── buster-headless ├── buster-static ├── buster-server ├── buster-ci ├── buster-autotest ├── buster └── buster-test ├── .npmignore ├── lib ├── buster.js └── buster │ ├── wiring-extension.js │ ├── capture-server-wiring.js │ ├── amd-shim.js │ ├── framework-extension.js │ ├── buster-wiring.js │ └── browser-wiring.js ├── .travis.yml ├── appveyor.yml ├── script └── phantom.js ├── LICENSE ├── package.json ├── run-integration-tests.js ├── Readme.md └── resources └── buster-test.css /run-tests.js: -------------------------------------------------------------------------------- 1 | require("./test/buster-test.js"); 2 | -------------------------------------------------------------------------------- /test/integration/exclude/lib/mod1.js: -------------------------------------------------------------------------------- 1 | var mod1 = {}; -------------------------------------------------------------------------------- /test/integration/exclude/lib/mod2.js: -------------------------------------------------------------------------------- 1 | var mod2 = {}; -------------------------------------------------------------------------------- /test/integration/test-filter/args.txt: -------------------------------------------------------------------------------- 1 | "test to run" -------------------------------------------------------------------------------- /doc/samples/buster-test.css: -------------------------------------------------------------------------------- 1 | ../../resources/buster-test.css -------------------------------------------------------------------------------- /doc/samples/buster-test.js: -------------------------------------------------------------------------------- 1 | ../../resources/buster-test.js -------------------------------------------------------------------------------- /test/integration/exclude/res/file1.json: -------------------------------------------------------------------------------- 1 | { 2 | "value": "test" 3 | } -------------------------------------------------------------------------------- /test/integration/exclude/res/file2.json: -------------------------------------------------------------------------------- 1 | { 2 | "value": "test2" 3 | } -------------------------------------------------------------------------------- /test/integration/expect/config.js: -------------------------------------------------------------------------------- 1 | require.config({ 2 | baseUrl: 'src/' 3 | }); 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | node_modules 3 | resources/buster-test.js 4 | .idea 5 | .cache-require-paths.json 6 | -------------------------------------------------------------------------------- /bin/buster-headless: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | dir=`dirname $0` 4 | dir=`dirname $dir` 5 | phantomjs $dir/script/phantom.js 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.log 2 | node_modules 3 | # resources/buster-test.js # gitignored, but publishable 4 | .idea 5 | .cache-require-paths.json 6 | 7 | doc 8 | -------------------------------------------------------------------------------- /test/integration/test-filter/buster.js: -------------------------------------------------------------------------------- 1 | var config = module.exports; 2 | 3 | config["node tests"] = { 4 | environment: "node", 5 | tests: ["test*.js"] 6 | }; -------------------------------------------------------------------------------- /test/integration/match-data-attribute/buster.js: -------------------------------------------------------------------------------- 1 | var config = module.exports; 2 | 3 | config["browser tests"] = { 4 | environment: "browser", 5 | tests: ["test*.js"], 6 | extensions: [require("buster-html-doc")] 7 | }; -------------------------------------------------------------------------------- /doc/samples/minimal-scaffold.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /test/integration/sinon-match/buster.js: -------------------------------------------------------------------------------- 1 | var config = module.exports; 2 | 3 | config["browser tests"] = { 4 | environment : "browser", 5 | tests : [ "test.js" ] 6 | }; 7 | 8 | config["node tests"] = { 9 | environment : "node", 10 | tests : [ "test.js" ] 11 | }; -------------------------------------------------------------------------------- /test/integration/browser/buster.js: -------------------------------------------------------------------------------- 1 | var config = module.exports; 2 | 3 | 4 | config["browser tests"] = { 5 | environment: "browser", 6 | tests: [ 7 | "*-test.js" 8 | ], 9 | libs: [ 10 | "capture-server-test-helper.js" 11 | ] 12 | }; 13 | 14 | -------------------------------------------------------------------------------- /test/integration/expect/test/test-browser.js: -------------------------------------------------------------------------------- 1 | var assert = buster.assert; 2 | var expect = buster.expect; 3 | 4 | buster.testCase("amd", { 5 | 6 | "test mod": function () { 7 | assert.equals(mod.func(), "tut"); 8 | expect(mod.func()).toBe("tut"); 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /bin/buster-static: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var path = require("path"); 3 | var root = path.join(__dirname, "..", "lib", "buster"); 4 | 5 | require("buster-static").create(process.stdout, process.stderr, { 6 | extensions: [require(path.join(root, "framework-extension"))] 7 | }).run(process.argv.slice(2)); 8 | -------------------------------------------------------------------------------- /test/integration/exclude/buster.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | var config = module.exports; 5 | 6 | config["Browser tests"] = { 7 | environment: "browser", 8 | sources: [ 9 | 'lib/**/*.js', '!lib/mod2.js' 10 | ], 11 | tests: ["test/test.js"], 12 | resources: ["res/file*.json", "!res/file2.json"] 13 | } -------------------------------------------------------------------------------- /test/integration/backend-proxy/buster.js: -------------------------------------------------------------------------------- 1 | var config = module.exports; 2 | 3 | 4 | config["browser tests"] = { 5 | environment: "browser", 6 | tests: ["some-tests.js"], 7 | resources: [ 8 | { 9 | path: "/app", 10 | backend: "http://docs.busterjs.org" 11 | } 12 | ] 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /test/integration/expect/test/test-browser-amd.js: -------------------------------------------------------------------------------- 1 | define(['mod'], function(mod) { 2 | 3 | var assert = buster.assert; 4 | var expect = buster.expect; 5 | 6 | buster.testCase("amd tests", { 7 | 8 | "test mod": function () { 9 | assert.equals(mod.func(), "tut"); 10 | expect(mod.func()).toBe("tut"); 11 | } 12 | }); 13 | }); -------------------------------------------------------------------------------- /doc/samples/test-case.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Buster.JS Test case 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/integration/test-filter/test.js: -------------------------------------------------------------------------------- 1 | var buster = require("../../.."); 2 | 3 | var assert = buster.assert; 4 | var fail = buster.referee.fail; 5 | 6 | buster.testCase("filter by test name", { 7 | 8 | "test to run": function () { 9 | assert(true); 10 | }, 11 | 12 | "test to be filtered out": function () { 13 | fail("Test should have been filtered out!"); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /test/integration/expect/src/mod.js: -------------------------------------------------------------------------------- 1 | ((typeof define === "function" && define.amd && function (m) { 2 | define("mod", [], m); 3 | }) || (typeof module === "object" && function (m) { 4 | module.exports = m(); 5 | }) || function (m) { 6 | this.mod = m(); 7 | })(function () { 8 | 9 | function func() { 10 | return ("tut"); 11 | } 12 | 13 | return { 14 | func: func 15 | }; 16 | }); -------------------------------------------------------------------------------- /doc/samples/strftime.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | strftime tests 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /lib/buster.js: -------------------------------------------------------------------------------- 1 | if (typeof module === "object" && typeof require === "function") { 2 | var buster = module.exports = require("./buster/buster-wiring"); 3 | } 4 | 5 | (function (glbl) { 6 | var tc = buster.testContext; 7 | if (tc.listeners && (tc.listeners.create || []).length > 0) { return; } 8 | 9 | tc.on("create", buster.autoRun({ 10 | cwd: typeof process != "undefined" ? process.cwd() : null 11 | })); 12 | }(typeof global != "undefined" ? global : this)); 13 | -------------------------------------------------------------------------------- /lib/buster/wiring-extension.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | 3 | module.exports = { 4 | configure: function (configuration) { 5 | configuration.on("load:framework", function (rs) { 6 | rs.addFileResource(path.join(__dirname, "capture-server-wiring.js"), { 7 | path: "/buster/capture-server-wiring.js" 8 | }).then(function () { 9 | rs.loadPath.append("/buster/capture-server-wiring.js"); 10 | }); 11 | }); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /test/integration/browser/capture-server-test-helper.js: -------------------------------------------------------------------------------- 1 | function createRemoteEmitter() { 2 | var emitter = buster.bane.createEventEmitter(); 3 | 4 | emitter.emit = function (event, data) { 5 | return buster.emit.call(this, event, { 6 | data: data, 7 | topic: event 8 | }); 9 | }; 10 | 11 | emitter.connect = sinon.spy(); 12 | return emitter; 13 | } 14 | 15 | buster.testRunner.onCreate(function (runner) { 16 | buster.wiredRunner = runner; 17 | }); 18 | -------------------------------------------------------------------------------- /test/integration/expect/test/test-node.js: -------------------------------------------------------------------------------- 1 | var buster = require("../../../.."); 2 | var mod = require("../src/mod"); 3 | var assert = buster.assert; 4 | var expect = buster.expect; 5 | 6 | buster.testCase("expect", { 7 | 8 | "test mod": function () { 9 | assert.equals(mod.func(), "tut"); 10 | expect(mod.func()).toBe("tut"); 11 | }, 12 | 13 | "should not fail if path.resolve is stubbed": function () { 14 | this.stub(require("path"), "resolve"); 15 | expect(true).toBeTruthy(); 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /bin/buster-server: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require("buster-server-cli").create(process.stdout, process.stderr, { 4 | name: "Buster.JS", 5 | binary: "buster-server", 6 | description: "buster-server [options]", 7 | missionStatement: "Server for automating Buster.JS test runs across browsers", 8 | unexpectedErrorMessage: "Something went horribly wrong. This is most likely " + 9 | "a bug, please report at\n" + 10 | "http://github.com/busterjs/buster/issues\n" 11 | }).run(process.argv.slice(2)); 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | env: 4 | global: 5 | - CXX=g++-4.8 6 | addons: 7 | apt: 8 | sources: 9 | - ubuntu-toolchain-r-test 10 | packages: 11 | - g++-4.8 12 | matrix: 13 | include: 14 | - node_js: "0.10" 15 | env: JSDOM=3 16 | - node_js: "0.12" 17 | env: JSDOM=3 18 | - node_js: "4" 19 | env: JSDOM=3 20 | - node_js: "4" 21 | env: JSDOM=7 22 | - node_js: "5" 23 | env: JSDOM=3 24 | - node_js: "5" 25 | env: JSDOM=7 26 | before_install: 27 | - npm i -g npm 28 | - npm i jsdom@$JSDOM # becomes extraneous, but overrides buster-test dependency 29 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | NO_INTEGRATION: 1 3 | matrix: 4 | - nodejs_version: "0.10" 5 | jsdom_version: "3" 6 | - nodejs_version: "0.12" 7 | jsdom_version: "3" 8 | - nodejs_version: "4" 9 | jsdom_version: "3" 10 | - nodejs_version: "4" 11 | jsdom_version: "7" 12 | - nodejs_version: "5" 13 | jsdom_version: "3" 14 | - nodejs_version: "5" 15 | jsdom_version: "7" 16 | 17 | install: 18 | - ps: Install-Product node $env:nodejs_version 19 | - npm i -g npm 20 | - npm i jsdom@%jsdom_version% 21 | - npm i 22 | 23 | test_script: 24 | - node --version 25 | - npm --version 26 | - npm test 27 | 28 | build: off 29 | -------------------------------------------------------------------------------- /test/integration/match-data-attribute/test.js: -------------------------------------------------------------------------------- 1 | buster.spec.expose(); 2 | var expect = buster.referee.expect; 3 | 4 | var spec = describe("DOM elements with data attibute", function () { 5 | it("match dom element", function () { 6 | /*:DOC element = */ 7 | expect(this.element).toMatch({id: 'id1', type:'checkbox', 'data-path':'foo.bar'}); 8 | }); 9 | 10 | it("not match dom element", function () { 11 | /*:DOC element = */ 12 | expect(this.element).not.toMatch({id: 'id1', type:'checkbox', 'data-path':'foo.bar'}); 13 | }); 14 | }); -------------------------------------------------------------------------------- /bin/buster-ci: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | process.on("uncaughtException", function (err) { 4 | console.log(err.stack); 5 | }); 6 | 7 | var path = require("path"); 8 | 9 | var BusterCi = require("buster-ci"); 10 | 11 | function loadConfig(fileName) { 12 | var mod = path.resolve("", fileName); 13 | try { 14 | return require(mod); 15 | } catch (e) { 16 | if (e.message !== "Cannot find module '" + mod + "'") { 17 | throw e; 18 | } 19 | } 20 | } 21 | 22 | var config = loadConfig("buster-ci.js"); 23 | 24 | if (config) { 25 | var busterCi = new BusterCi(config); 26 | busterCi.run(process.argv.slice(2)); 27 | } else { 28 | console.log("Configuration file 'buster-ci.js' not found!"); 29 | } 30 | -------------------------------------------------------------------------------- /bin/buster-autotest: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var fs = require("fs"); 3 | 4 | if (!fs.watch) { 5 | console.log("Your system does not support fs.watch"); 6 | console.log("buster-autotest will unfortunately not work."); 7 | console.log("Please consult the Node documentation for more information."); 8 | process.exit(1); 9 | } 10 | 11 | var autotest = require("buster-autotest"); 12 | var cwd = process.cwd(); 13 | var cmd = "node"; 14 | var argv = process.argv.slice(2); 15 | argv.unshift(process.argv[1].replace("autotest", "test")); 16 | 17 | autotest.watch(cwd, { argv: argv, cmd: cmd }); 18 | console.log("Watching", cwd); 19 | console.log("Test command:"); 20 | console.log(" ", cmd, argv.join(" "), "\n"); 21 | console.log("Have fun!"); 22 | -------------------------------------------------------------------------------- /test/integration/backend-proxy/some-tests.js: -------------------------------------------------------------------------------- 1 | var assert = buster.assert; 2 | 3 | buster.testCase("backend proxy", { 4 | 5 | "forwards request to http://docs.busterjs.org/en/latest/" : function (done) { 6 | 7 | this.timeout = 10000; 8 | var xmlhttp = new XMLHttpRequest(); 9 | 10 | xmlhttp.onreadystatechange = function() { 11 | if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { 12 | var data = xmlhttp.responseText; 13 | assert.match(data, "Welcome! Buster.JS is..."); 14 | done(); 15 | } 16 | }; 17 | 18 | var url = buster.env.contextPath + "/app/en/latest/"; 19 | xmlhttp.open("GET", url, true); 20 | xmlhttp.send(); 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /test/integration/sinon-match/test.js: -------------------------------------------------------------------------------- 1 | if (typeof module == "object" && typeof require == "function") { 2 | var buster = require("../../.."); 3 | } 4 | 5 | var assert = buster.assert; 6 | 7 | buster.testCase("sinon.match", { 8 | 9 | setUp : function () { 10 | this.obj = { 11 | someMethod : function () {} 12 | } 13 | this.mockedObj = this.mock(this.obj); 14 | }, 15 | 16 | "verify that the typeOf matchers are working as expected" : function () { 17 | 18 | this.mockedObj.expects('someMethod').once().withArgs( 19 | buster.sinon.match.func).returns(1); 20 | this.mockedObj.expects('someMethod').once().withArgs( 21 | buster.sinon.match.string).returns(2); 22 | 23 | assert(this.obj.someMethod(function () {}), 1); 24 | assert(this.obj.someMethod("str"), 2); 25 | } 26 | }); -------------------------------------------------------------------------------- /test/integration/expect/buster.js: -------------------------------------------------------------------------------- 1 | var config = module.exports; 2 | 3 | config["browser tests"] = { 4 | environment: "browser", 5 | sources: ["src/mod.js"], 6 | tests: ["test/test-browser.js"] 7 | }; 8 | 9 | config["browser amd tests"] = { 10 | environment: "browser", 11 | libs: [ 12 | "lib/require.js", 13 | "config.js" 14 | ], 15 | sources: ["src/mod.js"], 16 | tests: ["test/test-browser-amd.js"], 17 | extensions: [require("buster-amd")], 18 | "buster-amd": { 19 | pathMapper: function (path) { 20 | return path. 21 | // remove extension 22 | replace(/\.js$/, ""). 23 | // replace leading slash with previous directory for test files 24 | replace(/^\//, "../"); 25 | } 26 | } 27 | }; 28 | 29 | config["node tests"] = { 30 | environment: "node", 31 | tests: ["test/test-node.js"] 32 | }; 33 | 34 | 35 | -------------------------------------------------------------------------------- /script/phantom.js: -------------------------------------------------------------------------------- 1 | var page = require('webpage').create(); 2 | var system = require('system'); 3 | var captureUrl = 'http://localhost:1111/capture'; 4 | 5 | if (system.args.length==2) { 6 | captureUrl = system.args[1]; 7 | } 8 | 9 | phantom.silent = false; 10 | 11 | page.open(captureUrl, function(status) { 12 | if(!phantom.silent) { 13 | console.log(status); 14 | if (status !== 'success') { 15 | console.log('phantomjs failed to connect'); 16 | phantom.exit(1); 17 | } 18 | 19 | page.onConsoleMessage = function (msg, line, id) { 20 | var composedMsg = ''; 21 | var fileName = id ? id.split('/') : null; 22 | // format the output message with filename, line number and message 23 | // weird gotcha: phantom only uses the first console.log argument it gets :( 24 | composedMsg = fileName ? fileName[fileName.length-1]+', ' : ''; 25 | composedMsg = line ? line +': ' : ''; 26 | composedMsg = msg; 27 | console.log(composedMsg); 28 | }; 29 | 30 | page.onAlert = function(msg) { 31 | console.log(msg); 32 | }; 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /lib/buster/capture-server-wiring.js: -------------------------------------------------------------------------------- 1 | (function (B) { 2 | B.configureTestClient = function (emitter) { 3 | function runtimeEmitter(runner) { 4 | return { 5 | emit: function (event, data) { 6 | data.runtime = runner.runtime; 7 | data.uuid = runner.runtime && runner.runtime.uuid; 8 | emitter.emit(event, data); 9 | } 10 | }; 11 | } 12 | 13 | var wiring = B.wire(function () { 14 | var runner = B.testRunner.create({ 15 | runtime: navigator.userAgent 16 | }); 17 | var re = runtimeEmitter(runner); 18 | B.wire.logger(re); 19 | B.wire.uncaughtErrors(re); 20 | var reporter = B.reporters.jsonProxy.create(emitter); 21 | reporter.listen(runner); 22 | return runner; 23 | }); 24 | 25 | // TODO: FIX FIX FIX 26 | B.configureTestRun = wiring.ready; 27 | }; 28 | 29 | if (B.on && B.emit) { 30 | B.configureTestClient(B); 31 | } 32 | }(buster)); 33 | -------------------------------------------------------------------------------- /doc/samples/strftime-test.js: -------------------------------------------------------------------------------- 1 | if (typeof require != "undefined") { 2 | var buster = require("../../lib/buster"); 3 | require("./strftime"); 4 | } 5 | 6 | var assert = buster.assert; 7 | 8 | buster.testCase("Date.prototype.strftime", { 9 | setUp: function () { 10 | this.date = new Date(2009, 9, 2, 22, 14, 45); 11 | }, 12 | 13 | "%Y should return full year": function () { 14 | var year = Date.formats.Y(this.date); 15 | 16 | assert.isNumber(year); 17 | assert.equals(year, 2009); 18 | }, 19 | 20 | "%m should return month": function () { 21 | var month = Date.formats.m(this.date); 22 | 23 | assert.isString(month); 24 | assert.equals(month, "10"); 25 | }, 26 | 27 | "%d should return date": function () { 28 | assert.equals(Date.formats.d(this.date), "02"); 29 | }, 30 | 31 | "%y should return year as two digits": function () { 32 | assert.equals(Date.formats.y(this.date), "09"); 33 | }, 34 | 35 | "%F should act as %Y-%m-%d": function () { 36 | assert.equals(this.date.strftime("%F"), "2009-10-02"); 37 | } 38 | }); 39 | -------------------------------------------------------------------------------- /bin/buster: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require("fs"); 4 | 5 | var argv = Array.prototype.slice.call(process.argv).slice(2); 6 | 7 | if (argv.length == 0) { 8 | printHelp(); 9 | } else if (argv[0] == "--version" || argv[0] == "-v") { 10 | printVersions(); 11 | } else { 12 | printHelp(); 13 | } 14 | 15 | function printHelp() { 16 | console.log("Usage: buster [--version,-v]"); 17 | console.log(); 18 | console.log("Run one of the following commands to use Buster.JS:"); 19 | console.log("buster-test Run tests with Buster.JS"); 20 | console.log("buster-server Manually start the server for browser capturing"); 21 | console.log("buster-autotest Like 'test', but automatically re-run tests when files change"); 22 | console.log("buster-static An alternative static HTML page based runner"); 23 | console.log(); 24 | console.log("Please note:"); 25 | console.log("Sub-commands such as 'buster test' are no longer supported"); 26 | console.log(); 27 | } 28 | 29 | function printVersions() { 30 | var version = JSON.parse(fs.readFileSync(__dirname + "/../package.json")).version; 31 | console.log("Buster.JS version " + version + " Beta 5"); 32 | } 33 | -------------------------------------------------------------------------------- /doc/samples/spec.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Buster.JS Spec 5 | 6 | 7 | 8 | 9 |

Buster.JS Spec

10 |

11 | This test sample runs in embedded mode - i.e., it only styles it self and 12 | does not assume it's alone on the page. Use for running tests inside your 13 | app. 14 |

15 |

16 | To embed the runner, simply create an element with id "buster", and buster 17 | will figure out the rest. Note that if you want the buster-test styling, 18 | you have to manually include the CSS file (found in 19 | resources/buster-test.css). 20 |

21 |

22 | For more fine-grained control, you can instantiate and configure your own 23 | runner, see documentation for buster-test/runner. 24 |

25 |
26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /bin/buster-test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var path = require("path"); 3 | var testCli = require("buster-test-cli"); 4 | 5 | process.on("uncaughtException", function (err) { 6 | console.error("Uncaught exception:", err.stack ? err.stack : err); 7 | }); 8 | 9 | var busterPath = path.join(__dirname, "..", "lib", "buster"); 10 | 11 | testCli.create(process.stdout, process.stderr, { 12 | missionStatement: "Run Buster.JS tests on node, in browsers, or both", 13 | description: [ 14 | "Usage: buster-test [options] [filters]\n", 15 | "Optionally provide a test name filter to run a selection of tests:", 16 | "`buster-test configuration` runs all contexts/tests with the word", 17 | "'configuration' in their name." 18 | ].join("\n"), 19 | environmentVariable: "BUSTER_TEST_OPT", 20 | runners: testCli.runners, 21 | configBaseName: "buster", 22 | extensions: { 23 | browser: [ 24 | require(path.join(busterPath, "framework-extension")), 25 | require(path.join(busterPath, "wiring-extension")), 26 | require("buster-syntax").create({ ignoreReferenceErrors: true }) 27 | ] 28 | } 29 | }).run(process.argv.slice(2), function (err) { 30 | if (err) { 31 | console.error("buster-test run failed:", err.stack ? err.stack : err); 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /lib/buster/amd-shim.js: -------------------------------------------------------------------------------- 1 | this.buster = this.buster || {}; 2 | buster._ = buster.lodash = this._; 3 | buster.when = this.when; 4 | buster.async = this.async; 5 | buster.platform = this.platform; 6 | 7 | this.define = (function () { 8 | function resolve(ns) { 9 | var pieces = ns.replace(/^buster-test\//, "").replace(/-(.)/g, function (m, l) { 10 | return l.toUpperCase(); 11 | }).split("/"); 12 | 13 | return { 14 | property: pieces.pop(), 15 | object: buster._.reduce(pieces, function (ctx, name) { 16 | if (!ctx[name]) { ctx[name] = {}; } 17 | return ctx[name]; 18 | }, buster) 19 | }; 20 | } 21 | 22 | return function (id, dependencies, factory) { 23 | if (arguments.length === 2) { 24 | factory = dependencies; 25 | dependencies = []; 26 | } 27 | 28 | var deps = [], dep; 29 | 30 | for (var j, i = 0, l = dependencies.length; i < l; ++i) { 31 | dep = resolve(dependencies[i]); 32 | 33 | if (!dep.object[dep.property]) { 34 | throw new Error(id + " depends on unknown module " + dep.property); 35 | } 36 | deps.push(dep.object[dep.property]); 37 | } 38 | 39 | dep = resolve(id); 40 | dep.object[dep.property] = factory.apply(this, deps); 41 | }; 42 | }()); 43 | 44 | this.define.amd = true; 45 | -------------------------------------------------------------------------------- /doc/samples/strftime.js: -------------------------------------------------------------------------------- 1 | /*jslint indent: 2, eqeqeq: false, onevar: false*/ 2 | Date.prototype.strftime = (function () { 3 | function strftime(format) { 4 | var date = this; 5 | 6 | return (format + "").replace(/%([a-zA-Z])/g, function (m, f) { 7 | var formatter = Date.formats && Date.formats[f]; 8 | 9 | if (typeof formatter == "function") { 10 | return formatter.call(Date.formats, date); 11 | } else if (typeof formatter == "string") { 12 | return date.strftime(formatter); 13 | } 14 | 15 | return f; 16 | }); 17 | } 18 | 19 | // Internal helper 20 | function zeroPad(num) { 21 | return (+num < 10 ? "0" : "") + num; 22 | } 23 | 24 | Date.formats = { 25 | // Formatting methods 26 | d: function (date) { 27 | return zeroPad(date.getDate()); 28 | }, 29 | 30 | m: function (date) { 31 | return zeroPad(date.getMonth() + 1); 32 | }, 33 | 34 | y: function (date) { 35 | return zeroPad(date.getYear() % 100); 36 | }, 37 | 38 | Y: function (date) { 39 | return date.getFullYear(); 40 | }, 41 | 42 | j: function (date) { 43 | var jan1 = new Date(date.getFullYear(), 0, 1); 44 | var diff = date.getTime() - jan1.getTime(); 45 | 46 | // 86400000 == 60 * 60 * 24 * 1000 47 | return Math.ceil(diff / 86400000); 48 | }, 49 | 50 | // Format shorthands 51 | F: "%Y-%m-%d", 52 | D: "%m/%d/%y" 53 | }; 54 | 55 | return strftime; 56 | }()); 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The BSD License) 2 | 3 | Copyright (c) 2010-2015, Christian Johansen, christian@cjohansen.no and 4 | August Lilleaas, august.lilleaas@gmail.com. All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | * Neither the name of Christian Johansen nor the names of his contributors 15 | may be used to endorse or promote products derived from this software 16 | without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "buster", 3 | "version": "0.8.0", 4 | "main": "./lib/buster", 5 | "description": "Buster.JS JavaScript Test framework. Meta package that pieces together various sub-projects.", 6 | "homepage": "http://busterjs.org/", 7 | "author": { 8 | "name": "August Lilleaas and Christian Johansen" 9 | }, 10 | "contributors": [ 11 | { 12 | "name": "August Lilleaas", 13 | "email": "august.lilleaas@gmail.com", 14 | "url": "http://augustl.com" 15 | }, 16 | { 17 | "name": "Christian Johansen", 18 | "email": "christian@cjohansen.no", 19 | "url": "http://cjohansen.no" 20 | }, 21 | { 22 | "name": "Daniel Wittner", 23 | "email": "d.wittner@gmx.de", 24 | "url": "https://github.com/dwittner" 25 | } 26 | ], 27 | "license": "BSD-3-Clause", 28 | "repository": { 29 | "type": "git", 30 | "url": "https://github.com/busterjs/buster.git" 31 | }, 32 | "bin": { 33 | "buster": "./bin/buster", 34 | "buster-test": "./bin/buster-test", 35 | "buster-server": "./bin/buster-server", 36 | "buster-autotest": "./bin/buster-autotest", 37 | "buster-static": "./bin/buster-static", 38 | "buster-ci": "./bin/buster-ci" 39 | }, 40 | "scripts": { 41 | "test": "node run-tests.js && node run-integration-tests.js", 42 | "test-debug": "node --debug-brk run-tests.js" 43 | }, 44 | "dependencies": { 45 | "async": "1.x", 46 | "bane": "1.x", 47 | "buster-autotest": "0.6.x", 48 | "buster-ci": "0.3.x", 49 | "buster-server-cli": "0.4.x", 50 | "buster-sinon": "0.7.x", 51 | "buster-static": "0.7.x", 52 | "buster-syntax": "0.5.x", 53 | "buster-test": "0.8.x", 54 | "buster-test-cli": "0.9.x", 55 | "evented-logger": "1.x", 56 | "formatio": "1.x", 57 | "lodash": "3.x", 58 | "platform": "1.x", 59 | "referee": "1.x", 60 | "referee-sinon": "1.x", 61 | "samsam": "1.x", 62 | "sinon": "1.x", 63 | "stack-filter": "1.x", 64 | "when": "3.x >=3.7.6" 65 | }, 66 | "devDependencies": { 67 | "buster-amd": "0.3.x", 68 | "buster-html-doc": "2.x", 69 | "phantomjs": "1.x", 70 | "uglify-js": "2.x" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/integration/exclude/test/test.js: -------------------------------------------------------------------------------- 1 | var assert = buster.assert; 2 | var refute = buster.refute; 3 | var fail = buster.referee.fail; 4 | 5 | buster.testCase("exclude test", { 6 | 7 | "mod1 should be loaded" : function () { 8 | assert.isObject(window.mod1); 9 | }, 10 | 11 | "mod2 should not be loaded" : function () { 12 | refute.isObject(window.mod2); 13 | }, 14 | 15 | "for resources" : { 16 | setUp : function () { 17 | this.timeout = 10000; 18 | this.get = function(url, cb) { 19 | 20 | var xmlhttp = new XMLHttpRequest(); 21 | var errorCbs = []; 22 | 23 | xmlhttp.onreadystatechange = function() { 24 | if (xmlhttp.readyState == 4) { 25 | 26 | if (xmlhttp.status == 200) { 27 | var data = xmlhttp.responseText; 28 | cb(data); 29 | } else { 30 | for ( var i = 0; i < errorCbs.length; i++) { 31 | errorCbs[i]("Error " + xmlhttp.status) 32 | } 33 | } 34 | } 35 | }; 36 | xmlhttp.open("GET", url, true); 37 | xmlhttp.send(); 38 | 39 | return { 40 | fail : function(cb) { 41 | errorCbs.push(cb); 42 | } 43 | } 44 | }; 45 | }, 46 | 47 | "load file1.json" : function (done) { 48 | this.get(buster.env.contextPath + "/res/file1.json", 49 | done(function(data) { 50 | assert.equals(JSON.parse(data).value, "test"); 51 | })); 52 | }, 53 | 54 | "file2.json can't be loaded" : function (done) { 55 | this.get(buster.env.contextPath + "/res/file2.json", 56 | done(function(data) { 57 | fail("Error 404 expected!"); 58 | })).fail(done(function(err) { 59 | assert.match(err, "Error 404"); 60 | })); 61 | } 62 | } 63 | }); -------------------------------------------------------------------------------- /test/buster-test.js: -------------------------------------------------------------------------------- 1 | var buster = require("../lib/buster"); 2 | var assert = buster.assert; 3 | var refute = buster.refute; 4 | 5 | buster.testCase("Buster integration test", { 6 | "should count sinon assertions": function (done) { 7 | var testCase = buster.testCase("Sinon assertion count", { 8 | "should count two assertions": function () { 9 | var spy = this.spy(); 10 | spy(); 11 | spy(42); 12 | 13 | assert.calledTwice(spy); 14 | assert.calledWith(spy, 42); 15 | } 16 | }); 17 | 18 | var runner = buster.testRunner.create(); 19 | 20 | runner.on("suite:end", done(function (results) { 21 | assert.equals(results.assertions, 2); 22 | })); 23 | 24 | runner.runSuite([testCase]); 25 | }, 26 | 27 | "should expose console.log as this.log in tests": function (done) { 28 | this.spy(buster.console, "log"); 29 | var runner = buster.testRunner.create(); 30 | 31 | runner.on("suite:end", function (results) { 32 | assert.calledOnce(buster.console.log); 33 | assert.calledWith(buster.console.log, "Hey man"); 34 | done(); 35 | }); 36 | 37 | runner.runSuite([buster.testCase("Sinon assertion count", { 38 | "should count two assertions": function () { 39 | this.log("Hey man"); 40 | } 41 | })]); 42 | }, 43 | 44 | "should fail when no exception": function () { 45 | this.stub(buster.referee, "emit"); 46 | 47 | try { 48 | buster.referee.throwOnFailure = true; 49 | assert.exception(function () {}); 50 | throw new Error("Didn't throw"); 51 | } catch (e) { 52 | buster.referee.emit.restore(); 53 | refute.match(e.message, "Didn't throw"); 54 | refute.match(e.message, "toString"); 55 | } finally { 56 | if (buster.referee.emit.restore) { 57 | buster.referee.emit.restore(); 58 | } 59 | } 60 | }, 61 | 62 | "should not expose global objects and functions": function () { 63 | refute.defined(global.buster); 64 | refute.defined(global.assert); 65 | refute.defined(global.refute); 66 | refute.defined(global.expect); 67 | } 68 | }); 69 | -------------------------------------------------------------------------------- /doc/samples/spec.js: -------------------------------------------------------------------------------- 1 | if (typeof require != "undefined") { 2 | var buster = require("../../lib/buster"); 3 | var when = require("when"); 4 | var assert = require("assert"); 5 | } 6 | 7 | buster.spec.expose(); 8 | var assertThat = buster.assert.that; 9 | 10 | var spec = describe("Sample spec", function () { 11 | should("pass simple assertion", function () { 12 | assertThat(true).isTrue(); 13 | }); 14 | 15 | should("fail when test throws", function () { 16 | throw new Error("Ooops!"); 17 | }); 18 | 19 | should("fail test", function () { 20 | assertThat("Something").equals("Other"); 21 | }); 22 | 23 | describe("nested", function () { 24 | should("do it", function () { 25 | assertThat(true).isTrue(); 26 | }); 27 | }); 28 | }); 29 | 30 | var spec2 = describe("Another test", function () { 31 | before(function () { 32 | this.value = 42; 33 | }); 34 | 35 | should("passes simple assertion", function () { 36 | assertThat(this.value).equals(42); 37 | }); 38 | 39 | should("passes true assertion", function () { 40 | assertThat(true).isTrue(); 41 | }); 42 | 43 | should("passes node assertion", function () { 44 | assert.ok(true); 45 | }); 46 | 47 | should("fail node assertion", function () { 48 | buster.console.warn("This is unsafe"); 49 | assert.ok(false); 50 | }); 51 | 52 | shouldEventually("is asynchronous", function () { 53 | var deferred = when.defer(); 54 | 55 | setTimeout(function () { 56 | buster.log("Async"); 57 | deferred.resolver.resolve(); 58 | }, 1000); 59 | 60 | return deferred.promise; 61 | }), 62 | 63 | should("puts the lotion on its skin or else it gets the hose again", function () { 64 | assertThat(true).isTrue(); 65 | }); 66 | }); 67 | 68 | var spec3 = describe("Third one", function () { 69 | should("should do #1", function () { assertThat(true).isTrue(); }); 70 | should("should do #2", function () { assertThat(true).isTrue(); }); 71 | should("should do #3", function () { assertThat(true).isTrue(); }); 72 | should("should do #4", function () { assertThat(true).isTrue(); }); 73 | should("should do #5", function () { assertThat(true).isTrue(); }); 74 | should("should do #6", function () { assertThat(true).isTrue(); }); 75 | should("should do #7", function () { assertThat(true).isTrue(); }); 76 | should("should do #8", function () { assertThat(true).isTrue(); }); 77 | }); 78 | -------------------------------------------------------------------------------- /lib/buster/framework-extension.js: -------------------------------------------------------------------------------- 1 | // WHOA! This will change before 1.0 2 | 3 | var BUSTER_VERSION = require("../../package.json").version; 4 | 5 | function resolveModules(resourceSet, modules) { 6 | return modules.map(function (module) { 7 | var moduleName = module[0]; 8 | var moduleFile = module[1]; 9 | var resourcePath = "/buster/" + moduleFile; 10 | var absolutePath = require.resolve(moduleName + moduleFile); 11 | resourceSet.addFileResource(absolutePath, { path: resourcePath }); 12 | return resourcePath; 13 | }); 14 | } 15 | 16 | var when = require("when"); 17 | 18 | var NO_CACHE_HEADERS = { 19 | "Cache-Control": "no-cache", 20 | "Pragma": "no-cache", 21 | "Expires": "0" 22 | }; 23 | 24 | function loadTestFramework(configuration) { 25 | configuration.on("load:framework", function (rs) { 26 | var files = resolveModules(rs, [ 27 | ["when/", "dist/browser/when.js"], 28 | ["lodash/", "index.js"], 29 | ["async/lib/", "async.js"], 30 | ["platform/", "platform.js"], 31 | ["../", "buster/amd-shim.js"], 32 | ["bane/lib/", "bane.js"], 33 | ["samsam/lib/", "samsam.js"], 34 | ["evented-logger/lib/", "evented-logger.js"], 35 | ["referee/lib/", "expect.js"], 36 | ["referee/lib/", "referee.js"], 37 | ["formatio/lib/", "formatio.js"], 38 | ["sinon/pkg/", "sinon.js"], 39 | ["buster-test/lib/", "seedrandom.js"], 40 | ["buster-test/lib/", "browser-env.js"], 41 | ["buster-test/lib/", "test-context.js"], 42 | ["buster-test/lib/", "spec.js"], 43 | ["buster-test/lib/", "test-case.js"], 44 | ["buster-test/lib/", "test-runner.js"], 45 | ["buster-test/lib/", "reporters/json-proxy.js"], 46 | ["referee-sinon/lib/", "referee-sinon.js"], 47 | ["buster-sinon/lib/", "buster-sinon.js"], 48 | 49 | // Slightly silly, will be fixed 50 | ["../", "buster/buster-wiring.js"], 51 | ["../", "buster/browser-wiring.js"] 52 | ]); 53 | 54 | var ieFiles = resolveModules(rs, [ 55 | ["sinon/pkg/", "sinon-ie.js"] 56 | ]); 57 | 58 | var compatResourceName = "/buster/compat-" + BUSTER_VERSION + ".js"; 59 | var bundleResourceName = "/buster/bundle-" + BUSTER_VERSION + ".js"; 60 | 61 | when.all([ 62 | rs.addResource({ 63 | path: compatResourceName, 64 | combine: ieFiles, 65 | headers: NO_CACHE_HEADERS 66 | }), 67 | rs.addResource({ 68 | path: bundleResourceName, 69 | combine: files, 70 | headers: NO_CACHE_HEADERS 71 | }) 72 | ]).then(function () { 73 | rs.loadPath.prepend(compatResourceName); 74 | rs.loadPath.prepend(bundleResourceName); 75 | }); 76 | }.bind(this)); 77 | } 78 | 79 | module.exports = { 80 | configure: function (configuration) { loadTestFramework(configuration); } 81 | }; 82 | -------------------------------------------------------------------------------- /doc/samples/test-case.js: -------------------------------------------------------------------------------- 1 | if (typeof require != "undefined") { 2 | var buster = require("../../lib/buster"); 3 | var when = require("when"); 4 | } 5 | 6 | buster.testCase("Sample test", { 7 | setUp: function () { 8 | this.a = 1; 9 | }, 10 | 11 | "should use sinon": function () { 12 | var obj = { meth: function () {} }; 13 | this.spy(obj, "meth"); 14 | buster.assert.called(obj.meth); 15 | }, 16 | 17 | "should use sinon successfully": function () { 18 | var obj = { meth: function () {} }; 19 | this.spy(obj, "meth"); 20 | 21 | obj.meth(); 22 | buster.log("Just called a spy, tihi"); 23 | 24 | buster.assert.called(obj.meth); 25 | }, 26 | 27 | "//should pass simple assertion": function () { 28 | buster.console.log("Trying shit out"); 29 | buster.assert(true); 30 | }, 31 | 32 | "should fail when test throws": function () { 33 | buster.console.warn("This gonna burn"); 34 | throw new Error("Ooops!"); 35 | }, 36 | 37 | "should fail test": function () { 38 | buster.assert.equals("Something", "Other"); 39 | }, 40 | 41 | "should fail after timing out": function () { 42 | setTimeout(function () { 43 | throw new Error("Ah, crap!"); 44 | }, 50); 45 | }, 46 | 47 | "look ma, I'm asynchronous": function () { 48 | var deferred = when.defer(); 49 | 50 | setTimeout(function () { 51 | buster.assert(true); 52 | deferred.resolver.resolve(); 53 | }, 200); 54 | 55 | return deferred.promise; 56 | }, 57 | 58 | "look ma, I'm implicitly asynchronous": function (done) { 59 | buster.assert(true); 60 | buster.console.error("This is not good"); 61 | 62 | setTimeout(function () { 63 | buster.log("Failing assertion asynchronously"); 64 | buster.assert(false); 65 | done(); 66 | }, 50); 67 | }, 68 | 69 | "context": { 70 | "should be awesome": function () { 71 | buster.assert.equals(1, 1); 72 | }, 73 | 74 | "inside here": { 75 | setUp: function () { 76 | this.a += 1; 77 | }, 78 | 79 | "should do it more": function () { 80 | buster.assert.equals(2, this.a); 81 | } 82 | } 83 | }, 84 | 85 | "http stuff": { 86 | requiresSupportForAny: { 87 | "node http client": function () { 88 | return typeof require == "undefined"; 89 | }, 90 | 91 | "another thing": false 92 | }, 93 | 94 | "should do it": function () { 95 | buster.assert(false); 96 | } 97 | } 98 | }); 99 | 100 | var testCase2 = buster.testCase("Another test", { 101 | setUp: function (done) { 102 | setTimeout(function () { 103 | done(); 104 | }, 200); 105 | }, 106 | 107 | "should pass simple assertion": function () { 108 | buster.assert(true); 109 | }, 110 | 111 | "should fail when test throws": function () { 112 | throw new Error("Ooops!"); 113 | }, 114 | 115 | "should fail test": function () { 116 | buster.assert.equals("Something", "Other"); 117 | }, 118 | 119 | "some context": { 120 | setUp: function (done) { 121 | setTimeout(function() { 122 | done(); 123 | }, 240); 124 | }, 125 | 126 | tearDown: function (done) { 127 | setTimeout(function() { 128 | done(); 129 | }, 100); 130 | }, 131 | 132 | "some other nested test": function () { 133 | } 134 | } 135 | }); 136 | -------------------------------------------------------------------------------- /lib/buster/buster-wiring.js: -------------------------------------------------------------------------------- 1 | (function (glbl, buster, sinon) { 2 | if (typeof require == "function" && typeof module == "object") { 3 | var busterTest = require("buster-test"); 4 | var path = require("path"); 5 | var fs = require("fs"); 6 | var referee = require("referee"); 7 | var stackFilter = require("stack-filter"); 8 | sinon = require("sinon"); 9 | 10 | buster = module.exports = { 11 | testCase: busterTest.testCase, 12 | spec: busterTest.spec, 13 | testRunner: busterTest.testRunner, 14 | testContext: busterTest.testContext, 15 | reporters: busterTest.reporters, 16 | autoRun: busterTest.autoRun, 17 | referee: referee, 18 | assertions: referee, 19 | formatio: require("formatio"), 20 | eventedLogger: require("evented-logger"), 21 | frameworkExtension: require("./framework-extension"), 22 | wiringExtension: require("./wiring-extension"), 23 | sinon: sinon, 24 | busterSinon: require("buster-sinon"), 25 | refereeSinon: require("referee-sinon") 26 | }; 27 | 28 | Object.defineProperty(buster, "VERSION", { 29 | get: function () { 30 | if (!this.version) { 31 | var pkgJSON = path.resolve(__dirname, "..", "package.json"); 32 | var pkg = JSON.parse(fs.readFileSync(pkgJSON, "utf8")); 33 | this.version = pkg.version; 34 | } 35 | 36 | return this.version; 37 | } 38 | }); 39 | } 40 | 41 | var logFormatter = buster.formatio.configure({ quoteStrings: false }); 42 | var asciiFormat = function () { 43 | return logFormatter.ascii.apply(logFormatter, arguments); 44 | }; 45 | 46 | if (asciiFormat) { 47 | buster.console = buster.eventedLogger.create({ 48 | formatter: asciiFormat, 49 | logFunctions: true 50 | }); 51 | } 52 | 53 | buster.log = function () { 54 | return buster.console.log.apply(buster.console, arguments); 55 | }; 56 | 57 | buster.captureConsole = function () { 58 | glbl.console = buster.console; 59 | 60 | if (glbl.console !== buster.console) { 61 | glbl.console.log = buster.log; 62 | } 63 | }; 64 | 65 | if (asciiFormat) { 66 | buster.referee.format = asciiFormat; 67 | } 68 | 69 | buster.assert = buster.referee.assert; 70 | buster.refute = buster.referee.refute; 71 | buster.expect = buster.referee.expect; 72 | 73 | var hasDefineProperty = false; 74 | 75 | try { 76 | if (Object.defineProperty && Object.defineProperty({}, "x", {})) { 77 | hasDefineProperty = true; 78 | } 79 | } catch (e) {} 80 | 81 | if (hasDefineProperty) { 82 | Object.defineProperty(buster, "assertions", { 83 | get: function () { 84 | console.log("buster.assertions is provided for backwards compatibility. Please update your code to use buster.referee"); 85 | return buster.referee; 86 | } 87 | }); 88 | 89 | Object.defineProperty(buster, "format", { 90 | get: function () { 91 | console.log("buster.format is provided for backwards compatibility. Please update your code to use buster.formatio"); 92 | return buster.formatio; 93 | } 94 | }); 95 | } else { 96 | buster.assertions = buster.referee; 97 | buster.format = buster.formatio; 98 | } 99 | 100 | buster.testRunner.onCreate(function (runner) { 101 | buster.referee.on("pass", function () { 102 | runner.assertionPass(); 103 | }); 104 | 105 | buster.referee.on("failure", function (err) { 106 | runner.assertionFailure(err); 107 | }); 108 | 109 | runner.on("test:async", function () { 110 | buster.referee.throwOnFailure = true; 111 | }); 112 | 113 | runner.on("test:setUp", function () { 114 | buster.referee.throwOnFailure = true; 115 | }); 116 | 117 | runner.on("context:start", function (context) { 118 | if (context.testCase) { 119 | context.testCase.log = buster.log; 120 | } 121 | }); 122 | }); 123 | 124 | var sf = typeof stackFilter !== "undefined" && stackFilter; 125 | buster.busterSinon(sinon, buster, sf, logFormatter); 126 | buster.refereeSinon(buster.referee, sinon); 127 | }(typeof global != "undefined" ? global : this, 128 | typeof buster == "object" ? buster : null, 129 | typeof sinon == "object" ? sinon : null)); 130 | -------------------------------------------------------------------------------- /run-integration-tests.js: -------------------------------------------------------------------------------- 1 | if (process.env["NO_INTEGRATION"]) { 2 | console.log("Found NO_INTEGRATION environment variable - skipping tests"); 3 | process.exit(0); 4 | } 5 | 6 | var when = require('when'); 7 | var path = require('path'); 8 | var fs = require('fs'); 9 | var nodefn = require('when/node/function'); 10 | var sequence = require('when/sequence'); 11 | var cp = require('child_process'); 12 | 13 | var BUSTER_SERVER_BIN = path.join(__dirname, "bin", "buster-server"); 14 | var BUSTER_TEST_BIN = path.join(__dirname, "bin", "buster-test"); 15 | 16 | function die(code) { 17 | if (code > 0) { 18 | console.error("Exiting with code " + code); 19 | } else { 20 | console.log("Exiting with code " + code); 21 | } 22 | process.exit(code); 23 | } 24 | 25 | function startServer() { 26 | var started = false; 27 | var serverExited = when.defer(); 28 | var serverStarted = when.defer(); 29 | 30 | var serverProcess = cp.spawn("node", [BUSTER_SERVER_BIN, "-c"], {detached: true}); 31 | 32 | serverProcess.stdout.on('data', function (data) { 33 | var msg = data.toString(); 34 | if (!started && /Headless browser was captured/.test(msg)) { 35 | started = true; 36 | serverStarted.resolve(); 37 | clearTimeout(capturedTimeout); 38 | } 39 | console.log("[buster-server][stdout]", msg); 40 | }); 41 | 42 | serverProcess.stderr.on('data', function (data) { 43 | console.error("[buster-server][stderr]", data.toString()); 44 | }); 45 | 46 | serverProcess.on('exit', function (code) { 47 | if (!started) { 48 | serverStarted.reject(new Error("buster-server crashed")); 49 | } 50 | 51 | if (code > 0 || code === 143) { // see: https://github.com/nodejs/node-v0.x-archive/issues/8667 52 | console.log("buster-server exited with code " + code); 53 | } 54 | 55 | serverExited.resolve(code === 143 ? 0 : code); 56 | }); 57 | 58 | var capturedTimeout = setTimeout(function () { 59 | serverStarted.reject(new Error("Timed out while waiting for buster-server to capture a headless browser")); 60 | }, 5000); 61 | 62 | return { 63 | started: serverStarted.promise, 64 | exited: serverExited.promise, 65 | kill: function () { 66 | var isWin = /^win/.test(process.platform); 67 | if (isWin) { 68 | cp.spawn("taskkill", ["/pid", serverProcess.pid, '/f', '/t']); 69 | } else { 70 | process.kill(-serverProcess.pid); 71 | } 72 | } 73 | } 74 | } 75 | 76 | function readArgs(dirName) { 77 | var fullPath = path.join(__dirname, "test", "integration", dirName); 78 | return nodefn.call(fs.readFile.bind(fs), path.join(fullPath, "args.txt")) 79 | .then(function (contents) { 80 | return { 81 | fullPath: fullPath, 82 | args: contents.toString() 83 | }; 84 | }, function (err) { 85 | if (err && err.code === "ENOENT") { 86 | return { 87 | fullPath: fullPath, 88 | args: undefined 89 | }; 90 | } 91 | throw err; 92 | }); 93 | } 94 | 95 | function readTestFolders() { 96 | return nodefn.call(fs.readdir.bind(fs), path.join(__dirname, "test", "integration")) 97 | .then(function (integrationTests) { 98 | return when.map(integrationTests, readArgs) 99 | }) 100 | } 101 | 102 | function getRunTestFn(integrationTest) { 103 | return function () { 104 | var deferred = when.defer(); 105 | var cmd = ["BUSTER_REPORTER=specification", "node", BUSTER_TEST_BIN]; 106 | if (integrationTest.args) { 107 | cmd.push(integrationTest.args); 108 | } 109 | var execOptions = { 110 | cwd: integrationTest.fullPath 111 | }; 112 | cp.exec(cmd.join(" "), execOptions, function (err, stdout, stderr) { 113 | if (err) { 114 | console.error(err); 115 | } 116 | if (stdout.length) { 117 | console.log("[buster-test][stdout][" + integrationTest.fullPath + "]", stdout); 118 | } 119 | if (stderr.length) { 120 | console.log("[buster-test][stderr][" + integrationTest.fullPath + "]", stderr); 121 | } 122 | deferred.resolver.resolve(err ? 1 : 0); 123 | }); 124 | return deferred.promise; 125 | } 126 | } 127 | 128 | function runIntegrationTests() { 129 | var server = startServer(); 130 | 131 | process.on('SIGINT', function () { 132 | server.kill(); 133 | die(1); 134 | }); 135 | 136 | server.started 137 | .then(function () { 138 | return readTestFolders(); 139 | }) 140 | .then(function (tests) { 141 | var deferred = when.defer(); // @todo: replace with when.race() 142 | 143 | sequence(tests.map(getRunTestFn)) 144 | .then(deferred.resolver.resolve); 145 | 146 | server.exited 147 | .then(function () { 148 | deferred.resolver.reject(new Error("Server exited before test results completed!")); 149 | }); 150 | 151 | return deferred.promise; 152 | }) 153 | .then(function (testResults) { 154 | server.kill(); 155 | return when.all(testResults.concat(server.exited)); 156 | }) 157 | .then(function (exitCodes) { 158 | var nonZero = exitCodes.filter(function (i) { 159 | return i > 0 && i !== 143 160 | }); 161 | 162 | if (nonZero.length > 0) { 163 | console.log("One or more processes exited with non-zero exit code", exitCodes); 164 | die(1); 165 | return; 166 | } 167 | 168 | die(0); 169 | }) 170 | .then(null, function (err) { 171 | console.error(err); 172 | server.kill(); 173 | die(1); 174 | }); 175 | } 176 | 177 | runIntegrationTests(); 178 | -------------------------------------------------------------------------------- /lib/buster/browser-wiring.js: -------------------------------------------------------------------------------- 1 | (function (B) { 2 | B.env = B.env || {}; 3 | 4 | var errorQueue = []; 5 | 6 | // Need to bind this immediately, so we can report errors that happen after 7 | // this file has loaded, but before the test runner is initialized 8 | window.onerror = function () { 9 | errorQueue.push(arguments); 10 | }; 11 | 12 | // Globally uncaught errors will be emitted as messages through 13 | // the test runner 14 | function handleUncaughtError(runner, args) { 15 | if (args.length >= 3) { 16 | var message = args[0]; 17 | var url = args[1]; 18 | var line = args[2]; 19 | 20 | var cp = B.env.contextPath || window.location; 21 | var index = (url || "").indexOf(cp); 22 | if (index >= 0) { 23 | url = "." + url.slice(index + cp.length); 24 | } 25 | 26 | if (line === 1 && message === "Error loading script") { 27 | message = "Unable to load script " + url; 28 | } else if (url) { 29 | message = url + ":" + line + " " + message; 30 | } else { 31 | message = message.replace("uncaught exception: ", ""); 32 | } 33 | } 34 | 35 | runner.emit("uncaughtException", { 36 | name: "UncaughtError", 37 | message: message 38 | }); 39 | 40 | return true; 41 | }; 42 | 43 | function uncaughtErrors(runner) { 44 | for (var i = 0, l = errorQueue.length; i < l; ++i) { 45 | handleUncaughtError(runner, errorQueue[i]); 46 | } 47 | 48 | window.onerror = function () { 49 | handleUncaughtError(runner, arguments); 50 | }; 51 | } 52 | 53 | // Emit messages from the evented logger buster.console through 54 | // the test runner 55 | function logger(runner) { 56 | B.console.on("log", function (msg) { 57 | runner.emit("log", msg); 58 | }); 59 | } 60 | 61 | // Collect test cases and specs created with buster.testCase 62 | // and buster.spec.describe 63 | function testContexts() { 64 | var contexts = []; 65 | B.addTestContext = function (context) { contexts.push(context); }; 66 | B.testContext.on("create", B.addTestContext); 67 | return contexts; 68 | } 69 | 70 | // Clear scripts and use the browserEnv object from buster-test to 71 | // reset the document between tests runs 72 | function documentState(runner) { 73 | var scripts = document.getElementsByTagName("script"), script; 74 | while ((script = scripts[0])) { 75 | script.parentNode.removeChild(script); 76 | } 77 | var env = B.browserEnv.create(document.body); 78 | env.listen(runner); 79 | } 80 | 81 | function shouldAutoRun(config) { 82 | var autoRunPropertyIsSet = config.hasOwnProperty("autoRun"); 83 | return config.autoRun || !autoRunPropertyIsSet; 84 | } 85 | 86 | function shouldResetDoc(config) { 87 | var resetDocumentPropertyIsSet = config.hasOwnProperty("resetDocument"); 88 | return config.resetDocument || !resetDocumentPropertyIsSet; 89 | } 90 | 91 | // Wire up the test runner. It will start running tests when 92 | // the environment is ready and when we've been told to run. 93 | // Note that run() and ready() may occur in any order, and 94 | // we cannot do anything until both have happened. 95 | // 96 | // When running tests with buster-server, we'll be ready() when 97 | // the server sends the "tests:run" message. This message is sent 98 | // by the server when it receives the "loaded all scripts" message 99 | // from the browser. We'll usually run as soon as we're ready. 100 | // However, if the autoRun option is false, we will not run 101 | // until buster.run() is explicitly called. 102 | // 103 | // For static browser runs, the environment is ready() when 104 | // ready() is called, which happens after all files have been 105 | // loaded in the browser. Tests will run immediately for autoRun: 106 | // true, and on run() otherwise. 107 | // 108 | function testRunner(runner) { 109 | var ctxts = B.wire.testContexts(); 110 | var ready, started, alreadyRunning, config; 111 | 112 | function attemptRun() { 113 | if (!ready || !started || alreadyRunning) { return; } 114 | alreadyRunning = true; 115 | if (typeof runner === "function") { runner = runner(); } 116 | if (shouldResetDoc(config)) { B.wire.documentState(runner); } 117 | if (config.captureConsole) { B.captureConsole(); } 118 | 119 | for (var prop in config) { 120 | runner[prop] = config[prop]; 121 | } 122 | 123 | runner.runSuite(B.testContext.compile(ctxts, config.filters)); 124 | } 125 | 126 | return { 127 | ready: function (options) { 128 | config = options || {}; 129 | ready = true; 130 | started = started || shouldAutoRun(config); 131 | attemptRun(); 132 | }, 133 | 134 | run: function () { 135 | started = true; 136 | attemptRun(); 137 | } 138 | }; 139 | } 140 | 141 | B.wire = function (testRunner) { 142 | var wiring = B.wire.testRunner(testRunner); 143 | B.ready = wiring.ready; 144 | B.run = wiring.run; 145 | return wiring; 146 | }; 147 | 148 | B.wire.uncaughtErrors = uncaughtErrors; 149 | B.wire.logger = logger; 150 | B.wire.testContexts = testContexts; 151 | B.wire.documentState = documentState; 152 | B.wire.testRunner = testRunner; 153 | }(buster)); 154 | 155 | // TMP Performance fix 156 | (function () { 157 | var i = 0; 158 | 159 | buster.nextTick = function (cb) { 160 | i += 1; 161 | 162 | if (i === 10) { 163 | setTimeout(function () { 164 | cb(); 165 | }, 0); 166 | 167 | i = 0; 168 | } else { 169 | cb(); 170 | } 171 | }; 172 | }()); 173 | 174 | buster.sinon = sinon; 175 | try { 176 | delete this.define; 177 | delete this.when; 178 | delete this.async; 179 | delete this.platform; 180 | delete this._; 181 | } catch (e) { 182 | this['define'] = undefined; 183 | this['when'] = undefined; 184 | this['async'] = undefined; 185 | this['platform'] = undefined; 186 | this['_'] = undefined; 187 | } 188 | -------------------------------------------------------------------------------- /test/integration/browser/capture-server-wiring-test.js: -------------------------------------------------------------------------------- 1 | (function (B) { 2 | if (typeof require == "function" && typeof module == "object") { 3 | return; 4 | } 5 | 6 | function setUp() { 7 | this.emitter = createRemoteEmitter(); 8 | this.readyListener = sinon.spy(); 9 | this.emitter.on("ready", this.readyListener); 10 | B.configureTestClient(this.emitter); 11 | 12 | this.bnt = B.nextTick; 13 | sinon.spy(B.testRunner, "runSuite"); 14 | 15 | B.nextTick = function (callback) { 16 | callback(); 17 | }; 18 | } 19 | 20 | function tearDown() { 21 | B.nextTick = this.bnt; 22 | B.testRunner.runSuite.restore(); 23 | } 24 | 25 | B.testCase("BrowserWiringTest", { 26 | setUp: setUp, 27 | tearDown: tearDown, 28 | 29 | "//should connect bayeux emitter": function () { 30 | B.referee.assert(this.emitter.connect.calledOnce); 31 | }, 32 | 33 | "should not emit ready immediately": function () { 34 | B.referee.refute(this.readyListener.called); 35 | }, 36 | 37 | "//should not emit ready when connected": function () { 38 | this.emitter.connect.args[0][0](); 39 | 40 | B.referee.refute(this.readyListener.calledOnce); 41 | }, 42 | 43 | "//should emit ready when calling buster.ready": function () { 44 | this.emitter.connect.args[0][0](); 45 | B.ready(); 46 | 47 | B.referee.assert(this.readyListener.calledOnce); 48 | }, 49 | 50 | "//should not emit ready before client is connected": function () { 51 | B.ready(); 52 | 53 | B.referee.refute(this.readyListener.calledOnce); 54 | }, 55 | 56 | "//should emit ready when connected after calling buster.ready": function () { 57 | B.ready(); 58 | this.emitter.connect.args[0][0](); 59 | 60 | B.referee.assert(this.readyListener.calledOnce); 61 | }, 62 | 63 | "//should emit user agent string with ready event": function () { 64 | this.emitter.connect.args[0][0](); 65 | B.ready(); 66 | 67 | B.referee.assert(/Mozilla/.test(this.readyListener.args[0][0].data)); 68 | }, 69 | 70 | "should set up console and buster.log shortcut": function () { 71 | B.referee.assert.isObject(B.console); 72 | B.referee.assert.isFunction(B.log); 73 | }, 74 | 75 | "should format log messages nicely": function () { 76 | var message; 77 | B.console.on("log", function (msg) { message = msg.message }); 78 | B.console.log("Hello world", { id: 42 }); 79 | 80 | B.referee.assert.equals("Hello world { id: 42 }", message); 81 | }, 82 | 83 | "should log messages through buster.log": function () { 84 | var message; 85 | B.console.on("log", function (msg) { message = msg.message }); 86 | B.log("Hello world", { id: 42 }); 87 | 88 | B.assert.equals(message, "Hello world { id: 42 }"); 89 | }, 90 | 91 | "//should send log messages over the wire": function () { 92 | var listener = sinon.spy(); 93 | this.emitter.on("log", listener); 94 | 95 | B.console.warn("Hello world", { id: 42 }); 96 | B.referee.assert(listener.calledOnce); 97 | 98 | var msg = listener.args[0][0]; 99 | B.referee.assert.equals("log", msg.topic); 100 | 101 | B.referee.assert.equals({ 102 | message: "Hello world { id: 42 }", 103 | level: "warn" 104 | }, msg.data); 105 | }, 106 | 107 | "//should start running tests when emitting tests:run": function () { 108 | var listener = sinon.spy(); 109 | this.emitter.on("suite:start", listener); 110 | 111 | B.testCase("SomeTest", { "should do it": function () {} }); 112 | this.emitter.emit("tests:run", { autoRun: true }); 113 | 114 | B.referee.assert(listener.calledOnce); 115 | }, 116 | 117 | "//should run specs when emitting tests:run": function () { 118 | var listener = sinon.spy(); 119 | this.emitter.on("context:start", listener); 120 | 121 | B.spec.describe("Spec", function () { 122 | B.spec.it("does it", function () {}); 123 | }); 124 | 125 | this.emitter.emit("tests:run", { autoRun: true }); 126 | 127 | B.referee.assert(listener.calledOnce); 128 | B.referee.assert.equals(listener.args[0][0].data.name, "Spec"); 129 | }, 130 | 131 | "//should run parsable context when emitting tests:run": function () { 132 | var context = { name: "Parsed", tests: [{ name: "Yay" }], contexts: [] }; 133 | 134 | B.addTestContext({ 135 | parse: function () { 136 | return context; 137 | } 138 | }); 139 | 140 | this.emitter.emit("tests:run", { autoRun: true }); 141 | 142 | B.referee.assert(B.testRunner.runSuite.calledOnce); 143 | B.referee.assert.equals(B.testRunner.runSuite.args[0][0], [context]); 144 | }, 145 | 146 | "//should create test runner with options": function () { 147 | this.emitter.emit("tests:run", { 148 | timeout: 25 149 | }); 150 | 151 | B.referee.assert.equals(25, B.wiredRunner.timeout); 152 | }, 153 | 154 | "should configure assertions to throw": function () { 155 | this.emitter.emit("tests:run"); 156 | B.referee.assert(B.assertions.throwOnFailure); 157 | }, 158 | 159 | "//should count assertions": function () { 160 | var counts = []; 161 | 162 | B.testCase("AssertionCountTest", { 163 | tearDown: function () { 164 | counts.push(B.wiredRunner.assertionCount()); 165 | }, 166 | 167 | "test #1": function () { 168 | B.referee.assert(true); 169 | }, 170 | 171 | "test #2": function () { 172 | B.referee.assert(true); 173 | B.referee.assert(true); 174 | } 175 | }); 176 | 177 | this.emitter.emit("tests:run"); 178 | 179 | B.referee.assert.equals(counts.sort(), [1, 2]); 180 | }, 181 | 182 | "//should filter contexts prior to running": function () { 183 | var tests = [this.spy(), this.spy()]; 184 | B.testCase("AssertionCountTest", { 185 | "test #1": tests[0], 186 | "test #2": tests[1] 187 | }); 188 | 189 | this.emitter.emit("tests:run", { autoRun: true, filters: ["#1"] }); 190 | 191 | B.referee.assert(tests[0].calledOnce); 192 | B.referee.refute(tests[1].called); 193 | }, 194 | 195 | "//should capture console": function () { 196 | this.stub(B, "captureConsole"); 197 | this.emitter.emit("tests:run", { captureConsole: true }); 198 | B.referee.assert(B.captureConsole.calledOnce); 199 | }, 200 | 201 | "should not always capture console": function () { 202 | this.stub(B, "captureConsole"); 203 | this.emitter.emit("tests:run", { captureConsole: false }); 204 | B.referee.assert(!B.captureConsole.called); 205 | } 206 | }); 207 | 208 | B.testCase("BrowserWiringAutoRunFalseTest", { 209 | setUp: function () { 210 | setUp.call(this); 211 | 212 | this.listener = sinon.spy(); 213 | this.emitter.on("suite:start", this.listener); 214 | B.testCase("SomeTest", { "should do it": function () {} }); 215 | }, 216 | 217 | tearDown: tearDown, 218 | 219 | "should not start running tests on tests:run when autoRun is false": 220 | function () { 221 | this.emitter.emit("tests:run", { autoRun: false }); 222 | 223 | B.referee.refute(this.listener.calledOnce); 224 | }, 225 | 226 | "//should start running tests when calling run after tests:run": function () { 227 | this.emitter.emit("tests:run", { autoRun: false }); 228 | B.run(); 229 | 230 | B.referee.assert(this.listener.calledOnce); 231 | }, 232 | 233 | "//should start running tests when calling run before tests:run": function () { 234 | B.run(); 235 | this.emitter.emit("tests:run", { autoRun: false }); 236 | 237 | B.referee.assert(this.listener.calledOnce); 238 | }, 239 | 240 | "should remove all script tags": function () { 241 | B.run(); 242 | this.emitter.emit("tests:run", { autoRun: false }); 243 | 244 | B.referee.assert.equals(0, document.body.getElementsByTagName("script").length); 245 | } 246 | }); 247 | }(buster)); 248 | -------------------------------------------------------------------------------- /test/integration/browser/browser-wiring-test.js: -------------------------------------------------------------------------------- 1 | (function (B) { 2 | if (typeof require == "function" && typeof module == "object") { 3 | return; 4 | } 5 | 6 | function setUp() { 7 | this.runner = B.bane.createEventEmitter(); 8 | this.runner.runSuite = sinon.spy(); 9 | this.bnt = B.nextTick; 10 | this.sandbox = sinon.sandbox.create(); 11 | 12 | B.nextTick = function (callback) { 13 | callback(); 14 | }; 15 | } 16 | 17 | function tearDown() { 18 | B.nextTick = this.bnt; 19 | this.sandbox.restore(); 20 | } 21 | 22 | B.testCase("BrowserWiringLoggerTest", { 23 | setUp: setUp, 24 | tearDown: tearDown, 25 | 26 | "emits console log messages through runner": function () { 27 | var listener = sinon.spy(); 28 | this.runner.on("log", listener); 29 | 30 | B.wire.logger(this.runner); 31 | B.console.log("Hey man"); 32 | 33 | B.referee.assert(listener.calledOnce); 34 | } 35 | }); 36 | 37 | B.testCase("BrowserWiringContextsTest", { 38 | setUp: setUp, 39 | tearDown: tearDown, 40 | 41 | "collects testCase": function () { 42 | var contexts = B.wire.testContexts(); 43 | B.testCase("Something", {}); 44 | 45 | B.referee.assert.equals(contexts.length, 1); 46 | B.referee.assert.equals(contexts[0].name, "Something"); 47 | }, 48 | 49 | "collects spec": function () { 50 | var contexts = B.wire.testContexts(); 51 | B.spec.describe("Something", function () {}); 52 | 53 | B.referee.assert.equals(contexts.length, 1); 54 | B.referee.assert.equals(contexts[0].name, "Something"); 55 | } 56 | }); 57 | 58 | B.testCase("BrowserWiringDocumentStateTest", { 59 | setUp: setUp, 60 | tearDown: tearDown, 61 | 62 | "should remove all script tags to avoid re-downloading scripts": function () { 63 | B.wire.documentState(this.runner); 64 | 65 | B.referee.assert.equals(document.getElementsByTagName("script").length, 0); 66 | }, 67 | 68 | "should clear document when test succeeds": function () { 69 | B.wire.documentState(this.runner); 70 | document.body.appendChild(document.createElement("h1")); 71 | this.runner.emit("test:success", {}); 72 | 73 | B.referee.assert.equals(document.getElementsByTagName("h1").length, 0); 74 | }, 75 | 76 | "should clear document when test fails": function () { 77 | B.wire.documentState(this.runner); 78 | document.body.appendChild(document.createElement("h1")); 79 | this.runner.emit("test:failure", {}); 80 | 81 | B.referee.assert.equals(document.getElementsByTagName("h1").length, 0); 82 | }, 83 | 84 | "should clear document when test errors": function () { 85 | B.wire.documentState(this.runner); 86 | document.body.appendChild(document.createElement("h1")); 87 | this.runner.emit("test:error", {}); 88 | 89 | B.referee.assert.equals(document.getElementsByTagName("h1").length, 0); 90 | }, 91 | 92 | "should clear document when test times out": function () { 93 | B.wire.documentState(this.runner); 94 | document.body.appendChild(document.createElement("h1")); 95 | this.runner.emit("test:timeout", {}); 96 | 97 | B.referee.assert.equals(document.getElementsByTagName("h1").length, 0); 98 | }, 99 | 100 | "should use snapshot from suite:start": function () { 101 | B.wire.documentState(this.runner); 102 | document.body.appendChild(document.createElement("h1")); 103 | this.runner.emit("suite:start", {}); 104 | document.body.appendChild(document.createElement("h2")); 105 | this.runner.emit("test:timeout", {}); 106 | 107 | B.referee.assert.equals(document.getElementsByTagName("h1").length, 1); 108 | B.referee.assert.equals(document.getElementsByTagName("h2").length, 0); 109 | } 110 | }); 111 | 112 | B.testCase("BrowserWiringTestRunnerTest", { 113 | setUp: setUp, 114 | tearDown: tearDown, 115 | 116 | "should auto-run all tests when ready": function () { 117 | var wire = B.wire.testRunner(this.runner); 118 | B.testCase("Thing", { doIt: function () {} }); 119 | B.spec.describe("Other thing", function () { 120 | B.spec.it("does stuff", function () {}); 121 | }); 122 | 123 | wire.ready({}); 124 | wire.run(); 125 | 126 | B.referee.assert(this.runner.runSuite.calledOnce); 127 | B.referee.assert.equals(this.runner.runSuite.args[0][0].length, 2); 128 | }, 129 | 130 | "should copy config properties to test runner": function () { 131 | var wire = B.wire.testRunner(this.runner); 132 | 133 | wire.ready({ timeout: 1200 }); 134 | wire.run(); 135 | 136 | B.referee.assert.equals(this.runner.timeout, 1200); 137 | }, 138 | 139 | "should run parsable context when ready": function () { 140 | var wire = B.wire.testRunner(this.runner); 141 | var context = { name: "Parsed", tests: [{ name: "Yay" }], contexts: [] }; 142 | B.addTestContext({ parse: sinon.stub().returns(context) }); 143 | 144 | wire.ready({}); 145 | wire.run(); 146 | 147 | B.referee.assert.equals(this.runner.runSuite.args[0][0].length, 1); 148 | B.referee.assert.equals(this.runner.runSuite.args[0][0][0].name, "Parsed"); 149 | }, 150 | 151 | "should filter context before running": function () { 152 | var wire = B.wire.testRunner(this.runner); 153 | B.testCase("Thing", { doIt: function () {} }); 154 | 155 | wire.ready({ filters: ["Stuff"] }); 156 | wire.run(); 157 | 158 | B.referee.assert.equals(this.runner.runSuite.args[0][0].length, 0); 159 | }, 160 | 161 | "should capture console if configured to do so": function () { 162 | this.sandbox.stub(B, "captureConsole"); 163 | var wire = B.wire.testRunner(this.runner); 164 | 165 | wire.ready({ captureConsole: true }); 166 | wire.run(); 167 | 168 | B.referee.assert(B.captureConsole.calledOnce); 169 | }, 170 | 171 | "should not capture console by default": function () { 172 | this.sandbox.stub(B, "captureConsole"); 173 | var wire = B.wire.testRunner(this.runner); 174 | 175 | wire.ready({}); 176 | wire.run(); 177 | 178 | B.referee.assert(!B.captureConsole.called); 179 | }, 180 | 181 | "should wire document state": function () { 182 | this.sandbox.stub(B.wire, "documentState"); 183 | var wire = B.wire.testRunner(this.runner); 184 | 185 | wire.ready({}); 186 | wire.run(); 187 | 188 | B.referee.assert(B.wire.documentState.calledOnce); 189 | }, 190 | 191 | "should optionally not wire document state": function () { 192 | this.sandbox.stub(B.wire, "documentState"); 193 | var wire = B.wire.testRunner(this.runner); 194 | 195 | wire.ready({ resetDocument: false }); 196 | wire.run(); 197 | 198 | B.referee.assert(!B.wire.documentState.called); 199 | }, 200 | 201 | "should start automatically": function () { 202 | var wire = B.wire.testRunner(this.runner); 203 | 204 | wire.ready({}); 205 | 206 | B.referee.assert(this.runner.runSuite.calledOnce); 207 | }, 208 | 209 | "should not start automatically if autoRun: false": function () { 210 | var wire = B.wire.testRunner(this.runner); 211 | 212 | wire.ready({ autoRun: false }); 213 | 214 | B.referee.assert(!this.runner.runSuite.calledOnce); 215 | }, 216 | 217 | "should start on run() if autoRun: false": function () { 218 | var wire = B.wire.testRunner(this.runner); 219 | 220 | wire.ready({ autoRun: false }); 221 | wire.run(); 222 | 223 | B.referee.assert(this.runner.runSuite.calledOnce); 224 | }, 225 | 226 | "should not run if not ready": function () { 227 | var wire = B.wire.testRunner(this.runner); 228 | 229 | wire.run(); 230 | 231 | B.referee.assert(!this.runner.runSuite.calledOnce); 232 | }, 233 | 234 | "should run when ready": function () { 235 | var wire = B.wire.testRunner(this.runner); 236 | 237 | wire.run(); 238 | wire.ready(); 239 | 240 | B.referee.assert(this.runner.runSuite.calledOnce); 241 | }, 242 | 243 | "should receive config when running before ready": function () { 244 | this.sandbox.stub(B, "captureConsole"); 245 | var wire = B.wire.testRunner(this.runner); 246 | 247 | wire.run(); 248 | wire.ready({ captureConsole: true }); 249 | 250 | B.referee.assert(B.captureConsole.calledOnce); 251 | }, 252 | 253 | "should defer creating test runner": function () { 254 | var runSuite = sinon.spy(); 255 | var wire = B.wire.testRunner(function () { 256 | return { on: function () {}, runSuite: runSuite }; 257 | }); 258 | 259 | wire.run(); 260 | wire.ready({}); 261 | 262 | B.referee.assert(runSuite.calledOnce); 263 | } 264 | }); 265 | 266 | B.testCase("BrowserWireTest", { 267 | setUp: setUp, 268 | tearDown: tearDown, 269 | 270 | "should set up ready and run functions": function () { 271 | B.wire(this.runner); 272 | B.ready(); 273 | B.run(); 274 | 275 | B.referee.assert(this.runner.runSuite.calledOnce); 276 | } 277 | }); 278 | }(buster)); 279 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # This project has been abandoned 2 | 3 | If anyone wants to take over maintenance, please get in touch with one of the organisation owners 4 | 5 | --- 6 | 7 | # buster 8 | 9 | [![Build status](https://secure.travis-ci.org/busterjs/buster.png?branch=master)](http://travis-ci.org/busterjs/buster) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/buster?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/buster) 10 | 11 | Meta package, which wires together all Buster.JS submodules. 12 | 13 | * [Documentation](http://docs.busterjs.org/en/latest/) 14 | * [Changelog](http://docs.busterjs.org/en/latest/changelog/) 15 | 16 | > Want to support Buster.JS development? [Here](http://docs.busterjs.org/en/latest/developers/) you get useful information. 17 | 18 | 19 | ### Core modules (in alphabetical order) ### 20 | 21 | * [ansi-colorizer](https://github.com/busterjs/ansi-colorizer) [![Build status](https://secure.travis-ci.org/busterjs/ansi-colorizer.png?branch=master)](http://travis-ci.org/busterjs/ansi-colorizer) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/ansi-colorizer?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/ansi-colorizer) 22 | * [ansi-grid](https://github.com/busterjs/ansi-grid) [![Build status](https://secure.travis-ci.org/busterjs/ansi-grid.png?branch=master)](http://travis-ci.org/busterjs/ansi-grid) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/ansi-grid?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/ansi-grid) 23 | * [bane](https://github.com/busterjs/bane) [![Build status](https://secure.travis-ci.org/busterjs/bane.png?branch=master)](http://travis-ci.org/busterjs/bane) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/bane?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/bane) 24 | * [buster-analyzer](https://github.com/busterjs/buster-analyzer) [![Build status](https://secure.travis-ci.org/busterjs/buster-analyzer.png?branch=master)](http://travis-ci.org/busterjs/buster-analyzer) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/buster-analyzer?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/buster-analyzer) 25 | * [buster-autotest](https://github.com/busterjs/buster-autotest) [![Build status](https://secure.travis-ci.org/busterjs/buster-autotest.png?branch=master)](http://travis-ci.org/busterjs/buster-autotest) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/buster-autotest?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/buster-autotest) 26 | * [buster-ci](https://github.com/busterjs/buster-ci) [![Build status](https://secure.travis-ci.org/busterjs/buster-ci.png?branch=master)](http://travis-ci.org/busterjs/buster-ci) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/buster-ci?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/buster-ci) 27 | * [buster-ci-agent](https://github.com/busterjs/buster-ci-agent) [![Build status](https://secure.travis-ci.org/busterjs/buster-ci-agent.png?branch=master)](http://travis-ci.org/busterjs/buster-ci-agent) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/buster-ci-agent?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/buster-ci-agent) 28 | * [buster-cli](https://github.com/busterjs/buster-cli) [![Build status](https://secure.travis-ci.org/busterjs/buster-cli.png?branch=master)](http://travis-ci.org/busterjs/buster-cli) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/buster-cli?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/buster-cli) 29 | * [buster-configuration](https://github.com/busterjs/buster-configuration) [![Build status](https://secure.travis-ci.org/busterjs/buster-configuration.png?branch=master)](http://travis-ci.org/busterjs/buster-configuration) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/buster-configuration?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/buster-configuration) 30 | * [buster-server-cli](https://github.com/busterjs/buster-server-cli) [![Build status](https://secure.travis-ci.org/busterjs/buster-server-cli.png?branch=master)](http://travis-ci.org/busterjs/buster-server-cli) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/buster-server-cli?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/buster-server-cli) 31 | * [buster-node](https://github.com/busterjs/buster-node) [![Build status](https://secure.travis-ci.org/busterjs/buster-node.png?branch=master)](http://travis-ci.org/busterjs/buster-node) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/buster-node?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/buster-node) 32 | * [buster-sinon](https://github.com/busterjs/buster-sinon) [![Build status](https://secure.travis-ci.org/busterjs/buster-sinon.png?branch=master)](http://travis-ci.org/busterjs/buster-sinon) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/buster-sinon?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/buster-sinon) 33 | * [buster-static](https://github.com/busterjs/buster-static) [![Build status](https://secure.travis-ci.org/busterjs/buster-static.png?branch=master)](http://travis-ci.org/busterjs/buster-static) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/buster-static?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/buster-static) 34 | * [buster-test](https://github.com/busterjs/buster-test) [![Build status](https://secure.travis-ci.org/busterjs/buster-test.png?branch=master)](http://travis-ci.org/busterjs/buster-test) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/buster-test?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/buster-test) 35 | * [buster-test-cli](https://github.com/busterjs/buster-test-cli) [![Build status](https://secure.travis-ci.org/busterjs/buster-test-cli.png?branch=master)](http://travis-ci.org/busterjs/buster-test-cli) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/buster-test-cli?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/buster-test-cli) 36 | * [evented-logger](https://github.com/busterjs/evented-logger) [![Build status](https://secure.travis-ci.org/busterjs/evented-logger.png?branch=master)](http://travis-ci.org/busterjs/evented-logger) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/evented-logger?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/evented-logger) 37 | * [formatio](https://github.com/busterjs/formatio) [![Build status](https://secure.travis-ci.org/busterjs/formatio.png?branch=master)](http://travis-ci.org/busterjs/formatio) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/formatio?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/formatio) 38 | * [grunt-buster](https://github.com/busterjs/grunt-buster) [![Build status](https://secure.travis-ci.org/busterjs/grunt-buster.png?branch=master)](http://travis-ci.org/busterjs/grunt-buster) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/grunt-buster?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/grunt-buster) 39 | * [multi-glob](https://github.com/busterjs/multi-glob) [![Build status](https://secure.travis-ci.org/busterjs/multi-glob.png?branch=master)](http://travis-ci.org/busterjs/multi-glob) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/multi-glob?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/multi-glob) 40 | * [posix-argv-parser](https://github.com/busterjs/posix-argv-parser) [![Build status](https://secure.travis-ci.org/busterjs/posix-argv-parser.png?branch=master)](http://travis-ci.org/busterjs/posix-argv-parser) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/posix-argv-parser?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/posix-argv-parser) 41 | * [ramp](https://github.com/busterjs/ramp) [![Build status](https://secure.travis-ci.org/busterjs/ramp.png?branch=master)](http://travis-ci.org/busterjs/ramp) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/ramp?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/ramp) 42 | * [ramp-resources](https://github.com/busterjs/ramp-resources) [![Build status](https://secure.travis-ci.org/busterjs/ramp-resources.png?branch=master)](http://travis-ci.org/busterjs/ramp-resources) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/ramp-resources?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/ramp-resources) 43 | * [referee](https://github.com/busterjs/referee) [![Build status](https://secure.travis-ci.org/busterjs/referee.png?branch=master)](http://travis-ci.org/busterjs/referee) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/referee?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/referee) 44 | * [referee-sinon](https://github.com/busterjs/referee-sinon) [![Build status](https://secure.travis-ci.org/busterjs/referee-sinon.png?branch=master)](http://travis-ci.org/busterjs/referee-sinon) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/referee-sinon?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/referee-sinon) 45 | * [samsam](https://github.com/busterjs/samsam) [![Build status](https://secure.travis-ci.org/busterjs/samsam.png?branch=master)](http://travis-ci.org/busterjs/samsam) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/samsam?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/samsam) 46 | * [stack-filter](https://github.com/busterjs/stack-filter) [![Build status](https://secure.travis-ci.org/busterjs/stack-filter.png?branch=master)](http://travis-ci.org/busterjs/stack-filter) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/stack-filter?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/stack-filter) 47 | * [stream-logger](https://github.com/busterjs/stream-logger) [![Build status](https://secure.travis-ci.org/busterjs/stream-logger.png?branch=master)](http://travis-ci.org/busterjs/stream-logger) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/stream-logger?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/stream-logger) 48 | 49 | ### Extension modules (in alphabetical order) ### 50 | 51 | * [buster-amd](https://github.com/busterjs/buster-amd) [![Build status](https://secure.travis-ci.org/busterjs/buster-amd.png?branch=master)](http://travis-ci.org/busterjs/buster-amd) 52 | * [buster-coffee](https://github.com/busterjs/buster-coffee) [![Build status](https://secure.travis-ci.org/busterjs/buster-coffee.png?branch=master)](http://travis-ci.org/busterjs/buster-coffee) 53 | * [buster-html-doc](https://github.com/busterjs/buster-html-doc) [![Build status](https://secure.travis-ci.org/busterjs/buster-html-doc.png?branch=master)](http://travis-ci.org/busterjs/buster-html-doc) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/buster-html-doc?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/buster-html-doc) 54 | * [buster-jstestdriver](https://github.com/busterjs/buster-jstestdriver) [![Build status](https://secure.travis-ci.org/busterjs/buster-jstestdriver.png?branch=master)](http://travis-ci.org/busterjs/buster-jstestdriver) 55 | * [buster-lint](https://github.com/busterjs/buster-lint) [![Build status](https://secure.travis-ci.org/busterjs/buster-lint.png?branch=master)](http://travis-ci.org/busterjs/buster-lint) 56 | * [buster-syntax](https://github.com/busterjs/buster-syntax) [![Build status](https://secure.travis-ci.org/busterjs/buster-syntax.png?branch=master)](http://travis-ci.org/busterjs/buster-syntax) [![Build status](https://ci.appveyor.com/api/projects/status/github/busterjs/buster-syntax?branch=master&svg=true)](https://ci.appveyor.com/project/dominykas/buster-syntax) 57 | -------------------------------------------------------------------------------- /resources/buster-test.css: -------------------------------------------------------------------------------- 1 | .buster-test, 2 | html.buster-test { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | .buster-test, 8 | body.buster-test { 9 | font-family: Georgia, Times, serif; 10 | font-size: 16px; 11 | padding: 0 12px; 12 | } 13 | 14 | .buster-test h2 { 15 | display: inline-block; 16 | font-weight: normal; 17 | margin: 0 0 0.25em; 18 | } 19 | 20 | .buster-test ul { 21 | margin: 0 0 1em 2em; 22 | padding: 0; 23 | } 24 | 25 | .buster-test ul + ul { 26 | margin-top: -1em; 27 | } 28 | 29 | .buster-test ul li { 30 | list-style: none; 31 | } 32 | 33 | .buster-test li h3 { 34 | display: inline-block; 35 | font-size: 1.2em; 36 | margin: 0 0 .25em; 37 | padding: 0; 38 | } 39 | 40 | .buster-test li ul, 41 | .buster-test li p { 42 | margin: 0 0 0.5em 2em; 43 | } 44 | 45 | .buster-test .success h3:before { 46 | color: #3c3; 47 | content: "✓"; 48 | padding-right: 6px; 49 | } 50 | 51 | .buster-test .timeout h3, 52 | .buster-test .failure h3, 53 | .buster-test .error-message, 54 | .buster-test .stats.failure { 55 | color: #c33; 56 | } 57 | 58 | .buster-test .stats.success { 59 | color: #3c3; 60 | } 61 | 62 | .buster-test .error h3, 63 | .buster-test .error .error-message { 64 | color: #cc3; 65 | } 66 | 67 | .buster-test .failure h3:before, 68 | .buster-test .error h3:before { 69 | content: "✖"; 70 | padding-right: 6px; 71 | } 72 | 73 | .buster-test .deferred h3 { 74 | color: #33c; 75 | } 76 | 77 | .buster-test .deferred h3:before { 78 | content: "✎"; 79 | padding-right: 6px; 80 | } 81 | 82 | .buster-test .stats ul { 83 | margin: 0 0 1em; 84 | } 85 | 86 | .buster-test .stats h2 { 87 | margin: 0 0 .25em; 88 | } 89 | 90 | .buster-test .stats li { 91 | display: inline; 92 | padding-right: 1em; 93 | } 94 | 95 | .buster-test p, 96 | .buster-test li { 97 | line-height: 1.5; 98 | } 99 | 100 | .buster-test h1, 101 | .buster-test h2, 102 | .buster-test h3, 103 | .buster-test h4, 104 | .buster-test h5, 105 | .buster-test h6, 106 | .buster-test p { 107 | font-weight: normal; 108 | margin: 0 0 1em; 109 | } 110 | 111 | .buster-test ul { 112 | margin: 0 0 1em 15px; 113 | } 114 | 115 | /* Masthead */ 116 | .buster-test h1 { 117 | background: #333; 118 | 119 | background: -webkit-gradient( 120 | linear, 121 | left top, 122 | left bottom, 123 | from(#5f5f5f), 124 | to(#222) 125 | ); 126 | 127 | background: -moz-linear-gradient( 128 | top center, 129 | #5f5f5f, 130 | #222 131 | ); 132 | 133 | /* For Internet Explorer 5.5 - 7 */ 134 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#ff5f5f5f, endColorstr=#ff222222); 135 | /* For Internet Explorer 8 */ 136 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ff5f5f5f, endColorstr=#ff222222)"; 137 | zoom: 1; 138 | 139 | border-bottom: 4px solid #c0c6cc; 140 | box-shadow: 0 0 10px #ddd; 141 | -moz-box-shadow: 0 0 10px #ddd; 142 | -webkit-box-shadow: 0 0 10px #ddd; 143 | color: #fff; 144 | font-size: 2em; 145 | margin: 0 -12px 1em; 146 | overflow: auto; 147 | padding: 6px 12px; 148 | } 149 | 150 | .buster-test h1 a { 151 | color: #fff; 152 | padding-right: 1em; 153 | text-decoration: none; 154 | } 155 | 156 | .buster-test h1 .buster-logo { 157 | background-repeat: no-repeat; 158 | background-image: url(data:image/png;base64,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); 159 | float: left; 160 | height: 71px; 161 | margin-right: 20px; 162 | width: 200px; 163 | } 164 | 165 | .buster-test h1 .title { 166 | border-left: 1px solid #aaa; 167 | float: left; 168 | padding: 30px 0 0 20px; 169 | } 170 | 171 | .buster-test .messages li:before { 172 | background: #f0f0f0; 173 | border-radius: 6px; 174 | -moz-border-radius: 6px; 175 | -webkit-border-radius: 6px; 176 | border: 1px solid #ccc; 177 | display: inline-block; 178 | font-family: monospace; 179 | font-size: 10px; 180 | font-variant: small-caps; 181 | margin-right: 1em; 182 | padding: 0 4px; 183 | } 184 | 185 | .buster-test .log:before { 186 | content: "LOG"; 187 | } 188 | 189 | .buster-test .info:before { 190 | content: "INFO"; 191 | } 192 | 193 | .buster-test .messages .warn:before { 194 | content: "WARN"; 195 | border-color: #cc3; 196 | } 197 | 198 | .buster-test .messages .error:before { 199 | content: "ERR"; 200 | border-color: #c00; 201 | } 202 | -------------------------------------------------------------------------------- /test/integration/expect/lib/require.js: -------------------------------------------------------------------------------- 1 | /** vim: et:ts=4:sw=4:sts=4 2 | * @license RequireJS 2.1.15 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/jrburke/requirejs for details 5 | */ 6 | //Not using strict: uneven strict support in browsers, #392, and causes 7 | //problems with requirejs.exec()/transpiler plugins that may not be strict. 8 | /*jslint regexp: true, nomen: true, sloppy: true */ 9 | /*global window, navigator, document, importScripts, setTimeout, opera */ 10 | 11 | var requirejs, require, define; 12 | (function (global) { 13 | var req, s, head, baseElement, dataMain, src, 14 | interactiveScript, currentlyAddingScript, mainScript, subPath, 15 | version = '2.1.15', 16 | commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, 17 | cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, 18 | jsSuffixRegExp = /\.js$/, 19 | currDirRegExp = /^\.\//, 20 | op = Object.prototype, 21 | ostring = op.toString, 22 | hasOwn = op.hasOwnProperty, 23 | ap = Array.prototype, 24 | apsp = ap.splice, 25 | isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document), 26 | isWebWorker = !isBrowser && typeof importScripts !== 'undefined', 27 | //PS3 indicates loaded and complete, but need to wait for complete 28 | //specifically. Sequence is 'loading', 'loaded', execution, 29 | // then 'complete'. The UA check is unfortunate, but not sure how 30 | //to feature test w/o causing perf issues. 31 | readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ? 32 | /^complete$/ : /^(complete|loaded)$/, 33 | defContextName = '_', 34 | //Oh the tragedy, detecting opera. See the usage of isOpera for reason. 35 | isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]', 36 | contexts = {}, 37 | cfg = {}, 38 | globalDefQueue = [], 39 | useInteractive = false; 40 | 41 | function isFunction(it) { 42 | return ostring.call(it) === '[object Function]'; 43 | } 44 | 45 | function isArray(it) { 46 | return ostring.call(it) === '[object Array]'; 47 | } 48 | 49 | /** 50 | * Helper function for iterating over an array. If the func returns 51 | * a true value, it will break out of the loop. 52 | */ 53 | function each(ary, func) { 54 | if (ary) { 55 | var i; 56 | for (i = 0; i < ary.length; i += 1) { 57 | if (ary[i] && func(ary[i], i, ary)) { 58 | break; 59 | } 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * Helper function for iterating over an array backwards. If the func 66 | * returns a true value, it will break out of the loop. 67 | */ 68 | function eachReverse(ary, func) { 69 | if (ary) { 70 | var i; 71 | for (i = ary.length - 1; i > -1; i -= 1) { 72 | if (ary[i] && func(ary[i], i, ary)) { 73 | break; 74 | } 75 | } 76 | } 77 | } 78 | 79 | function hasProp(obj, prop) { 80 | return hasOwn.call(obj, prop); 81 | } 82 | 83 | function getOwn(obj, prop) { 84 | return hasProp(obj, prop) && obj[prop]; 85 | } 86 | 87 | /** 88 | * Cycles over properties in an object and calls a function for each 89 | * property value. If the function returns a truthy value, then the 90 | * iteration is stopped. 91 | */ 92 | function eachProp(obj, func) { 93 | var prop; 94 | for (prop in obj) { 95 | if (hasProp(obj, prop)) { 96 | if (func(obj[prop], prop)) { 97 | break; 98 | } 99 | } 100 | } 101 | } 102 | 103 | /** 104 | * Simple function to mix in properties from source into target, 105 | * but only if target does not already have a property of the same name. 106 | */ 107 | function mixin(target, source, force, deepStringMixin) { 108 | if (source) { 109 | eachProp(source, function (value, prop) { 110 | if (force || !hasProp(target, prop)) { 111 | if (deepStringMixin && typeof value === 'object' && value && 112 | !isArray(value) && !isFunction(value) && 113 | !(value instanceof RegExp)) { 114 | 115 | if (!target[prop]) { 116 | target[prop] = {}; 117 | } 118 | mixin(target[prop], value, force, deepStringMixin); 119 | } else { 120 | target[prop] = value; 121 | } 122 | } 123 | }); 124 | } 125 | return target; 126 | } 127 | 128 | //Similar to Function.prototype.bind, but the 'this' object is specified 129 | //first, since it is easier to read/figure out what 'this' will be. 130 | function bind(obj, fn) { 131 | return function () { 132 | return fn.apply(obj, arguments); 133 | }; 134 | } 135 | 136 | function scripts() { 137 | return document.getElementsByTagName('script'); 138 | } 139 | 140 | function defaultOnError(err) { 141 | throw err; 142 | } 143 | 144 | //Allow getting a global that is expressed in 145 | //dot notation, like 'a.b.c'. 146 | function getGlobal(value) { 147 | if (!value) { 148 | return value; 149 | } 150 | var g = global; 151 | each(value.split('.'), function (part) { 152 | g = g[part]; 153 | }); 154 | return g; 155 | } 156 | 157 | /** 158 | * Constructs an error with a pointer to an URL with more information. 159 | * @param {String} id the error ID that maps to an ID on a web page. 160 | * @param {String} message human readable error. 161 | * @param {Error} [err] the original error, if there is one. 162 | * 163 | * @returns {Error} 164 | */ 165 | function makeError(id, msg, err, requireModules) { 166 | var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); 167 | e.requireType = id; 168 | e.requireModules = requireModules; 169 | if (err) { 170 | e.originalError = err; 171 | } 172 | return e; 173 | } 174 | 175 | if (typeof define !== 'undefined') { 176 | //If a define is already in play via another AMD loader, 177 | //do not overwrite. 178 | return; 179 | } 180 | 181 | if (typeof requirejs !== 'undefined') { 182 | if (isFunction(requirejs)) { 183 | //Do not overwrite an existing requirejs instance. 184 | return; 185 | } 186 | cfg = requirejs; 187 | requirejs = undefined; 188 | } 189 | 190 | //Allow for a require config object 191 | if (typeof require !== 'undefined' && !isFunction(require)) { 192 | //assume it is a config object. 193 | cfg = require; 194 | require = undefined; 195 | } 196 | 197 | function newContext(contextName) { 198 | var inCheckLoaded, Module, context, handlers, 199 | checkLoadedTimeoutId, 200 | config = { 201 | //Defaults. Do not set a default for map 202 | //config to speed up normalize(), which 203 | //will run faster if there is no default. 204 | waitSeconds: 7, 205 | baseUrl: './', 206 | paths: {}, 207 | bundles: {}, 208 | pkgs: {}, 209 | shim: {}, 210 | config: {} 211 | }, 212 | registry = {}, 213 | //registry of just enabled modules, to speed 214 | //cycle breaking code when lots of modules 215 | //are registered, but not activated. 216 | enabledRegistry = {}, 217 | undefEvents = {}, 218 | defQueue = [], 219 | defined = {}, 220 | urlFetched = {}, 221 | bundlesMap = {}, 222 | requireCounter = 1, 223 | unnormalizedCounter = 1; 224 | 225 | /** 226 | * Trims the . and .. from an array of path segments. 227 | * It will keep a leading path segment if a .. will become 228 | * the first path segment, to help with module name lookups, 229 | * which act like paths, but can be remapped. But the end result, 230 | * all paths that use this function should look normalized. 231 | * NOTE: this method MODIFIES the input array. 232 | * @param {Array} ary the array of path segments. 233 | */ 234 | function trimDots(ary) { 235 | var i, part; 236 | for (i = 0; i < ary.length; i++) { 237 | part = ary[i]; 238 | if (part === '.') { 239 | ary.splice(i, 1); 240 | i -= 1; 241 | } else if (part === '..') { 242 | // If at the start, or previous value is still .., 243 | // keep them so that when converted to a path it may 244 | // still work when converted to a path, even though 245 | // as an ID it is less than ideal. In larger point 246 | // releases, may be better to just kick out an error. 247 | if (i === 0 || (i == 1 && ary[2] === '..') || ary[i - 1] === '..') { 248 | continue; 249 | } else if (i > 0) { 250 | ary.splice(i - 1, 2); 251 | i -= 2; 252 | } 253 | } 254 | } 255 | } 256 | 257 | /** 258 | * Given a relative module name, like ./something, normalize it to 259 | * a real name that can be mapped to a path. 260 | * @param {String} name the relative name 261 | * @param {String} baseName a real name that the name arg is relative 262 | * to. 263 | * @param {Boolean} applyMap apply the map config to the value. Should 264 | * only be done if this normalization is for a dependency ID. 265 | * @returns {String} normalized name 266 | */ 267 | function normalize(name, baseName, applyMap) { 268 | var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex, 269 | foundMap, foundI, foundStarMap, starI, normalizedBaseParts, 270 | baseParts = (baseName && baseName.split('/')), 271 | map = config.map, 272 | starMap = map && map['*']; 273 | 274 | //Adjust any relative paths. 275 | if (name) { 276 | name = name.split('/'); 277 | lastIndex = name.length - 1; 278 | 279 | // If wanting node ID compatibility, strip .js from end 280 | // of IDs. Have to do this here, and not in nameToUrl 281 | // because node allows either .js or non .js to map 282 | // to same file. 283 | if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { 284 | name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); 285 | } 286 | 287 | // Starts with a '.' so need the baseName 288 | if (name[0].charAt(0) === '.' && baseParts) { 289 | //Convert baseName to array, and lop off the last part, 290 | //so that . matches that 'directory' and not name of the baseName's 291 | //module. For instance, baseName of 'one/two/three', maps to 292 | //'one/two/three.js', but we want the directory, 'one/two' for 293 | //this normalization. 294 | normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); 295 | name = normalizedBaseParts.concat(name); 296 | } 297 | 298 | trimDots(name); 299 | name = name.join('/'); 300 | } 301 | 302 | //Apply map config if available. 303 | if (applyMap && map && (baseParts || starMap)) { 304 | nameParts = name.split('/'); 305 | 306 | outerLoop: for (i = nameParts.length; i > 0; i -= 1) { 307 | nameSegment = nameParts.slice(0, i).join('/'); 308 | 309 | if (baseParts) { 310 | //Find the longest baseName segment match in the config. 311 | //So, do joins on the biggest to smallest lengths of baseParts. 312 | for (j = baseParts.length; j > 0; j -= 1) { 313 | mapValue = getOwn(map, baseParts.slice(0, j).join('/')); 314 | 315 | //baseName segment has config, find if it has one for 316 | //this name. 317 | if (mapValue) { 318 | mapValue = getOwn(mapValue, nameSegment); 319 | if (mapValue) { 320 | //Match, update name to the new value. 321 | foundMap = mapValue; 322 | foundI = i; 323 | break outerLoop; 324 | } 325 | } 326 | } 327 | } 328 | 329 | //Check for a star map match, but just hold on to it, 330 | //if there is a shorter segment match later in a matching 331 | //config, then favor over this star map. 332 | if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) { 333 | foundStarMap = getOwn(starMap, nameSegment); 334 | starI = i; 335 | } 336 | } 337 | 338 | if (!foundMap && foundStarMap) { 339 | foundMap = foundStarMap; 340 | foundI = starI; 341 | } 342 | 343 | if (foundMap) { 344 | nameParts.splice(0, foundI, foundMap); 345 | name = nameParts.join('/'); 346 | } 347 | } 348 | 349 | // If the name points to a package's name, use 350 | // the package main instead. 351 | pkgMain = getOwn(config.pkgs, name); 352 | 353 | return pkgMain ? pkgMain : name; 354 | } 355 | 356 | function removeScript(name) { 357 | if (isBrowser) { 358 | each(scripts(), function (scriptNode) { 359 | if (scriptNode.getAttribute('data-requiremodule') === name && 360 | scriptNode.getAttribute('data-requirecontext') === context.contextName) { 361 | scriptNode.parentNode.removeChild(scriptNode); 362 | return true; 363 | } 364 | }); 365 | } 366 | } 367 | 368 | function hasPathFallback(id) { 369 | var pathConfig = getOwn(config.paths, id); 370 | if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { 371 | //Pop off the first array value, since it failed, and 372 | //retry 373 | pathConfig.shift(); 374 | context.require.undef(id); 375 | 376 | //Custom require that does not do map translation, since 377 | //ID is "absolute", already mapped/resolved. 378 | context.makeRequire(null, { 379 | skipMap: true 380 | })([id]); 381 | 382 | return true; 383 | } 384 | } 385 | 386 | //Turns a plugin!resource to [plugin, resource] 387 | //with the plugin being undefined if the name 388 | //did not have a plugin prefix. 389 | function splitPrefix(name) { 390 | var prefix, 391 | index = name ? name.indexOf('!') : -1; 392 | if (index > -1) { 393 | prefix = name.substring(0, index); 394 | name = name.substring(index + 1, name.length); 395 | } 396 | return [prefix, name]; 397 | } 398 | 399 | /** 400 | * Creates a module mapping that includes plugin prefix, module 401 | * name, and path. If parentModuleMap is provided it will 402 | * also normalize the name via require.normalize() 403 | * 404 | * @param {String} name the module name 405 | * @param {String} [parentModuleMap] parent module map 406 | * for the module name, used to resolve relative names. 407 | * @param {Boolean} isNormalized: is the ID already normalized. 408 | * This is true if this call is done for a define() module ID. 409 | * @param {Boolean} applyMap: apply the map config to the ID. 410 | * Should only be true if this map is for a dependency. 411 | * 412 | * @returns {Object} 413 | */ 414 | function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { 415 | var url, pluginModule, suffix, nameParts, 416 | prefix = null, 417 | parentName = parentModuleMap ? parentModuleMap.name : null, 418 | originalName = name, 419 | isDefine = true, 420 | normalizedName = ''; 421 | 422 | //If no name, then it means it is a require call, generate an 423 | //internal name. 424 | if (!name) { 425 | isDefine = false; 426 | name = '_@r' + (requireCounter += 1); 427 | } 428 | 429 | nameParts = splitPrefix(name); 430 | prefix = nameParts[0]; 431 | name = nameParts[1]; 432 | 433 | if (prefix) { 434 | prefix = normalize(prefix, parentName, applyMap); 435 | pluginModule = getOwn(defined, prefix); 436 | } 437 | 438 | //Account for relative paths if there is a base name. 439 | if (name) { 440 | if (prefix) { 441 | if (pluginModule && pluginModule.normalize) { 442 | //Plugin is loaded, use its normalize method. 443 | normalizedName = pluginModule.normalize(name, function (name) { 444 | return normalize(name, parentName, applyMap); 445 | }); 446 | } else { 447 | // If nested plugin references, then do not try to 448 | // normalize, as it will not normalize correctly. This 449 | // places a restriction on resourceIds, and the longer 450 | // term solution is not to normalize until plugins are 451 | // loaded and all normalizations to allow for async 452 | // loading of a loader plugin. But for now, fixes the 453 | // common uses. Details in #1131 454 | normalizedName = name.indexOf('!') === -1 ? 455 | normalize(name, parentName, applyMap) : 456 | name; 457 | } 458 | } else { 459 | //A regular module. 460 | normalizedName = normalize(name, parentName, applyMap); 461 | 462 | //Normalized name may be a plugin ID due to map config 463 | //application in normalize. The map config values must 464 | //already be normalized, so do not need to redo that part. 465 | nameParts = splitPrefix(normalizedName); 466 | prefix = nameParts[0]; 467 | normalizedName = nameParts[1]; 468 | isNormalized = true; 469 | 470 | url = context.nameToUrl(normalizedName); 471 | } 472 | } 473 | 474 | //If the id is a plugin id that cannot be determined if it needs 475 | //normalization, stamp it with a unique ID so two matching relative 476 | //ids that may conflict can be separate. 477 | suffix = prefix && !pluginModule && !isNormalized ? 478 | '_unnormalized' + (unnormalizedCounter += 1) : 479 | ''; 480 | 481 | return { 482 | prefix: prefix, 483 | name: normalizedName, 484 | parentMap: parentModuleMap, 485 | unnormalized: !!suffix, 486 | url: url, 487 | originalName: originalName, 488 | isDefine: isDefine, 489 | id: (prefix ? 490 | prefix + '!' + normalizedName : 491 | normalizedName) + suffix 492 | }; 493 | } 494 | 495 | function getModule(depMap) { 496 | var id = depMap.id, 497 | mod = getOwn(registry, id); 498 | 499 | if (!mod) { 500 | mod = registry[id] = new context.Module(depMap); 501 | } 502 | 503 | return mod; 504 | } 505 | 506 | function on(depMap, name, fn) { 507 | var id = depMap.id, 508 | mod = getOwn(registry, id); 509 | 510 | if (hasProp(defined, id) && 511 | (!mod || mod.defineEmitComplete)) { 512 | if (name === 'defined') { 513 | fn(defined[id]); 514 | } 515 | } else { 516 | mod = getModule(depMap); 517 | if (mod.error && name === 'error') { 518 | fn(mod.error); 519 | } else { 520 | mod.on(name, fn); 521 | } 522 | } 523 | } 524 | 525 | function onError(err, errback) { 526 | var ids = err.requireModules, 527 | notified = false; 528 | 529 | if (errback) { 530 | errback(err); 531 | } else { 532 | each(ids, function (id) { 533 | var mod = getOwn(registry, id); 534 | if (mod) { 535 | //Set error on module, so it skips timeout checks. 536 | mod.error = err; 537 | if (mod.events.error) { 538 | notified = true; 539 | mod.emit('error', err); 540 | } 541 | } 542 | }); 543 | 544 | if (!notified) { 545 | req.onError(err); 546 | } 547 | } 548 | } 549 | 550 | /** 551 | * Internal method to transfer globalQueue items to this context's 552 | * defQueue. 553 | */ 554 | function takeGlobalQueue() { 555 | //Push all the globalDefQueue items into the context's defQueue 556 | if (globalDefQueue.length) { 557 | //Array splice in the values since the context code has a 558 | //local var ref to defQueue, so cannot just reassign the one 559 | //on context. 560 | apsp.apply(defQueue, 561 | [defQueue.length, 0].concat(globalDefQueue)); 562 | globalDefQueue = []; 563 | } 564 | } 565 | 566 | handlers = { 567 | 'require': function (mod) { 568 | if (mod.require) { 569 | return mod.require; 570 | } else { 571 | return (mod.require = context.makeRequire(mod.map)); 572 | } 573 | }, 574 | 'exports': function (mod) { 575 | mod.usingExports = true; 576 | if (mod.map.isDefine) { 577 | if (mod.exports) { 578 | return (defined[mod.map.id] = mod.exports); 579 | } else { 580 | return (mod.exports = defined[mod.map.id] = {}); 581 | } 582 | } 583 | }, 584 | 'module': function (mod) { 585 | if (mod.module) { 586 | return mod.module; 587 | } else { 588 | return (mod.module = { 589 | id: mod.map.id, 590 | uri: mod.map.url, 591 | config: function () { 592 | return getOwn(config.config, mod.map.id) || {}; 593 | }, 594 | exports: mod.exports || (mod.exports = {}) 595 | }); 596 | } 597 | } 598 | }; 599 | 600 | function cleanRegistry(id) { 601 | //Clean up machinery used for waiting modules. 602 | delete registry[id]; 603 | delete enabledRegistry[id]; 604 | } 605 | 606 | function breakCycle(mod, traced, processed) { 607 | var id = mod.map.id; 608 | 609 | if (mod.error) { 610 | mod.emit('error', mod.error); 611 | } else { 612 | traced[id] = true; 613 | each(mod.depMaps, function (depMap, i) { 614 | var depId = depMap.id, 615 | dep = getOwn(registry, depId); 616 | 617 | //Only force things that have not completed 618 | //being defined, so still in the registry, 619 | //and only if it has not been matched up 620 | //in the module already. 621 | if (dep && !mod.depMatched[i] && !processed[depId]) { 622 | if (getOwn(traced, depId)) { 623 | mod.defineDep(i, defined[depId]); 624 | mod.check(); //pass false? 625 | } else { 626 | breakCycle(dep, traced, processed); 627 | } 628 | } 629 | }); 630 | processed[id] = true; 631 | } 632 | } 633 | 634 | function checkLoaded() { 635 | var err, usingPathFallback, 636 | waitInterval = config.waitSeconds * 1000, 637 | //It is possible to disable the wait interval by using waitSeconds of 0. 638 | expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), 639 | noLoads = [], 640 | reqCalls = [], 641 | stillLoading = false, 642 | needCycleCheck = true; 643 | 644 | //Do not bother if this call was a result of a cycle break. 645 | if (inCheckLoaded) { 646 | return; 647 | } 648 | 649 | inCheckLoaded = true; 650 | 651 | //Figure out the state of all the modules. 652 | eachProp(enabledRegistry, function (mod) { 653 | var map = mod.map, 654 | modId = map.id; 655 | 656 | //Skip things that are not enabled or in error state. 657 | if (!mod.enabled) { 658 | return; 659 | } 660 | 661 | if (!map.isDefine) { 662 | reqCalls.push(mod); 663 | } 664 | 665 | if (!mod.error) { 666 | //If the module should be executed, and it has not 667 | //been inited and time is up, remember it. 668 | if (!mod.inited && expired) { 669 | if (hasPathFallback(modId)) { 670 | usingPathFallback = true; 671 | stillLoading = true; 672 | } else { 673 | noLoads.push(modId); 674 | removeScript(modId); 675 | } 676 | } else if (!mod.inited && mod.fetched && map.isDefine) { 677 | stillLoading = true; 678 | if (!map.prefix) { 679 | //No reason to keep looking for unfinished 680 | //loading. If the only stillLoading is a 681 | //plugin resource though, keep going, 682 | //because it may be that a plugin resource 683 | //is waiting on a non-plugin cycle. 684 | return (needCycleCheck = false); 685 | } 686 | } 687 | } 688 | }); 689 | 690 | if (expired && noLoads.length) { 691 | //If wait time expired, throw error of unloaded modules. 692 | err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads); 693 | err.contextName = context.contextName; 694 | return onError(err); 695 | } 696 | 697 | //Not expired, check for a cycle. 698 | if (needCycleCheck) { 699 | each(reqCalls, function (mod) { 700 | breakCycle(mod, {}, {}); 701 | }); 702 | } 703 | 704 | //If still waiting on loads, and the waiting load is something 705 | //other than a plugin resource, or there are still outstanding 706 | //scripts, then just try back later. 707 | if ((!expired || usingPathFallback) && stillLoading) { 708 | //Something is still waiting to load. Wait for it, but only 709 | //if a timeout is not already in effect. 710 | if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { 711 | checkLoadedTimeoutId = setTimeout(function () { 712 | checkLoadedTimeoutId = 0; 713 | checkLoaded(); 714 | }, 50); 715 | } 716 | } 717 | 718 | inCheckLoaded = false; 719 | } 720 | 721 | Module = function (map) { 722 | this.events = getOwn(undefEvents, map.id) || {}; 723 | this.map = map; 724 | this.shim = getOwn(config.shim, map.id); 725 | this.depExports = []; 726 | this.depMaps = []; 727 | this.depMatched = []; 728 | this.pluginMaps = {}; 729 | this.depCount = 0; 730 | 731 | /* this.exports this.factory 732 | this.depMaps = [], 733 | this.enabled, this.fetched 734 | */ 735 | }; 736 | 737 | Module.prototype = { 738 | init: function (depMaps, factory, errback, options) { 739 | options = options || {}; 740 | 741 | //Do not do more inits if already done. Can happen if there 742 | //are multiple define calls for the same module. That is not 743 | //a normal, common case, but it is also not unexpected. 744 | if (this.inited) { 745 | return; 746 | } 747 | 748 | this.factory = factory; 749 | 750 | if (errback) { 751 | //Register for errors on this module. 752 | this.on('error', errback); 753 | } else if (this.events.error) { 754 | //If no errback already, but there are error listeners 755 | //on this module, set up an errback to pass to the deps. 756 | errback = bind(this, function (err) { 757 | this.emit('error', err); 758 | }); 759 | } 760 | 761 | //Do a copy of the dependency array, so that 762 | //source inputs are not modified. For example 763 | //"shim" deps are passed in here directly, and 764 | //doing a direct modification of the depMaps array 765 | //would affect that config. 766 | this.depMaps = depMaps && depMaps.slice(0); 767 | 768 | this.errback = errback; 769 | 770 | //Indicate this module has be initialized 771 | this.inited = true; 772 | 773 | this.ignore = options.ignore; 774 | 775 | //Could have option to init this module in enabled mode, 776 | //or could have been previously marked as enabled. However, 777 | //the dependencies are not known until init is called. So 778 | //if enabled previously, now trigger dependencies as enabled. 779 | if (options.enabled || this.enabled) { 780 | //Enable this module and dependencies. 781 | //Will call this.check() 782 | this.enable(); 783 | } else { 784 | this.check(); 785 | } 786 | }, 787 | 788 | defineDep: function (i, depExports) { 789 | //Because of cycles, defined callback for a given 790 | //export can be called more than once. 791 | if (!this.depMatched[i]) { 792 | this.depMatched[i] = true; 793 | this.depCount -= 1; 794 | this.depExports[i] = depExports; 795 | } 796 | }, 797 | 798 | fetch: function () { 799 | if (this.fetched) { 800 | return; 801 | } 802 | this.fetched = true; 803 | 804 | context.startTime = (new Date()).getTime(); 805 | 806 | var map = this.map; 807 | 808 | //If the manager is for a plugin managed resource, 809 | //ask the plugin to load it now. 810 | if (this.shim) { 811 | context.makeRequire(this.map, { 812 | enableBuildCallback: true 813 | })(this.shim.deps || [], bind(this, function () { 814 | return map.prefix ? this.callPlugin() : this.load(); 815 | })); 816 | } else { 817 | //Regular dependency. 818 | return map.prefix ? this.callPlugin() : this.load(); 819 | } 820 | }, 821 | 822 | load: function () { 823 | var url = this.map.url; 824 | 825 | //Regular dependency. 826 | if (!urlFetched[url]) { 827 | urlFetched[url] = true; 828 | context.load(this.map.id, url); 829 | } 830 | }, 831 | 832 | /** 833 | * Checks if the module is ready to define itself, and if so, 834 | * define it. 835 | */ 836 | check: function () { 837 | if (!this.enabled || this.enabling) { 838 | return; 839 | } 840 | 841 | var err, cjsModule, 842 | id = this.map.id, 843 | depExports = this.depExports, 844 | exports = this.exports, 845 | factory = this.factory; 846 | 847 | if (!this.inited) { 848 | this.fetch(); 849 | } else if (this.error) { 850 | this.emit('error', this.error); 851 | } else if (!this.defining) { 852 | //The factory could trigger another require call 853 | //that would result in checking this module to 854 | //define itself again. If already in the process 855 | //of doing that, skip this work. 856 | this.defining = true; 857 | 858 | if (this.depCount < 1 && !this.defined) { 859 | if (isFunction(factory)) { 860 | //If there is an error listener, favor passing 861 | //to that instead of throwing an error. However, 862 | //only do it for define()'d modules. require 863 | //errbacks should not be called for failures in 864 | //their callbacks (#699). However if a global 865 | //onError is set, use that. 866 | if ((this.events.error && this.map.isDefine) || 867 | req.onError !== defaultOnError) { 868 | try { 869 | exports = context.execCb(id, factory, depExports, exports); 870 | } catch (e) { 871 | err = e; 872 | } 873 | } else { 874 | exports = context.execCb(id, factory, depExports, exports); 875 | } 876 | 877 | // Favor return value over exports. If node/cjs in play, 878 | // then will not have a return value anyway. Favor 879 | // module.exports assignment over exports object. 880 | if (this.map.isDefine && exports === undefined) { 881 | cjsModule = this.module; 882 | if (cjsModule) { 883 | exports = cjsModule.exports; 884 | } else if (this.usingExports) { 885 | //exports already set the defined value. 886 | exports = this.exports; 887 | } 888 | } 889 | 890 | if (err) { 891 | err.requireMap = this.map; 892 | err.requireModules = this.map.isDefine ? [this.map.id] : null; 893 | err.requireType = this.map.isDefine ? 'define' : 'require'; 894 | return onError((this.error = err)); 895 | } 896 | 897 | } else { 898 | //Just a literal value 899 | exports = factory; 900 | } 901 | 902 | this.exports = exports; 903 | 904 | if (this.map.isDefine && !this.ignore) { 905 | defined[id] = exports; 906 | 907 | if (req.onResourceLoad) { 908 | req.onResourceLoad(context, this.map, this.depMaps); 909 | } 910 | } 911 | 912 | //Clean up 913 | cleanRegistry(id); 914 | 915 | this.defined = true; 916 | } 917 | 918 | //Finished the define stage. Allow calling check again 919 | //to allow define notifications below in the case of a 920 | //cycle. 921 | this.defining = false; 922 | 923 | if (this.defined && !this.defineEmitted) { 924 | this.defineEmitted = true; 925 | this.emit('defined', this.exports); 926 | this.defineEmitComplete = true; 927 | } 928 | 929 | } 930 | }, 931 | 932 | callPlugin: function () { 933 | var map = this.map, 934 | id = map.id, 935 | //Map already normalized the prefix. 936 | pluginMap = makeModuleMap(map.prefix); 937 | 938 | //Mark this as a dependency for this plugin, so it 939 | //can be traced for cycles. 940 | this.depMaps.push(pluginMap); 941 | 942 | on(pluginMap, 'defined', bind(this, function (plugin) { 943 | var load, normalizedMap, normalizedMod, 944 | bundleId = getOwn(bundlesMap, this.map.id), 945 | name = this.map.name, 946 | parentName = this.map.parentMap ? this.map.parentMap.name : null, 947 | localRequire = context.makeRequire(map.parentMap, { 948 | enableBuildCallback: true 949 | }); 950 | 951 | //If current map is not normalized, wait for that 952 | //normalized name to load instead of continuing. 953 | if (this.map.unnormalized) { 954 | //Normalize the ID if the plugin allows it. 955 | if (plugin.normalize) { 956 | name = plugin.normalize(name, function (name) { 957 | return normalize(name, parentName, true); 958 | }) || ''; 959 | } 960 | 961 | //prefix and name should already be normalized, no need 962 | //for applying map config again either. 963 | normalizedMap = makeModuleMap(map.prefix + '!' + name, 964 | this.map.parentMap); 965 | on(normalizedMap, 966 | 'defined', bind(this, function (value) { 967 | this.init([], function () { return value; }, null, { 968 | enabled: true, 969 | ignore: true 970 | }); 971 | })); 972 | 973 | normalizedMod = getOwn(registry, normalizedMap.id); 974 | if (normalizedMod) { 975 | //Mark this as a dependency for this plugin, so it 976 | //can be traced for cycles. 977 | this.depMaps.push(normalizedMap); 978 | 979 | if (this.events.error) { 980 | normalizedMod.on('error', bind(this, function (err) { 981 | this.emit('error', err); 982 | })); 983 | } 984 | normalizedMod.enable(); 985 | } 986 | 987 | return; 988 | } 989 | 990 | //If a paths config, then just load that file instead to 991 | //resolve the plugin, as it is built into that paths layer. 992 | if (bundleId) { 993 | this.map.url = context.nameToUrl(bundleId); 994 | this.load(); 995 | return; 996 | } 997 | 998 | load = bind(this, function (value) { 999 | this.init([], function () { return value; }, null, { 1000 | enabled: true 1001 | }); 1002 | }); 1003 | 1004 | load.error = bind(this, function (err) { 1005 | this.inited = true; 1006 | this.error = err; 1007 | err.requireModules = [id]; 1008 | 1009 | //Remove temp unnormalized modules for this module, 1010 | //since they will never be resolved otherwise now. 1011 | eachProp(registry, function (mod) { 1012 | if (mod.map.id.indexOf(id + '_unnormalized') === 0) { 1013 | cleanRegistry(mod.map.id); 1014 | } 1015 | }); 1016 | 1017 | onError(err); 1018 | }); 1019 | 1020 | //Allow plugins to load other code without having to know the 1021 | //context or how to 'complete' the load. 1022 | load.fromText = bind(this, function (text, textAlt) { 1023 | /*jslint evil: true */ 1024 | var moduleName = map.name, 1025 | moduleMap = makeModuleMap(moduleName), 1026 | hasInteractive = useInteractive; 1027 | 1028 | //As of 2.1.0, support just passing the text, to reinforce 1029 | //fromText only being called once per resource. Still 1030 | //support old style of passing moduleName but discard 1031 | //that moduleName in favor of the internal ref. 1032 | if (textAlt) { 1033 | text = textAlt; 1034 | } 1035 | 1036 | //Turn off interactive script matching for IE for any define 1037 | //calls in the text, then turn it back on at the end. 1038 | if (hasInteractive) { 1039 | useInteractive = false; 1040 | } 1041 | 1042 | //Prime the system by creating a module instance for 1043 | //it. 1044 | getModule(moduleMap); 1045 | 1046 | //Transfer any config to this other module. 1047 | if (hasProp(config.config, id)) { 1048 | config.config[moduleName] = config.config[id]; 1049 | } 1050 | 1051 | try { 1052 | req.exec(text); 1053 | } catch (e) { 1054 | return onError(makeError('fromtexteval', 1055 | 'fromText eval for ' + id + 1056 | ' failed: ' + e, 1057 | e, 1058 | [id])); 1059 | } 1060 | 1061 | if (hasInteractive) { 1062 | useInteractive = true; 1063 | } 1064 | 1065 | //Mark this as a dependency for the plugin 1066 | //resource 1067 | this.depMaps.push(moduleMap); 1068 | 1069 | //Support anonymous modules. 1070 | context.completeLoad(moduleName); 1071 | 1072 | //Bind the value of that module to the value for this 1073 | //resource ID. 1074 | localRequire([moduleName], load); 1075 | }); 1076 | 1077 | //Use parentName here since the plugin's name is not reliable, 1078 | //could be some weird string with no path that actually wants to 1079 | //reference the parentName's path. 1080 | plugin.load(map.name, localRequire, load, config); 1081 | })); 1082 | 1083 | context.enable(pluginMap, this); 1084 | this.pluginMaps[pluginMap.id] = pluginMap; 1085 | }, 1086 | 1087 | enable: function () { 1088 | enabledRegistry[this.map.id] = this; 1089 | this.enabled = true; 1090 | 1091 | //Set flag mentioning that the module is enabling, 1092 | //so that immediate calls to the defined callbacks 1093 | //for dependencies do not trigger inadvertent load 1094 | //with the depCount still being zero. 1095 | this.enabling = true; 1096 | 1097 | //Enable each dependency 1098 | each(this.depMaps, bind(this, function (depMap, i) { 1099 | var id, mod, handler; 1100 | 1101 | if (typeof depMap === 'string') { 1102 | //Dependency needs to be converted to a depMap 1103 | //and wired up to this module. 1104 | depMap = makeModuleMap(depMap, 1105 | (this.map.isDefine ? this.map : this.map.parentMap), 1106 | false, 1107 | !this.skipMap); 1108 | this.depMaps[i] = depMap; 1109 | 1110 | handler = getOwn(handlers, depMap.id); 1111 | 1112 | if (handler) { 1113 | this.depExports[i] = handler(this); 1114 | return; 1115 | } 1116 | 1117 | this.depCount += 1; 1118 | 1119 | on(depMap, 'defined', bind(this, function (depExports) { 1120 | this.defineDep(i, depExports); 1121 | this.check(); 1122 | })); 1123 | 1124 | if (this.errback) { 1125 | on(depMap, 'error', bind(this, this.errback)); 1126 | } 1127 | } 1128 | 1129 | id = depMap.id; 1130 | mod = registry[id]; 1131 | 1132 | //Skip special modules like 'require', 'exports', 'module' 1133 | //Also, don't call enable if it is already enabled, 1134 | //important in circular dependency cases. 1135 | if (!hasProp(handlers, id) && mod && !mod.enabled) { 1136 | context.enable(depMap, this); 1137 | } 1138 | })); 1139 | 1140 | //Enable each plugin that is used in 1141 | //a dependency 1142 | eachProp(this.pluginMaps, bind(this, function (pluginMap) { 1143 | var mod = getOwn(registry, pluginMap.id); 1144 | if (mod && !mod.enabled) { 1145 | context.enable(pluginMap, this); 1146 | } 1147 | })); 1148 | 1149 | this.enabling = false; 1150 | 1151 | this.check(); 1152 | }, 1153 | 1154 | on: function (name, cb) { 1155 | var cbs = this.events[name]; 1156 | if (!cbs) { 1157 | cbs = this.events[name] = []; 1158 | } 1159 | cbs.push(cb); 1160 | }, 1161 | 1162 | emit: function (name, evt) { 1163 | each(this.events[name], function (cb) { 1164 | cb(evt); 1165 | }); 1166 | if (name === 'error') { 1167 | //Now that the error handler was triggered, remove 1168 | //the listeners, since this broken Module instance 1169 | //can stay around for a while in the registry. 1170 | delete this.events[name]; 1171 | } 1172 | } 1173 | }; 1174 | 1175 | function callGetModule(args) { 1176 | //Skip modules already defined. 1177 | if (!hasProp(defined, args[0])) { 1178 | getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); 1179 | } 1180 | } 1181 | 1182 | function removeListener(node, func, name, ieName) { 1183 | //Favor detachEvent because of IE9 1184 | //issue, see attachEvent/addEventListener comment elsewhere 1185 | //in this file. 1186 | if (node.detachEvent && !isOpera) { 1187 | //Probably IE. If not it will throw an error, which will be 1188 | //useful to know. 1189 | if (ieName) { 1190 | node.detachEvent(ieName, func); 1191 | } 1192 | } else { 1193 | node.removeEventListener(name, func, false); 1194 | } 1195 | } 1196 | 1197 | /** 1198 | * Given an event from a script node, get the requirejs info from it, 1199 | * and then removes the event listeners on the node. 1200 | * @param {Event} evt 1201 | * @returns {Object} 1202 | */ 1203 | function getScriptData(evt) { 1204 | //Using currentTarget instead of target for Firefox 2.0's sake. Not 1205 | //all old browsers will be supported, but this one was easy enough 1206 | //to support and still makes sense. 1207 | var node = evt.currentTarget || evt.srcElement; 1208 | 1209 | //Remove the listeners once here. 1210 | removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange'); 1211 | removeListener(node, context.onScriptError, 'error'); 1212 | 1213 | return { 1214 | node: node, 1215 | id: node && node.getAttribute('data-requiremodule') 1216 | }; 1217 | } 1218 | 1219 | function intakeDefines() { 1220 | var args; 1221 | 1222 | //Any defined modules in the global queue, intake them now. 1223 | takeGlobalQueue(); 1224 | 1225 | //Make sure any remaining defQueue items get properly processed. 1226 | while (defQueue.length) { 1227 | args = defQueue.shift(); 1228 | if (args[0] === null) { 1229 | return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); 1230 | } else { 1231 | //args are id, deps, factory. Should be normalized by the 1232 | //define() function. 1233 | callGetModule(args); 1234 | } 1235 | } 1236 | } 1237 | 1238 | context = { 1239 | config: config, 1240 | contextName: contextName, 1241 | registry: registry, 1242 | defined: defined, 1243 | urlFetched: urlFetched, 1244 | defQueue: defQueue, 1245 | Module: Module, 1246 | makeModuleMap: makeModuleMap, 1247 | nextTick: req.nextTick, 1248 | onError: onError, 1249 | 1250 | /** 1251 | * Set a configuration for the context. 1252 | * @param {Object} cfg config object to integrate. 1253 | */ 1254 | configure: function (cfg) { 1255 | //Make sure the baseUrl ends in a slash. 1256 | if (cfg.baseUrl) { 1257 | if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') { 1258 | cfg.baseUrl += '/'; 1259 | } 1260 | } 1261 | 1262 | //Save off the paths since they require special processing, 1263 | //they are additive. 1264 | var shim = config.shim, 1265 | objs = { 1266 | paths: true, 1267 | bundles: true, 1268 | config: true, 1269 | map: true 1270 | }; 1271 | 1272 | eachProp(cfg, function (value, prop) { 1273 | if (objs[prop]) { 1274 | if (!config[prop]) { 1275 | config[prop] = {}; 1276 | } 1277 | mixin(config[prop], value, true, true); 1278 | } else { 1279 | config[prop] = value; 1280 | } 1281 | }); 1282 | 1283 | //Reverse map the bundles 1284 | if (cfg.bundles) { 1285 | eachProp(cfg.bundles, function (value, prop) { 1286 | each(value, function (v) { 1287 | if (v !== prop) { 1288 | bundlesMap[v] = prop; 1289 | } 1290 | }); 1291 | }); 1292 | } 1293 | 1294 | //Merge shim 1295 | if (cfg.shim) { 1296 | eachProp(cfg.shim, function (value, id) { 1297 | //Normalize the structure 1298 | if (isArray(value)) { 1299 | value = { 1300 | deps: value 1301 | }; 1302 | } 1303 | if ((value.exports || value.init) && !value.exportsFn) { 1304 | value.exportsFn = context.makeShimExports(value); 1305 | } 1306 | shim[id] = value; 1307 | }); 1308 | config.shim = shim; 1309 | } 1310 | 1311 | //Adjust packages if necessary. 1312 | if (cfg.packages) { 1313 | each(cfg.packages, function (pkgObj) { 1314 | var location, name; 1315 | 1316 | pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; 1317 | 1318 | name = pkgObj.name; 1319 | location = pkgObj.location; 1320 | if (location) { 1321 | config.paths[name] = pkgObj.location; 1322 | } 1323 | 1324 | //Save pointer to main module ID for pkg name. 1325 | //Remove leading dot in main, so main paths are normalized, 1326 | //and remove any trailing .js, since different package 1327 | //envs have different conventions: some use a module name, 1328 | //some use a file name. 1329 | config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main') 1330 | .replace(currDirRegExp, '') 1331 | .replace(jsSuffixRegExp, ''); 1332 | }); 1333 | } 1334 | 1335 | //If there are any "waiting to execute" modules in the registry, 1336 | //update the maps for them, since their info, like URLs to load, 1337 | //may have changed. 1338 | eachProp(registry, function (mod, id) { 1339 | //If module already has init called, since it is too 1340 | //late to modify them, and ignore unnormalized ones 1341 | //since they are transient. 1342 | if (!mod.inited && !mod.map.unnormalized) { 1343 | mod.map = makeModuleMap(id); 1344 | } 1345 | }); 1346 | 1347 | //If a deps array or a config callback is specified, then call 1348 | //require with those args. This is useful when require is defined as a 1349 | //config object before require.js is loaded. 1350 | if (cfg.deps || cfg.callback) { 1351 | context.require(cfg.deps || [], cfg.callback); 1352 | } 1353 | }, 1354 | 1355 | makeShimExports: function (value) { 1356 | function fn() { 1357 | var ret; 1358 | if (value.init) { 1359 | ret = value.init.apply(global, arguments); 1360 | } 1361 | return ret || (value.exports && getGlobal(value.exports)); 1362 | } 1363 | return fn; 1364 | }, 1365 | 1366 | makeRequire: function (relMap, options) { 1367 | options = options || {}; 1368 | 1369 | function localRequire(deps, callback, errback) { 1370 | var id, map, requireMod; 1371 | 1372 | if (options.enableBuildCallback && callback && isFunction(callback)) { 1373 | callback.__requireJsBuild = true; 1374 | } 1375 | 1376 | if (typeof deps === 'string') { 1377 | if (isFunction(callback)) { 1378 | //Invalid call 1379 | return onError(makeError('requireargs', 'Invalid require call'), errback); 1380 | } 1381 | 1382 | //If require|exports|module are requested, get the 1383 | //value for them from the special handlers. Caveat: 1384 | //this only works while module is being defined. 1385 | if (relMap && hasProp(handlers, deps)) { 1386 | return handlers[deps](registry[relMap.id]); 1387 | } 1388 | 1389 | //Synchronous access to one module. If require.get is 1390 | //available (as in the Node adapter), prefer that. 1391 | if (req.get) { 1392 | return req.get(context, deps, relMap, localRequire); 1393 | } 1394 | 1395 | //Normalize module name, if it contains . or .. 1396 | map = makeModuleMap(deps, relMap, false, true); 1397 | id = map.id; 1398 | 1399 | if (!hasProp(defined, id)) { 1400 | return onError(makeError('notloaded', 'Module name "' + 1401 | id + 1402 | '" has not been loaded yet for context: ' + 1403 | contextName + 1404 | (relMap ? '' : '. Use require([])'))); 1405 | } 1406 | return defined[id]; 1407 | } 1408 | 1409 | //Grab defines waiting in the global queue. 1410 | intakeDefines(); 1411 | 1412 | //Mark all the dependencies as needing to be loaded. 1413 | context.nextTick(function () { 1414 | //Some defines could have been added since the 1415 | //require call, collect them. 1416 | intakeDefines(); 1417 | 1418 | requireMod = getModule(makeModuleMap(null, relMap)); 1419 | 1420 | //Store if map config should be applied to this require 1421 | //call for dependencies. 1422 | requireMod.skipMap = options.skipMap; 1423 | 1424 | requireMod.init(deps, callback, errback, { 1425 | enabled: true 1426 | }); 1427 | 1428 | checkLoaded(); 1429 | }); 1430 | 1431 | return localRequire; 1432 | } 1433 | 1434 | mixin(localRequire, { 1435 | isBrowser: isBrowser, 1436 | 1437 | /** 1438 | * Converts a module name + .extension into an URL path. 1439 | * *Requires* the use of a module name. It does not support using 1440 | * plain URLs like nameToUrl. 1441 | */ 1442 | toUrl: function (moduleNamePlusExt) { 1443 | var ext, 1444 | index = moduleNamePlusExt.lastIndexOf('.'), 1445 | segment = moduleNamePlusExt.split('/')[0], 1446 | isRelative = segment === '.' || segment === '..'; 1447 | 1448 | //Have a file extension alias, and it is not the 1449 | //dots from a relative path. 1450 | if (index !== -1 && (!isRelative || index > 1)) { 1451 | ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); 1452 | moduleNamePlusExt = moduleNamePlusExt.substring(0, index); 1453 | } 1454 | 1455 | return context.nameToUrl(normalize(moduleNamePlusExt, 1456 | relMap && relMap.id, true), ext, true); 1457 | }, 1458 | 1459 | defined: function (id) { 1460 | return hasProp(defined, makeModuleMap(id, relMap, false, true).id); 1461 | }, 1462 | 1463 | specified: function (id) { 1464 | id = makeModuleMap(id, relMap, false, true).id; 1465 | return hasProp(defined, id) || hasProp(registry, id); 1466 | } 1467 | }); 1468 | 1469 | //Only allow undef on top level require calls 1470 | if (!relMap) { 1471 | localRequire.undef = function (id) { 1472 | //Bind any waiting define() calls to this context, 1473 | //fix for #408 1474 | takeGlobalQueue(); 1475 | 1476 | var map = makeModuleMap(id, relMap, true), 1477 | mod = getOwn(registry, id); 1478 | 1479 | removeScript(id); 1480 | 1481 | delete defined[id]; 1482 | delete urlFetched[map.url]; 1483 | delete undefEvents[id]; 1484 | 1485 | //Clean queued defines too. Go backwards 1486 | //in array so that the splices do not 1487 | //mess up the iteration. 1488 | eachReverse(defQueue, function(args, i) { 1489 | if(args[0] === id) { 1490 | defQueue.splice(i, 1); 1491 | } 1492 | }); 1493 | 1494 | if (mod) { 1495 | //Hold on to listeners in case the 1496 | //module will be attempted to be reloaded 1497 | //using a different config. 1498 | if (mod.events.defined) { 1499 | undefEvents[id] = mod.events; 1500 | } 1501 | 1502 | cleanRegistry(id); 1503 | } 1504 | }; 1505 | } 1506 | 1507 | return localRequire; 1508 | }, 1509 | 1510 | /** 1511 | * Called to enable a module if it is still in the registry 1512 | * awaiting enablement. A second arg, parent, the parent module, 1513 | * is passed in for context, when this method is overridden by 1514 | * the optimizer. Not shown here to keep code compact. 1515 | */ 1516 | enable: function (depMap) { 1517 | var mod = getOwn(registry, depMap.id); 1518 | if (mod) { 1519 | getModule(depMap).enable(); 1520 | } 1521 | }, 1522 | 1523 | /** 1524 | * Internal method used by environment adapters to complete a load event. 1525 | * A load event could be a script load or just a load pass from a synchronous 1526 | * load call. 1527 | * @param {String} moduleName the name of the module to potentially complete. 1528 | */ 1529 | completeLoad: function (moduleName) { 1530 | var found, args, mod, 1531 | shim = getOwn(config.shim, moduleName) || {}, 1532 | shExports = shim.exports; 1533 | 1534 | takeGlobalQueue(); 1535 | 1536 | while (defQueue.length) { 1537 | args = defQueue.shift(); 1538 | if (args[0] === null) { 1539 | args[0] = moduleName; 1540 | //If already found an anonymous module and bound it 1541 | //to this name, then this is some other anon module 1542 | //waiting for its completeLoad to fire. 1543 | if (found) { 1544 | break; 1545 | } 1546 | found = true; 1547 | } else if (args[0] === moduleName) { 1548 | //Found matching define call for this script! 1549 | found = true; 1550 | } 1551 | 1552 | callGetModule(args); 1553 | } 1554 | 1555 | //Do this after the cycle of callGetModule in case the result 1556 | //of those calls/init calls changes the registry. 1557 | mod = getOwn(registry, moduleName); 1558 | 1559 | if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) { 1560 | if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { 1561 | if (hasPathFallback(moduleName)) { 1562 | return; 1563 | } else { 1564 | return onError(makeError('nodefine', 1565 | 'No define call for ' + moduleName, 1566 | null, 1567 | [moduleName])); 1568 | } 1569 | } else { 1570 | //A script that does not call define(), so just simulate 1571 | //the call for it. 1572 | callGetModule([moduleName, (shim.deps || []), shim.exportsFn]); 1573 | } 1574 | } 1575 | 1576 | checkLoaded(); 1577 | }, 1578 | 1579 | /** 1580 | * Converts a module name to a file path. Supports cases where 1581 | * moduleName may actually be just an URL. 1582 | * Note that it **does not** call normalize on the moduleName, 1583 | * it is assumed to have already been normalized. This is an 1584 | * internal API, not a public one. Use toUrl for the public API. 1585 | */ 1586 | nameToUrl: function (moduleName, ext, skipExt) { 1587 | var paths, syms, i, parentModule, url, 1588 | parentPath, bundleId, 1589 | pkgMain = getOwn(config.pkgs, moduleName); 1590 | 1591 | if (pkgMain) { 1592 | moduleName = pkgMain; 1593 | } 1594 | 1595 | bundleId = getOwn(bundlesMap, moduleName); 1596 | 1597 | if (bundleId) { 1598 | return context.nameToUrl(bundleId, ext, skipExt); 1599 | } 1600 | 1601 | //If a colon is in the URL, it indicates a protocol is used and it is just 1602 | //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) 1603 | //or ends with .js, then assume the user meant to use an url and not a module id. 1604 | //The slash is important for protocol-less URLs as well as full paths. 1605 | if (req.jsExtRegExp.test(moduleName)) { 1606 | //Just a plain path, not module name lookup, so just return it. 1607 | //Add extension if it is included. This is a bit wonky, only non-.js things pass 1608 | //an extension, this method probably needs to be reworked. 1609 | url = moduleName + (ext || ''); 1610 | } else { 1611 | //A module that needs to be converted to a path. 1612 | paths = config.paths; 1613 | 1614 | syms = moduleName.split('/'); 1615 | //For each module name segment, see if there is a path 1616 | //registered for it. Start with most specific name 1617 | //and work up from it. 1618 | for (i = syms.length; i > 0; i -= 1) { 1619 | parentModule = syms.slice(0, i).join('/'); 1620 | 1621 | parentPath = getOwn(paths, parentModule); 1622 | if (parentPath) { 1623 | //If an array, it means there are a few choices, 1624 | //Choose the one that is desired 1625 | if (isArray(parentPath)) { 1626 | parentPath = parentPath[0]; 1627 | } 1628 | syms.splice(0, i, parentPath); 1629 | break; 1630 | } 1631 | } 1632 | 1633 | //Join the path parts together, then figure out if baseUrl is needed. 1634 | url = syms.join('/'); 1635 | url += (ext || (/^data\:|\?/.test(url) || skipExt ? '' : '.js')); 1636 | url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; 1637 | } 1638 | 1639 | return config.urlArgs ? url + 1640 | ((url.indexOf('?') === -1 ? '?' : '&') + 1641 | config.urlArgs) : url; 1642 | }, 1643 | 1644 | //Delegates to req.load. Broken out as a separate function to 1645 | //allow overriding in the optimizer. 1646 | load: function (id, url) { 1647 | req.load(context, id, url); 1648 | }, 1649 | 1650 | /** 1651 | * Executes a module callback function. Broken out as a separate function 1652 | * solely to allow the build system to sequence the files in the built 1653 | * layer in the right sequence. 1654 | * 1655 | * @private 1656 | */ 1657 | execCb: function (name, callback, args, exports) { 1658 | return callback.apply(exports, args); 1659 | }, 1660 | 1661 | /** 1662 | * callback for script loads, used to check status of loading. 1663 | * 1664 | * @param {Event} evt the event from the browser for the script 1665 | * that was loaded. 1666 | */ 1667 | onScriptLoad: function (evt) { 1668 | //Using currentTarget instead of target for Firefox 2.0's sake. Not 1669 | //all old browsers will be supported, but this one was easy enough 1670 | //to support and still makes sense. 1671 | if (evt.type === 'load' || 1672 | (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { 1673 | //Reset interactive script so a script node is not held onto for 1674 | //to long. 1675 | interactiveScript = null; 1676 | 1677 | //Pull out the name of the module and the context. 1678 | var data = getScriptData(evt); 1679 | context.completeLoad(data.id); 1680 | } 1681 | }, 1682 | 1683 | /** 1684 | * Callback for script errors. 1685 | */ 1686 | onScriptError: function (evt) { 1687 | var data = getScriptData(evt); 1688 | if (!hasPathFallback(data.id)) { 1689 | return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id])); 1690 | } 1691 | } 1692 | }; 1693 | 1694 | context.require = context.makeRequire(); 1695 | return context; 1696 | } 1697 | 1698 | /** 1699 | * Main entry point. 1700 | * 1701 | * If the only argument to require is a string, then the module that 1702 | * is represented by that string is fetched for the appropriate context. 1703 | * 1704 | * If the first argument is an array, then it will be treated as an array 1705 | * of dependency string names to fetch. An optional function callback can 1706 | * be specified to execute when all of those dependencies are available. 1707 | * 1708 | * Make a local req variable to help Caja compliance (it assumes things 1709 | * on a require that are not standardized), and to give a short 1710 | * name for minification/local scope use. 1711 | */ 1712 | req = requirejs = function (deps, callback, errback, optional) { 1713 | 1714 | //Find the right context, use default 1715 | var context, config, 1716 | contextName = defContextName; 1717 | 1718 | // Determine if have config object in the call. 1719 | if (!isArray(deps) && typeof deps !== 'string') { 1720 | // deps is a config object 1721 | config = deps; 1722 | if (isArray(callback)) { 1723 | // Adjust args if there are dependencies 1724 | deps = callback; 1725 | callback = errback; 1726 | errback = optional; 1727 | } else { 1728 | deps = []; 1729 | } 1730 | } 1731 | 1732 | if (config && config.context) { 1733 | contextName = config.context; 1734 | } 1735 | 1736 | context = getOwn(contexts, contextName); 1737 | if (!context) { 1738 | context = contexts[contextName] = req.s.newContext(contextName); 1739 | } 1740 | 1741 | if (config) { 1742 | context.configure(config); 1743 | } 1744 | 1745 | return context.require(deps, callback, errback); 1746 | }; 1747 | 1748 | /** 1749 | * Support require.config() to make it easier to cooperate with other 1750 | * AMD loaders on globally agreed names. 1751 | */ 1752 | req.config = function (config) { 1753 | return req(config); 1754 | }; 1755 | 1756 | /** 1757 | * Execute something after the current tick 1758 | * of the event loop. Override for other envs 1759 | * that have a better solution than setTimeout. 1760 | * @param {Function} fn function to execute later. 1761 | */ 1762 | req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { 1763 | setTimeout(fn, 4); 1764 | } : function (fn) { fn(); }; 1765 | 1766 | /** 1767 | * Export require as a global, but only if it does not already exist. 1768 | */ 1769 | if (!require) { 1770 | require = req; 1771 | } 1772 | 1773 | req.version = version; 1774 | 1775 | //Used to filter out dependencies that are already paths. 1776 | req.jsExtRegExp = /^\/|:|\?|\.js$/; 1777 | req.isBrowser = isBrowser; 1778 | s = req.s = { 1779 | contexts: contexts, 1780 | newContext: newContext 1781 | }; 1782 | 1783 | //Create default context. 1784 | req({}); 1785 | 1786 | //Exports some context-sensitive methods on global require. 1787 | each([ 1788 | 'toUrl', 1789 | 'undef', 1790 | 'defined', 1791 | 'specified' 1792 | ], function (prop) { 1793 | //Reference from contexts instead of early binding to default context, 1794 | //so that during builds, the latest instance of the default context 1795 | //with its config gets used. 1796 | req[prop] = function () { 1797 | var ctx = contexts[defContextName]; 1798 | return ctx.require[prop].apply(ctx, arguments); 1799 | }; 1800 | }); 1801 | 1802 | if (isBrowser) { 1803 | head = s.head = document.getElementsByTagName('head')[0]; 1804 | //If BASE tag is in play, using appendChild is a problem for IE6. 1805 | //When that browser dies, this can be removed. Details in this jQuery bug: 1806 | //http://dev.jquery.com/ticket/2709 1807 | baseElement = document.getElementsByTagName('base')[0]; 1808 | if (baseElement) { 1809 | head = s.head = baseElement.parentNode; 1810 | } 1811 | } 1812 | 1813 | /** 1814 | * Any errors that require explicitly generates will be passed to this 1815 | * function. Intercept/override it if you want custom error handling. 1816 | * @param {Error} err the error object. 1817 | */ 1818 | req.onError = defaultOnError; 1819 | 1820 | /** 1821 | * Creates the node for the load command. Only used in browser envs. 1822 | */ 1823 | req.createNode = function (config, moduleName, url) { 1824 | var node = config.xhtml ? 1825 | document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : 1826 | document.createElement('script'); 1827 | node.type = config.scriptType || 'text/javascript'; 1828 | node.charset = 'utf-8'; 1829 | node.async = true; 1830 | return node; 1831 | }; 1832 | 1833 | /** 1834 | * Does the request to load a module for the browser case. 1835 | * Make this a separate function to allow other environments 1836 | * to override it. 1837 | * 1838 | * @param {Object} context the require context to find state. 1839 | * @param {String} moduleName the name of the module. 1840 | * @param {Object} url the URL to the module. 1841 | */ 1842 | req.load = function (context, moduleName, url) { 1843 | var config = (context && context.config) || {}, 1844 | node; 1845 | if (isBrowser) { 1846 | //In the browser so use a script tag 1847 | node = req.createNode(config, moduleName, url); 1848 | 1849 | node.setAttribute('data-requirecontext', context.contextName); 1850 | node.setAttribute('data-requiremodule', moduleName); 1851 | 1852 | //Set up load listener. Test attachEvent first because IE9 has 1853 | //a subtle issue in its addEventListener and script onload firings 1854 | //that do not match the behavior of all other browsers with 1855 | //addEventListener support, which fire the onload event for a 1856 | //script right after the script execution. See: 1857 | //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution 1858 | //UNFORTUNATELY Opera implements attachEvent but does not follow the script 1859 | //script execution mode. 1860 | if (node.attachEvent && 1861 | //Check if node.attachEvent is artificially added by custom script or 1862 | //natively supported by browser 1863 | //read https://github.com/jrburke/requirejs/issues/187 1864 | //if we can NOT find [native code] then it must NOT natively supported. 1865 | //in IE8, node.attachEvent does not have toString() 1866 | //Note the test for "[native code" with no closing brace, see: 1867 | //https://github.com/jrburke/requirejs/issues/273 1868 | !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && 1869 | !isOpera) { 1870 | //Probably IE. IE (at least 6-8) do not fire 1871 | //script onload right after executing the script, so 1872 | //we cannot tie the anonymous define call to a name. 1873 | //However, IE reports the script as being in 'interactive' 1874 | //readyState at the time of the define call. 1875 | useInteractive = true; 1876 | 1877 | node.attachEvent('onreadystatechange', context.onScriptLoad); 1878 | //It would be great to add an error handler here to catch 1879 | //404s in IE9+. However, onreadystatechange will fire before 1880 | //the error handler, so that does not help. If addEventListener 1881 | //is used, then IE will fire error before load, but we cannot 1882 | //use that pathway given the connect.microsoft.com issue 1883 | //mentioned above about not doing the 'script execute, 1884 | //then fire the script load event listener before execute 1885 | //next script' that other browsers do. 1886 | //Best hope: IE10 fixes the issues, 1887 | //and then destroys all installs of IE 6-9. 1888 | //node.attachEvent('onerror', context.onScriptError); 1889 | } else { 1890 | node.addEventListener('load', context.onScriptLoad, false); 1891 | node.addEventListener('error', context.onScriptError, false); 1892 | } 1893 | node.src = url; 1894 | 1895 | //For some cache cases in IE 6-8, the script executes before the end 1896 | //of the appendChild execution, so to tie an anonymous define 1897 | //call to the module name (which is stored on the node), hold on 1898 | //to a reference to this node, but clear after the DOM insertion. 1899 | currentlyAddingScript = node; 1900 | if (baseElement) { 1901 | head.insertBefore(node, baseElement); 1902 | } else { 1903 | head.appendChild(node); 1904 | } 1905 | currentlyAddingScript = null; 1906 | 1907 | return node; 1908 | } else if (isWebWorker) { 1909 | try { 1910 | //In a web worker, use importScripts. This is not a very 1911 | //efficient use of importScripts, importScripts will block until 1912 | //its script is downloaded and evaluated. However, if web workers 1913 | //are in play, the expectation that a build has been done so that 1914 | //only one script needs to be loaded anyway. This may need to be 1915 | //reevaluated if other use cases become common. 1916 | importScripts(url); 1917 | 1918 | //Account for anonymous modules 1919 | context.completeLoad(moduleName); 1920 | } catch (e) { 1921 | context.onError(makeError('importscripts', 1922 | 'importScripts failed for ' + 1923 | moduleName + ' at ' + url, 1924 | e, 1925 | [moduleName])); 1926 | } 1927 | } 1928 | }; 1929 | 1930 | function getInteractiveScript() { 1931 | if (interactiveScript && interactiveScript.readyState === 'interactive') { 1932 | return interactiveScript; 1933 | } 1934 | 1935 | eachReverse(scripts(), function (script) { 1936 | if (script.readyState === 'interactive') { 1937 | return (interactiveScript = script); 1938 | } 1939 | }); 1940 | return interactiveScript; 1941 | } 1942 | 1943 | //Look for a data-main script attribute, which could also adjust the baseUrl. 1944 | if (isBrowser && !cfg.skipDataMain) { 1945 | //Figure out baseUrl. Get it from the script tag with require.js in it. 1946 | eachReverse(scripts(), function (script) { 1947 | //Set the 'head' where we can append children by 1948 | //using the script's parent. 1949 | if (!head) { 1950 | head = script.parentNode; 1951 | } 1952 | 1953 | //Look for a data-main attribute to set main script for the page 1954 | //to load. If it is there, the path to data main becomes the 1955 | //baseUrl, if it is not already set. 1956 | dataMain = script.getAttribute('data-main'); 1957 | if (dataMain) { 1958 | //Preserve dataMain in case it is a path (i.e. contains '?') 1959 | mainScript = dataMain; 1960 | 1961 | //Set final baseUrl if there is not already an explicit one. 1962 | if (!cfg.baseUrl) { 1963 | //Pull off the directory of data-main for use as the 1964 | //baseUrl. 1965 | src = mainScript.split('/'); 1966 | mainScript = src.pop(); 1967 | subPath = src.length ? src.join('/') + '/' : './'; 1968 | 1969 | cfg.baseUrl = subPath; 1970 | } 1971 | 1972 | //Strip off any trailing .js since mainScript is now 1973 | //like a module name. 1974 | mainScript = mainScript.replace(jsSuffixRegExp, ''); 1975 | 1976 | //If mainScript is still a path, fall back to dataMain 1977 | if (req.jsExtRegExp.test(mainScript)) { 1978 | mainScript = dataMain; 1979 | } 1980 | 1981 | //Put the data-main script in the files to load. 1982 | cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript]; 1983 | 1984 | return true; 1985 | } 1986 | }); 1987 | } 1988 | 1989 | /** 1990 | * The function that handles definitions of modules. Differs from 1991 | * require() in that a string for the module should be the first argument, 1992 | * and the function to execute after dependencies are loaded should 1993 | * return a value to define the module corresponding to the first argument's 1994 | * name. 1995 | */ 1996 | define = function (name, deps, callback) { 1997 | var node, context; 1998 | 1999 | //Allow for anonymous modules 2000 | if (typeof name !== 'string') { 2001 | //Adjust args appropriately 2002 | callback = deps; 2003 | deps = name; 2004 | name = null; 2005 | } 2006 | 2007 | //This module may not have dependencies 2008 | if (!isArray(deps)) { 2009 | callback = deps; 2010 | deps = null; 2011 | } 2012 | 2013 | //If no name, and callback is a function, then figure out if it a 2014 | //CommonJS thing with dependencies. 2015 | if (!deps && isFunction(callback)) { 2016 | deps = []; 2017 | //Remove comments from the callback string, 2018 | //look for require calls, and pull them into the dependencies, 2019 | //but only if there are function args. 2020 | if (callback.length) { 2021 | callback 2022 | .toString() 2023 | .replace(commentRegExp, '') 2024 | .replace(cjsRequireRegExp, function (match, dep) { 2025 | deps.push(dep); 2026 | }); 2027 | 2028 | //May be a CommonJS thing even without require calls, but still 2029 | //could use exports, and module. Avoid doing exports and module 2030 | //work though if it just needs require. 2031 | //REQUIRES the function to expect the CommonJS variables in the 2032 | //order listed below. 2033 | deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps); 2034 | } 2035 | } 2036 | 2037 | //If in IE 6-8 and hit an anonymous define() call, do the interactive 2038 | //work. 2039 | if (useInteractive) { 2040 | node = currentlyAddingScript || getInteractiveScript(); 2041 | if (node) { 2042 | if (!name) { 2043 | name = node.getAttribute('data-requiremodule'); 2044 | } 2045 | context = contexts[node.getAttribute('data-requirecontext')]; 2046 | } 2047 | } 2048 | 2049 | //Always save off evaluating the def call until the script onload handler. 2050 | //This allows multiple modules to be in a file without prematurely 2051 | //tracing dependencies, and allows for anonymous module support, 2052 | //where the module name is not known until the script onload event 2053 | //occurs. If no context, use the global queue, and get it processed 2054 | //in the onscript load callback. 2055 | (context ? context.defQueue : globalDefQueue).push([name, deps, callback]); 2056 | }; 2057 | 2058 | define.amd = { 2059 | jQuery: true 2060 | }; 2061 | 2062 | 2063 | /** 2064 | * Executes the text. Normally just uses eval, but can be modified 2065 | * to use a better, environment-specific call. Only used for transpiling 2066 | * loader plugins, not for plain JS modules. 2067 | * @param {String} text the text to execute/evaluate. 2068 | */ 2069 | req.exec = function (text) { 2070 | /*jslint evil: true */ 2071 | return eval(text); 2072 | }; 2073 | 2074 | //Set up with config info. 2075 | req(cfg); 2076 | }(this)); 2077 | --------------------------------------------------------------------------------