mirror of
https://github.com/apache/cordova-android.git
synced 2026-04-23 00:00:09 +08:00
slightly more coherent organization of concerns
This commit is contained in:
Generated
+5
@@ -0,0 +1,5 @@
|
||||
dist
|
||||
stamp-build
|
||||
*~
|
||||
gmon.out
|
||||
v8.log
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
dist
|
||||
stamp-build
|
||||
test/fixtures/dir2
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
Nodeunit contributors (sorted alphabeticaly)
|
||||
============================================
|
||||
|
||||
* **[Alex Gorbatchev](https://github.com/alexgorbatchev)**
|
||||
|
||||
* Deeper default object inspection
|
||||
* Timeout to ensure flushing of console output (default reporter)
|
||||
|
||||
* **[Alex Wolfe](https://github.com/alexkwolfe)**
|
||||
|
||||
* HTML test reporter
|
||||
|
||||
* **[Caolan McMahon](https://github.com/caolan)**
|
||||
|
||||
* Author and maintainer
|
||||
* Most features develpopment
|
||||
|
||||
* **[Carl Fürstenberg](https://github.com/azatoth)**
|
||||
|
||||
* Debian-friendly Makefile, supports both 'node' and 'nodejs' executables
|
||||
* Sandbox utility
|
||||
* Minimal test reporter
|
||||
|
||||
* **[Gerad Suyderhoud](https://github.com/gerad)**
|
||||
|
||||
* First comand-line tool
|
||||
|
||||
* **[Kadir Pekel](https://github.com/coffeemate)**
|
||||
|
||||
* Improvements to default test reporter
|
||||
* HTTP test utility
|
||||
|
||||
* **[Matthias Lübken](https://github.com/luebken)**
|
||||
|
||||
* Utility functions for tracking incomplete tests on exit
|
||||
|
||||
* **[Oleg Efimov](https://github.com/Sannis)**
|
||||
|
||||
* Adding 'make lint' and fixing nodelint errors
|
||||
* Option parsing, --help text and config file support
|
||||
* Reporters option for command-line tool
|
||||
|
||||
* **[Orlando Vazquez](https://github.com/orlandov)**
|
||||
|
||||
* Added jUnit XML reporter
|
||||
|
||||
* **[Ryan Dahl](https://github.com/ry)**
|
||||
|
||||
* Add package.json
|
||||
|
||||
* **[Sam Stephenson](https://github.com/sstephenson)**
|
||||
|
||||
* Coffee-script support
|
||||
|
||||
* **[Thomas Mayfield](https://github.com/thegreatape)**
|
||||
|
||||
* Async setUp and tearDown support for testCase
|
||||
|
||||
**[Full contributors list](https://github.com/caolan/nodeunit/contributors).**
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2010 Caolan McMahon
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
PACKAGE = nodeunit
|
||||
NODEJS = $(if $(shell test -f /usr/bin/nodejs && echo "true"),nodejs,node)
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
DATADIR ?= $(PREFIX)/share
|
||||
MANDIR ?= $(PREFIX)/share/man
|
||||
LIBDIR ?= $(PREFIX)/lib
|
||||
NODEJSLIBDIR ?= $(LIBDIR)/$(NODEJS)
|
||||
|
||||
BUILDDIR = dist
|
||||
|
||||
DOCS = $(shell find doc -name '*.md' \
|
||||
|sed 's|.md|.1|g' \
|
||||
|sed 's|doc/|man1/|g' \
|
||||
)
|
||||
|
||||
|
||||
$(shell if [ ! -d $(BUILDDIR) ]; then mkdir $(BUILDDIR); fi)
|
||||
|
||||
all: build doc
|
||||
|
||||
browser:
|
||||
# super hacky build script for browser version!
|
||||
mkdir -p $(BUILDDIR)/browser
|
||||
rm -rf $(BUILDDIR)/browser/*
|
||||
# build browser version of nodeunit.js
|
||||
cat share/license.js >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "nodeunit = (function(){" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
cat deps/json2.js >> $(BUILDDIR)/browser/nodeunit.js
|
||||
# make assert global
|
||||
echo "var assert = this.assert = {};" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "var types = {};" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "var core = {};" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "var nodeunit = {};" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "var reporter = {};" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
cat deps/async.js >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "(function(exports){" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
cat lib/assert.js >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "})(assert);" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "(function(exports){" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
cat lib/types.js >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "})(types);" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "(function(exports){" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
cat lib/core.js >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "})(core);" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "(function(exports){" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
cat lib/reporters/browser.js >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "})(reporter);" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "nodeunit = core;" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "nodeunit.assert = assert;" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "nodeunit.reporter = reporter;" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "nodeunit.run = reporter.run;" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
echo "return nodeunit; })();" >> $(BUILDDIR)/browser/nodeunit.js
|
||||
sed -i "/\@REMOVE_LINE_FOR_BROWSER/d" $(BUILDDIR)/browser/nodeunit.js
|
||||
# copy nodeunit.css
|
||||
cp share/nodeunit.css $(BUILDDIR)/browser/nodeunit.css
|
||||
# create nodeunit.min.js
|
||||
uglifyjs $(BUILDDIR)/browser/nodeunit.js > $(BUILDDIR)/browser/nodeunit.min.js
|
||||
# create test scripts
|
||||
mkdir -p $(BUILDDIR)/browser/test
|
||||
cp test/test.html $(BUILDDIR)/browser/test/test.html
|
||||
# test-base.js
|
||||
echo "(function (exports) {" > $(BUILDDIR)/browser/test/test-base.js
|
||||
cat test/test-base.js >> $(BUILDDIR)/browser/test/test-base.js
|
||||
echo "})(this.test_base = {});" >> $(BUILDDIR)/browser/test/test-base.js
|
||||
sed -i "/\@REMOVE_LINE_FOR_BROWSER/d" $(BUILDDIR)/browser/test/test-base.js
|
||||
# test-runmodule.js
|
||||
echo "(function (exports) {" > $(BUILDDIR)/browser/test/test-runmodule.js
|
||||
cat test/test-runmodule.js >> $(BUILDDIR)/browser/test/test-runmodule.js
|
||||
echo "})(this.test_runmodule = {});" >> $(BUILDDIR)/browser/test/test-runmodule.js
|
||||
sed -i "/\@REMOVE_LINE_FOR_BROWSER/d" $(BUILDDIR)/browser/test/test-runmodule.js
|
||||
# test-runtest.js
|
||||
echo "(function (exports) {" > $(BUILDDIR)/browser/test/test-runtest.js
|
||||
cat test/test-runtest.js >> $(BUILDDIR)/browser/test/test-runtest.js
|
||||
echo "})(this.test_runtest = {});" >> $(BUILDDIR)/browser/test/test-runtest.js
|
||||
sed -i "/\@REMOVE_LINE_FOR_BROWSER/d" $(BUILDDIR)/browser/test/test-runtest.js
|
||||
# test-testcase.js
|
||||
echo "(function (exports) {" > $(BUILDDIR)/browser/test/test-testcase.js
|
||||
cat test/test-testcase.js >> $(BUILDDIR)/browser/test/test-testcase.js
|
||||
echo "})(this.test_testcase = {});" >> $(BUILDDIR)/browser/test/test-testcase.js
|
||||
sed -i "/\@REMOVE_LINE_FOR_BROWSER/d" $(BUILDDIR)/browser/test/test-testcase.js
|
||||
# copy nodeunit.js to dist/browser/test to make it easier for me to host and
|
||||
# run on windows VMs with IE
|
||||
cp $(BUILDDIR)/browser/nodeunit.js $(BUILDDIR)/browser/test/nodeunit.js
|
||||
cp $(BUILDDIR)/browser/nodeunit.css $(BUILDDIR)/browser/test/nodeunit.css
|
||||
|
||||
build: stamp-build
|
||||
|
||||
stamp-build: $(wildcard deps/* lib/*.js)
|
||||
touch $@;
|
||||
mkdir -p $(BUILDDIR)/nodeunit
|
||||
cp -R bin deps index.js lib package.json $(BUILDDIR)/nodeunit
|
||||
printf '#!/bin/sh\n$(NODEJS) $(NODEJSLIBDIR)/$(PACKAGE)/bin/nodeunit $$@' > $(BUILDDIR)/nodeunit.sh
|
||||
|
||||
test:
|
||||
$(NODEJS) ./bin/nodeunit test
|
||||
|
||||
install: build
|
||||
install -d $(NODEJSLIBDIR)
|
||||
cp -a $(BUILDDIR)/nodeunit $(NODEJSLIBDIR)
|
||||
install -m 0755 $(BUILDDIR)/nodeunit.sh $(BINDIR)/nodeunit
|
||||
install -d $(MANDIR)/man1/
|
||||
cp -a man1/nodeunit.1 $(MANDIR)/man1/
|
||||
|
||||
uninstall:
|
||||
rm -rf $(NODEJSLIBDIR)/nodeunit $(NODEJSLIBDIR)/nodeunit.js $(BINDIR)/nodeunit
|
||||
rm -rf $(MANDIR)/man1/nodeunit.1
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILDDIR) stamp-build
|
||||
|
||||
lint:
|
||||
nodelint --config nodelint.cfg ./index.js ./bin/nodeunit ./bin/nodeunit.json ./lib/*.js ./lib/reporters/*.js ./test/*.js
|
||||
|
||||
doc: man1 $(DOCS)
|
||||
@true
|
||||
|
||||
man1:
|
||||
@if ! test -d man1 ; then mkdir -p man1 ; fi
|
||||
|
||||
# use `npm install ronn` for this to work.
|
||||
man1/%.1: doc/%.md
|
||||
ronn --roff $< > $@
|
||||
|
||||
.PHONY: browser test install uninstall build all
|
||||
+432
@@ -0,0 +1,432 @@
|
||||
Nodeunit
|
||||
========
|
||||
|
||||
Simple syntax, powerful tools. Nodeunit provides easy async unit testing for
|
||||
node.js and the browser.
|
||||
|
||||
* Simple to use
|
||||
* Just export the tests from a module
|
||||
* Works with node.js and in the browser.
|
||||
* Helps you avoid common pitfalls when testing asynchronous code
|
||||
* Easy to add test cases with setUp and tearDown functions if you wish
|
||||
* Flexible reporters for custom output, built-in support for HTML and jUnit XML
|
||||
* Allows the use of mocks and stubs
|
||||
|
||||
__Contributors__
|
||||
|
||||
* [alexgorbatchev](https://github.com/alexgorbatchev)
|
||||
* [alexkwolfe](https://github.com/alexkwolfe)
|
||||
* [azatoth](https://github.com/azatoth)
|
||||
* [coffeemate](https://github.com/coffeemate)
|
||||
* [luebken](https://github.com/luebken)
|
||||
* [orlandov](https://github.com/orlandov)
|
||||
* [Sannis](https://github.com/Sannis)
|
||||
* [sstephenson](https://github.com/sstephenson)
|
||||
* [thegreatape](https://github.com/thegreatape)
|
||||
* and thanks to [cjohansen](https://github.com/cjohansen) for input and advice
|
||||
on implementing setUp and tearDown functions. See
|
||||
[cjohansen's fork](https://github.com/cjohansen/nodeunit).
|
||||
|
||||
Also, check out gerad's [nodeunit-dsl](https://github.com/gerad/nodeunit-dsl)
|
||||
project, which implements a 'pretty dsl on top of nodeunit'.
|
||||
|
||||
More contributor information can be found in the
|
||||
[CONTRIBUTORS.md](https://github.com/caolan/nodeunit/blob/master/CONTRIBUTORS.md)
|
||||
file.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Here is an example unit test module:
|
||||
|
||||
exports.testSomething = function(test){
|
||||
test.expect(1);
|
||||
test.ok(true, "this assertion should pass");
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.testSomethingElse = function(test){
|
||||
test.ok(false, "this assertion should fail");
|
||||
test.done();
|
||||
};
|
||||
|
||||
When run using the included test runner, this will output the following:
|
||||
|
||||
<img src="https://github.com/caolan/nodeunit/raw/master/img/example_fail.png" />
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
There are two options for installing nodeunit:
|
||||
|
||||
1. Clone / download nodeunit from [github](https://github.com/caolan/nodeunit),
|
||||
then:
|
||||
|
||||
make && sudo make install
|
||||
|
||||
2. Install via npm:
|
||||
|
||||
npm install nodeunit
|
||||
|
||||
API Documentation
|
||||
-----------------
|
||||
|
||||
Nodeunit uses the functions available in the node.js
|
||||
[assert module](http://nodejs.org/docs/v0.4.2/api/assert.html):
|
||||
|
||||
* __ok(value, [message])__ - Tests if value is a true value.
|
||||
* __equal(actual, expected, [message])__ - Tests shallow, coercive equality
|
||||
with the equal comparison operator ( == ).
|
||||
* __notEqual(actual, expected, [message])__ - Tests shallow, coercive
|
||||
non-equality with the not equal comparison operator ( != ).
|
||||
* __deepEqual(actual, expected, [message])__ - Tests for deep equality.
|
||||
* __notDeepEqual(actual, expected, [message])__ - Tests for any deep
|
||||
inequality.
|
||||
* __strictEqual(actual, expected, [message])__ - Tests strict equality, as
|
||||
determined by the strict equality operator ( === )
|
||||
* __notStrictEqual(actual, expected, [message])__ - Tests strict non-equality,
|
||||
as determined by the strict not equal operator ( !== )
|
||||
* __throws(block, [error], [message])__ - Expects block to throw an error.
|
||||
* __doesNotThrow(block, [error], [message])__ - Expects block not to throw an
|
||||
error.
|
||||
* __ifError(value)__ - Tests if value is not a false value, throws if it is a
|
||||
true value. Useful when testing the first argument, error in callbacks.
|
||||
|
||||
Nodeunit also provides the following functions within tests:
|
||||
|
||||
* __expect(amount)__ - Specify how many assertions are expected to run within a
|
||||
test. Very useful for ensuring that all your callbacks and assertions are
|
||||
run.
|
||||
* __done()__ - Finish the current test function, and move on to the next. ALL
|
||||
tests should call this!
|
||||
|
||||
Nodeunit aims to be simple and easy to learn. This is achieved through using
|
||||
existing structures (such as node.js modules) to maximum effect, and reducing
|
||||
the API where possible, to make it easier to digest.
|
||||
|
||||
Tests are simply exported from a module, but they are still run in the order
|
||||
they are defined.
|
||||
|
||||
__Note:__ Users of old nodeunit versions may remember using ok, equals and same
|
||||
in the style of qunit, instead of the assert functions above. These functions
|
||||
still exist for backwards compatibility, and are simply aliases to their assert
|
||||
module counterparts.
|
||||
|
||||
|
||||
Asynchronous Testing
|
||||
--------------------
|
||||
|
||||
When testing asynchronous code, there are a number of sharp edges to watch out
|
||||
for. Thankfully, nodeunit is designed to help you avoid as many of these
|
||||
pitfalls as possible. For the most part, testing asynchronous code in nodeunit
|
||||
_just works_.
|
||||
|
||||
|
||||
### Tests run in series
|
||||
|
||||
While running tests in parallel seems like a good idea for speeding up your
|
||||
test suite, in practice I've found it means writing much more complicated
|
||||
tests. Because of node's module cache, running tests in parallel means mocking
|
||||
and stubbing is pretty much impossible. One of the nicest things about testing
|
||||
in javascript is the ease of doing stubs:
|
||||
|
||||
var _readFile = fs.readFile;
|
||||
fs.readFile = function(path, callback){
|
||||
// its a stub!
|
||||
};
|
||||
// test function that uses fs.readFile
|
||||
|
||||
// we're done
|
||||
fs.readFile = _readFile;
|
||||
|
||||
You cannot do this when running tests in parallel. In order to keep testing as
|
||||
simple as possible, nodeunit avoids it. Thankfully, most unit-test suites run
|
||||
fast anyway.
|
||||
|
||||
|
||||
### Explicit ending of tests
|
||||
|
||||
When testing async code its important that tests end at the correct point, not
|
||||
just after a given number of assertions. Otherwise your tests can run short,
|
||||
ending before all assertions have completed. Its important to detect too
|
||||
many assertions as well as too few. Combining explicit ending of tests with
|
||||
an expected number of assertions helps to avoid false test passes, so be sure
|
||||
to use the test.expect() method at the start of your test functions, and
|
||||
test.done() when finished.
|
||||
|
||||
|
||||
Groups, setUp and tearDown
|
||||
--------------------------
|
||||
|
||||
Nodeunit allows the nesting of test functions:
|
||||
|
||||
exports.test1 = function (test) {
|
||||
...
|
||||
}
|
||||
|
||||
exports.group = {
|
||||
test2: function (test) {
|
||||
...
|
||||
},
|
||||
test3: function (test) {
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
This would be run as:
|
||||
|
||||
test1
|
||||
group - test2
|
||||
group - test3
|
||||
|
||||
Using these groups its possible to add setUp and tearDown functions to your
|
||||
tests. Nodeunit has a utility function called testCase which allows you to
|
||||
define a setUp function, which is run before each test, and a tearDown
|
||||
function, which is run after each test calls test.done():
|
||||
|
||||
var testCase = require('nodeunit').testCase;
|
||||
|
||||
module.exports = testCase({
|
||||
setUp: function (callback) {
|
||||
this.foo = 'bar';
|
||||
callback();
|
||||
},
|
||||
tearDown: function (callback) {
|
||||
// clean up
|
||||
callback();
|
||||
},
|
||||
test1: function (test) {
|
||||
test.equals(this.foo, 'bar');
|
||||
test.done();
|
||||
}
|
||||
});
|
||||
|
||||
In this way, its possible to have multiple groups of tests in a module, each
|
||||
group with its own setUp and tearDown functions.
|
||||
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
|
||||
Nodeunit comes with a basic command-line test runner, which can be installed
|
||||
using 'sudo make install'. Example usage:
|
||||
|
||||
nodeunit testmodule1.js testfolder [...]
|
||||
|
||||
The default test reporter uses color output, because I think that's more fun :) I
|
||||
intend to add a no-color option in future. To give you a feeling of the fun you'll
|
||||
be having writing tests, lets fix the example at the start of the README:
|
||||
|
||||
<img src="https://github.com/caolan/nodeunit/raw/master/img/example_pass.png" />
|
||||
|
||||
Ahhh, Doesn't that feel better?
|
||||
|
||||
When using the included test runner, it will exit using the failed number of
|
||||
assertions as the exit code. Exiting with 0 when all tests pass.
|
||||
|
||||
|
||||
### Command-line Options
|
||||
|
||||
* __--reporter FILE__ - you can set the test reporter to a custom module or
|
||||
on of the modules in nodeunit/lib/reporters, when omitted, the default test runner
|
||||
is used.
|
||||
* __--list-reporters__ - list available build-in reporters.
|
||||
* __--config FILE__ - load config options from a JSON file, allows
|
||||
the customisation of color schemes for the default test reporter etc. See
|
||||
bin/nodeunit.json for current available options.
|
||||
* __--version__ or __-v__ - report nodeunit version
|
||||
* __--help__ - show nodeunit help
|
||||
|
||||
|
||||
Running tests in the browser
|
||||
----------------------------
|
||||
|
||||
Nodeunit tests can also be run inside the browser. For example usage, see
|
||||
the examples/browser folder. The basic syntax is as follows:
|
||||
|
||||
__test.html__
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Example Test Suite</title>
|
||||
<link rel="stylesheet" href="nodeunit.css" type="text/css" />
|
||||
<script src="nodeunit.js"></script>
|
||||
<script src="suite1.js"></script>
|
||||
<script src="suite2.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="nodeunit-header>Example Test Suite</h1>
|
||||
<script>
|
||||
nodeunit.run({
|
||||
'Suite One': suite1,
|
||||
'Suite Two': suite2
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Here, suite1 and suite2 are just object literals containing test functions or
|
||||
groups, as would be returned if you did require('test-suite') in node.js:
|
||||
|
||||
__suite1.js__
|
||||
|
||||
this.suite1 = {
|
||||
'example test': function (test) {
|
||||
test.ok(true, 'everything is ok');
|
||||
test.done();
|
||||
}
|
||||
};
|
||||
|
||||
If you wish to use a commonjs format for your test suites (using exports), it is
|
||||
up to you to define the commonjs tools for the browser. There are a number of
|
||||
alternatives and its important it fits with your existing code, which is
|
||||
why nodeunit does not currently provide this out of the box.
|
||||
|
||||
In the example above, the tests will run when the page is loaded.
|
||||
|
||||
The browser-version of nodeunit.js is created in dist/browser when you do, 'make
|
||||
browser'. You'll need [UglifyJS](https://github.com/mishoo/UglifyJS) installed in
|
||||
order for it to automatically create nodeunit.min.js.
|
||||
|
||||
|
||||
Adding nodeunit to Your Projects
|
||||
--------------------------------
|
||||
|
||||
If you don't want people to have to install the nodeunit command-line tool,
|
||||
you'll want to create a script that runs the tests for your project with the
|
||||
correct require paths set up. Here's an example test script, with a deps
|
||||
directory containing the projects dependencies:
|
||||
|
||||
#!/usr/bin/env node
|
||||
require.paths.unshift(__dirname + '/deps');
|
||||
|
||||
var reporter = require('nodeunit').reporters.default;
|
||||
reporter.run(['test']);
|
||||
|
||||
If you're using git, you might find it useful to include nodeunit as a
|
||||
submodule. Using submodules makes it easy for developers to download nodeunit
|
||||
and run your test suite, without cluttering up your repository with
|
||||
the source code. To add nodeunit as a git submodule do the following:
|
||||
|
||||
git submodule add git://github.com/caolan/nodeunit.git deps/nodeunit
|
||||
|
||||
This will add nodeunit to the deps folder of your project. Now, when cloning
|
||||
the repository, nodeunit can be downloaded by doing the following:
|
||||
|
||||
git submodule init
|
||||
git submodule update
|
||||
|
||||
Let's update the test script above with a helpful hint on how to get nodeunit,
|
||||
if its missing:
|
||||
|
||||
#!/usr/bin/env node
|
||||
|
||||
require.paths.unshift(__dirname + '/deps');
|
||||
|
||||
try {
|
||||
var reporter = require('nodeunit').reporters.default;
|
||||
}
|
||||
catch(e) {
|
||||
console.log("Cannot find nodeunit module.");
|
||||
console.log("You can download submodules for this project by doing:");
|
||||
console.log("");
|
||||
console.log(" git submodule init");
|
||||
console.log(" git submodule update");
|
||||
console.log("");
|
||||
process.exit();
|
||||
}
|
||||
|
||||
process.chdir(__dirname);
|
||||
reporter.run(['test']);
|
||||
|
||||
Now if someone attempts to run your test suite without nodeunit installed they
|
||||
will be prompted to download the submodules for your project.
|
||||
|
||||
|
||||
Built-in Test Reporters
|
||||
-----------------------
|
||||
|
||||
* __default__ - The standard reporter seen in the nodeunit screenshots
|
||||
* __minimal__ - Pretty, minimal output, shows errors and progress only
|
||||
* __html__ - Outputs a HTML report to stdout
|
||||
* __junit__ - Creates jUnit compatible XML reports, which can be used with
|
||||
continuous integration tools such as [Hudson](http://hudson-ci.org/).
|
||||
|
||||
|
||||
Writing a Test Reporter
|
||||
---------------------
|
||||
|
||||
Nodeunit exports runTest(fn, options), runModule(mod, options) and
|
||||
runFiles(paths, options). You'll most likely want to run test suites from
|
||||
files, which can be done using the latter function. The _options_ argument can
|
||||
contain callbacks which run during testing. Nodeunit provides the following
|
||||
callbacks:
|
||||
|
||||
* __moduleStart(name)__ - called before a module is tested
|
||||
* __moduleDone(name, assertions)__ - called once all test functions within the
|
||||
module have completed (see assertions object reference below)
|
||||
ALL tests within the module
|
||||
* __testStart(name)__ - called before a test function is run
|
||||
* __testDone(name, assertions)__ - called once a test function has completed
|
||||
(by calling test.done())
|
||||
* __log(assertion)__ - called whenever an assertion is made (see assertion
|
||||
object reference below)
|
||||
* __done(assertions)__ - called after all tests/modules are complete
|
||||
|
||||
The __assertion__ object:
|
||||
|
||||
* __passed()__ - did the assertion pass?
|
||||
* __failed()__ - did the assertion fail?
|
||||
* __error__ - the AssertionError if the assertion failed
|
||||
* __method__ - the nodeunit assertion method used (ok, same, equals...)
|
||||
* __message__ - the message the assertion method was called with (optional)
|
||||
|
||||
The __assertionList__ object:
|
||||
|
||||
* An array-like object with the following new attributes:
|
||||
* __failures()__ - the number of assertions which failed
|
||||
* __duration__ - the time taken for the test to complete in msecs
|
||||
|
||||
For a reference implementation of a test reporter, see lib/reporters/default.js in
|
||||
the nodeunit project directory.
|
||||
|
||||
|
||||
Sandbox utility
|
||||
---------------
|
||||
|
||||
This is a function which evaluates JavaScript files in a sandbox and returns the
|
||||
context. The sandbox function can be used for testing client-side code or private
|
||||
un-exported functions within a module.
|
||||
|
||||
var sandbox = require('nodeunit').utils.sandbox;
|
||||
var example = sandbox('example.js');
|
||||
|
||||
__sandbox(files, sandbox)__ - Evaluates JavaScript files in a sandbox, returning
|
||||
the context. The first argument can either be a single filename or an array of
|
||||
filenames. If multiple filenames are given their contents are concatenated before
|
||||
evalution. The second argument is an optional context to use for the sandbox.
|
||||
|
||||
|
||||
Running the nodeunit Tests
|
||||
--------------------------
|
||||
|
||||
The tests for nodeunit are written using nodeunit itself as the test framework.
|
||||
However, the module test-base.js first does some basic tests using the assert
|
||||
module to ensure that test functions are actually run, and a basic level of
|
||||
nodeunit functionality is available.
|
||||
|
||||
To run the nodeunit tests do:
|
||||
|
||||
make test
|
||||
|
||||
__Note:__ There was a bug in node v0.2.0 causing the tests to hang, upgrading
|
||||
to v0.2.1 fixes this.
|
||||
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Contributions to the project are most welcome, so feel free to fork and improve.
|
||||
When submitting a pull request, please run 'make lint' first to ensure
|
||||
we're following a consistent coding style.
|
||||
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var
|
||||
fs = require('fs'),
|
||||
path = require('path');
|
||||
|
||||
// TODO: remove this when https://github.com/joyent/node/pull/1312
|
||||
// lands in core.
|
||||
//
|
||||
// Until then, use console.log from npm (https://gist.github.com/1077544)
|
||||
require('../deps/console.log');
|
||||
|
||||
require.paths.push(process.cwd());
|
||||
var args = process.ARGV.slice(2);
|
||||
|
||||
var files = [];
|
||||
|
||||
var testrunner,
|
||||
config_file,
|
||||
config_param_found = false,
|
||||
output_param_found = false,
|
||||
reporter_file = 'default',
|
||||
reporter_param_found = false,
|
||||
testspec_param_found = false;
|
||||
|
||||
var usage = "Usage: nodeunit [options] testmodule1.js testfolder [...] \n" +
|
||||
"Options:\n\n" +
|
||||
" --config FILE the path to a JSON file with options\n" +
|
||||
" --reporter FILE optional path to a reporter file to customize the output\n" +
|
||||
" --list-reporters list available build-in reporters\n" +
|
||||
" -t name, specify a test to run\n" +
|
||||
" -h, --help display this help and exit\n" +
|
||||
" -v, --version output version information and exit";
|
||||
|
||||
|
||||
// load default options
|
||||
var content = fs.readFileSync(__dirname + '/nodeunit.json', 'utf8');
|
||||
var options = JSON.parse(content);
|
||||
|
||||
// a very basic pseudo --options parser
|
||||
args.forEach(function (arg) {
|
||||
if (arg.slice(0, 9) === "--config=") {
|
||||
config_file = arg.slice(9);
|
||||
} else if (arg === '--config') {
|
||||
config_param_found = true;
|
||||
} else if (config_param_found) {
|
||||
config_file = arg;
|
||||
config_param_found = false;
|
||||
} else if (arg.slice(0, 9) === "--output=") {
|
||||
options.output = arg.slice(9);
|
||||
} else if (arg === '--output') {
|
||||
output_param_found = true;
|
||||
} else if (output_param_found) {
|
||||
options.output = arg;
|
||||
output_param_found = false;
|
||||
} else if (arg.slice(0, 11) === "--reporter=") {
|
||||
reporter_file = arg.slice(11);
|
||||
} else if (arg === '--reporter') {
|
||||
reporter_param_found = true;
|
||||
} else if (reporter_param_found) {
|
||||
reporter_file = arg;
|
||||
reporter_param_found = false;
|
||||
} else if (arg === '-t') {
|
||||
testspec_param_found = true;
|
||||
} else if (testspec_param_found) {
|
||||
options.testspec = arg;
|
||||
testspec_param_found = false;
|
||||
} else if (arg === '--list-reporters') {
|
||||
var reporters = fs.readdirSync(__dirname + '/../lib/reporters');
|
||||
reporters = reporters.filter(function (reporter_file) {
|
||||
return (/\.js$/).test(reporter_file);
|
||||
}).map(function (reporter_file) {
|
||||
return reporter_file.replace(/\.js$/, '');
|
||||
}).filter(function (reporter_file) {
|
||||
return reporter_file !== 'index';
|
||||
});
|
||||
console.log('Build-in reporters: ');
|
||||
reporters.forEach(function (reporter_file) {
|
||||
var reporter = require('../lib/reporters/' + reporter_file);
|
||||
console.log(' * ' + reporter_file + (reporter.info ? ': ' + reporter.info : ''));
|
||||
});
|
||||
process.exit(0);
|
||||
} else if ((arg === '-v') || (arg === '--version')) {
|
||||
var content = fs.readFileSync(__dirname + '/../package.json', 'utf8');
|
||||
var pkg = JSON.parse(content);
|
||||
console.log(pkg.version);
|
||||
process.exit(0);
|
||||
} else if ((arg === '-h') || (arg === '--help')) {
|
||||
console.log(usage);
|
||||
process.exit(0);
|
||||
} else {
|
||||
files.push(arg);
|
||||
}
|
||||
});
|
||||
|
||||
if (files.length === 0) {
|
||||
console.log('Files required.');
|
||||
console.log(usage);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (config_file) {
|
||||
content = fs.readFileSync(config_file, 'utf8');
|
||||
var custom_options = JSON.parse(content);
|
||||
|
||||
for (var option in custom_options) {
|
||||
if (typeof option === 'string') {
|
||||
options[option] = custom_options[option];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var builtin_reporters = require(__dirname + '/../lib/reporters');
|
||||
if (reporter_file in builtin_reporters) {
|
||||
testrunner = builtin_reporters[reporter_file];
|
||||
}
|
||||
else {
|
||||
testrunner = require(reporter_file);
|
||||
}
|
||||
testrunner.run(files, options);
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"error_prefix": "\u001B[31m",
|
||||
"error_suffix": "\u001B[39m",
|
||||
"ok_prefix": "\u001B[32m",
|
||||
"ok_suffix": "\u001B[39m",
|
||||
"bold_prefix": "\u001B[1m",
|
||||
"bold_suffix": "\u001B[22m",
|
||||
"assertion_prefix": "\u001B[35m",
|
||||
"assertion_suffix": "\u001B[39m"
|
||||
}
|
||||
+623
@@ -0,0 +1,623 @@
|
||||
/*global setTimeout: false, console: false */
|
||||
(function () {
|
||||
|
||||
var async = {};
|
||||
|
||||
// global on the server, window in the browser
|
||||
var root = this,
|
||||
previous_async = root.async;
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = async;
|
||||
}
|
||||
else {
|
||||
root.async = async;
|
||||
}
|
||||
|
||||
async.noConflict = function () {
|
||||
root.async = previous_async;
|
||||
return async;
|
||||
};
|
||||
|
||||
//// cross-browser compatiblity functions ////
|
||||
|
||||
var _forEach = function (arr, iterator) {
|
||||
if (arr.forEach) {
|
||||
return arr.forEach(iterator);
|
||||
}
|
||||
for (var i = 0; i < arr.length; i += 1) {
|
||||
iterator(arr[i], i, arr);
|
||||
}
|
||||
};
|
||||
|
||||
var _map = function (arr, iterator) {
|
||||
if (arr.map) {
|
||||
return arr.map(iterator);
|
||||
}
|
||||
var results = [];
|
||||
_forEach(arr, function (x, i, a) {
|
||||
results.push(iterator(x, i, a));
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
var _reduce = function (arr, iterator, memo) {
|
||||
if (arr.reduce) {
|
||||
return arr.reduce(iterator, memo);
|
||||
}
|
||||
_forEach(arr, function (x, i, a) {
|
||||
memo = iterator(memo, x, i, a);
|
||||
});
|
||||
return memo;
|
||||
};
|
||||
|
||||
var _keys = function (obj) {
|
||||
if (Object.keys) {
|
||||
return Object.keys(obj);
|
||||
}
|
||||
var keys = [];
|
||||
for (var k in obj) {
|
||||
if (obj.hasOwnProperty(k)) {
|
||||
keys.push(k);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
|
||||
var _indexOf = function (arr, item) {
|
||||
if (arr.indexOf) {
|
||||
return arr.indexOf(item);
|
||||
}
|
||||
for (var i = 0; i < arr.length; i += 1) {
|
||||
if (arr[i] === item) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
//// exported async module functions ////
|
||||
|
||||
//// nextTick implementation with browser-compatible fallback ////
|
||||
if (typeof process === 'undefined' || !(process.nextTick)) {
|
||||
async.nextTick = function (fn) {
|
||||
setTimeout(fn, 0);
|
||||
};
|
||||
}
|
||||
else {
|
||||
async.nextTick = process.nextTick;
|
||||
}
|
||||
|
||||
async.forEach = function (arr, iterator, callback) {
|
||||
if (!arr.length) {
|
||||
return callback();
|
||||
}
|
||||
var completed = 0;
|
||||
_forEach(arr, function (x) {
|
||||
iterator(x, function (err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
callback = function () {};
|
||||
}
|
||||
else {
|
||||
completed += 1;
|
||||
if (completed === arr.length) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
async.forEachSeries = function (arr, iterator, callback) {
|
||||
if (!arr.length) {
|
||||
return callback();
|
||||
}
|
||||
var completed = 0;
|
||||
var iterate = function () {
|
||||
iterator(arr[completed], function (err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
callback = function () {};
|
||||
}
|
||||
else {
|
||||
completed += 1;
|
||||
if (completed === arr.length) {
|
||||
callback();
|
||||
}
|
||||
else {
|
||||
iterate();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
iterate();
|
||||
};
|
||||
|
||||
|
||||
var doParallel = function (fn) {
|
||||
return function () {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
return fn.apply(null, [async.forEach].concat(args));
|
||||
};
|
||||
};
|
||||
var doSeries = function (fn) {
|
||||
return function () {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
return fn.apply(null, [async.forEachSeries].concat(args));
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
var _asyncMap = function (eachfn, arr, iterator, callback) {
|
||||
var results = [];
|
||||
arr = _map(arr, function (x, i) {
|
||||
return {index: i, value: x};
|
||||
});
|
||||
eachfn(arr, function (x, callback) {
|
||||
iterator(x.value, function (err, v) {
|
||||
results[x.index] = v;
|
||||
callback(err);
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, results);
|
||||
});
|
||||
};
|
||||
async.map = doParallel(_asyncMap);
|
||||
async.mapSeries = doSeries(_asyncMap);
|
||||
|
||||
|
||||
// reduce only has a series version, as doing reduce in parallel won't
|
||||
// work in many situations.
|
||||
async.reduce = function (arr, memo, iterator, callback) {
|
||||
async.forEachSeries(arr, function (x, callback) {
|
||||
iterator(memo, x, function (err, v) {
|
||||
memo = v;
|
||||
callback(err);
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, memo);
|
||||
});
|
||||
};
|
||||
// inject alias
|
||||
async.inject = async.reduce;
|
||||
// foldl alias
|
||||
async.foldl = async.reduce;
|
||||
|
||||
async.reduceRight = function (arr, memo, iterator, callback) {
|
||||
var reversed = _map(arr, function (x) {
|
||||
return x;
|
||||
}).reverse();
|
||||
async.reduce(reversed, memo, iterator, callback);
|
||||
};
|
||||
// foldr alias
|
||||
async.foldr = async.reduceRight;
|
||||
|
||||
var _filter = function (eachfn, arr, iterator, callback) {
|
||||
var results = [];
|
||||
arr = _map(arr, function (x, i) {
|
||||
return {index: i, value: x};
|
||||
});
|
||||
eachfn(arr, function (x, callback) {
|
||||
iterator(x.value, function (v) {
|
||||
if (v) {
|
||||
results.push(x);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}, function (err) {
|
||||
callback(_map(results.sort(function (a, b) {
|
||||
return a.index - b.index;
|
||||
}), function (x) {
|
||||
return x.value;
|
||||
}));
|
||||
});
|
||||
};
|
||||
async.filter = doParallel(_filter);
|
||||
async.filterSeries = doSeries(_filter);
|
||||
// select alias
|
||||
async.select = async.filter;
|
||||
async.selectSeries = async.filterSeries;
|
||||
|
||||
var _reject = function (eachfn, arr, iterator, callback) {
|
||||
var results = [];
|
||||
arr = _map(arr, function (x, i) {
|
||||
return {index: i, value: x};
|
||||
});
|
||||
eachfn(arr, function (x, callback) {
|
||||
iterator(x.value, function (v) {
|
||||
if (!v) {
|
||||
results.push(x);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}, function (err) {
|
||||
callback(_map(results.sort(function (a, b) {
|
||||
return a.index - b.index;
|
||||
}), function (x) {
|
||||
return x.value;
|
||||
}));
|
||||
});
|
||||
};
|
||||
async.reject = doParallel(_reject);
|
||||
async.rejectSeries = doSeries(_reject);
|
||||
|
||||
var _detect = function (eachfn, arr, iterator, main_callback) {
|
||||
eachfn(arr, function (x, callback) {
|
||||
iterator(x, function (result) {
|
||||
if (result) {
|
||||
main_callback(x);
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}, function (err) {
|
||||
main_callback();
|
||||
});
|
||||
};
|
||||
async.detect = doParallel(_detect);
|
||||
async.detectSeries = doSeries(_detect);
|
||||
|
||||
async.some = function (arr, iterator, main_callback) {
|
||||
async.forEach(arr, function (x, callback) {
|
||||
iterator(x, function (v) {
|
||||
if (v) {
|
||||
main_callback(true);
|
||||
main_callback = function () {};
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}, function (err) {
|
||||
main_callback(false);
|
||||
});
|
||||
};
|
||||
// any alias
|
||||
async.any = async.some;
|
||||
|
||||
async.every = function (arr, iterator, main_callback) {
|
||||
async.forEach(arr, function (x, callback) {
|
||||
iterator(x, function (v) {
|
||||
if (!v) {
|
||||
main_callback(false);
|
||||
main_callback = function () {};
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}, function (err) {
|
||||
main_callback(true);
|
||||
});
|
||||
};
|
||||
// all alias
|
||||
async.all = async.every;
|
||||
|
||||
async.sortBy = function (arr, iterator, callback) {
|
||||
async.map(arr, function (x, callback) {
|
||||
iterator(x, function (err, criteria) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
}
|
||||
else {
|
||||
callback(null, {value: x, criteria: criteria});
|
||||
}
|
||||
});
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
else {
|
||||
var fn = function (left, right) {
|
||||
var a = left.criteria, b = right.criteria;
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
};
|
||||
callback(null, _map(results.sort(fn), function (x) {
|
||||
return x.value;
|
||||
}));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async.auto = function (tasks, callback) {
|
||||
callback = callback || function () {};
|
||||
var keys = _keys(tasks);
|
||||
if (!keys.length) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
var completed = [];
|
||||
|
||||
var listeners = [];
|
||||
var addListener = function (fn) {
|
||||
listeners.unshift(fn);
|
||||
};
|
||||
var removeListener = function (fn) {
|
||||
for (var i = 0; i < listeners.length; i += 1) {
|
||||
if (listeners[i] === fn) {
|
||||
listeners.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
var taskComplete = function () {
|
||||
_forEach(listeners, function (fn) {
|
||||
fn();
|
||||
});
|
||||
};
|
||||
|
||||
addListener(function () {
|
||||
if (completed.length === keys.length) {
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
|
||||
_forEach(keys, function (k) {
|
||||
var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k];
|
||||
var taskCallback = function (err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
// stop subsequent errors hitting callback multiple times
|
||||
callback = function () {};
|
||||
}
|
||||
else {
|
||||
completed.push(k);
|
||||
taskComplete();
|
||||
}
|
||||
};
|
||||
var requires = task.slice(0, Math.abs(task.length - 1)) || [];
|
||||
var ready = function () {
|
||||
return _reduce(requires, function (a, x) {
|
||||
return (a && _indexOf(completed, x) !== -1);
|
||||
}, true);
|
||||
};
|
||||
if (ready()) {
|
||||
task[task.length - 1](taskCallback);
|
||||
}
|
||||
else {
|
||||
var listener = function () {
|
||||
if (ready()) {
|
||||
removeListener(listener);
|
||||
task[task.length - 1](taskCallback);
|
||||
}
|
||||
};
|
||||
addListener(listener);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async.waterfall = function (tasks, callback) {
|
||||
if (!tasks.length) {
|
||||
return callback();
|
||||
}
|
||||
callback = callback || function () {};
|
||||
var wrapIterator = function (iterator) {
|
||||
return function (err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
callback = function () {};
|
||||
}
|
||||
else {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
var next = iterator.next();
|
||||
if (next) {
|
||||
args.push(wrapIterator(next));
|
||||
}
|
||||
else {
|
||||
args.push(callback);
|
||||
}
|
||||
async.nextTick(function () {
|
||||
iterator.apply(null, args);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
wrapIterator(async.iterator(tasks))();
|
||||
};
|
||||
|
||||
async.parallel = function (tasks, callback) {
|
||||
callback = callback || function () {};
|
||||
if (tasks.constructor === Array) {
|
||||
async.map(tasks, function (fn, callback) {
|
||||
if (fn) {
|
||||
fn(function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (args.length <= 1) {
|
||||
args = args[0];
|
||||
}
|
||||
callback.call(null, err, args || null);
|
||||
});
|
||||
}
|
||||
}, callback);
|
||||
}
|
||||
else {
|
||||
var results = {};
|
||||
async.forEach(_keys(tasks), function (k, callback) {
|
||||
tasks[k](function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (args.length <= 1) {
|
||||
args = args[0];
|
||||
}
|
||||
results[k] = args;
|
||||
callback(err);
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, results);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
async.series = function (tasks, callback) {
|
||||
callback = callback || function () {};
|
||||
if (tasks.constructor === Array) {
|
||||
async.mapSeries(tasks, function (fn, callback) {
|
||||
if (fn) {
|
||||
fn(function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (args.length <= 1) {
|
||||
args = args[0];
|
||||
}
|
||||
callback.call(null, err, args || null);
|
||||
});
|
||||
}
|
||||
}, callback);
|
||||
}
|
||||
else {
|
||||
var results = {};
|
||||
async.forEachSeries(_keys(tasks), function (k, callback) {
|
||||
tasks[k](function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (args.length <= 1) {
|
||||
args = args[0];
|
||||
}
|
||||
results[k] = args;
|
||||
callback(err);
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, results);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
async.iterator = function (tasks) {
|
||||
var makeCallback = function (index) {
|
||||
var fn = function () {
|
||||
if (tasks.length) {
|
||||
tasks[index].apply(null, arguments);
|
||||
}
|
||||
return fn.next();
|
||||
};
|
||||
fn.next = function () {
|
||||
return (index < tasks.length - 1) ? makeCallback(index + 1): null;
|
||||
};
|
||||
return fn;
|
||||
};
|
||||
return makeCallback(0);
|
||||
};
|
||||
|
||||
async.apply = function (fn) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
return function () {
|
||||
return fn.apply(
|
||||
null, args.concat(Array.prototype.slice.call(arguments))
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
var _concat = function (eachfn, arr, fn, callback) {
|
||||
var r = [];
|
||||
eachfn(arr, function (x, cb) {
|
||||
fn(x, function (err, y) {
|
||||
r = r.concat(y || []);
|
||||
cb(err);
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, r);
|
||||
});
|
||||
};
|
||||
async.concat = doParallel(_concat);
|
||||
async.concatSeries = doSeries(_concat);
|
||||
|
||||
async.whilst = function (test, iterator, callback) {
|
||||
if (test()) {
|
||||
iterator(function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
async.whilst(test, iterator, callback);
|
||||
});
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
async.until = function (test, iterator, callback) {
|
||||
if (!test()) {
|
||||
iterator(function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
async.until(test, iterator, callback);
|
||||
});
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
async.queue = function (worker, concurrency) {
|
||||
var workers = 0;
|
||||
var tasks = [];
|
||||
var q = {
|
||||
concurrency: concurrency,
|
||||
push: function (data, callback) {
|
||||
tasks.push({data: data, callback: callback});
|
||||
async.nextTick(q.process);
|
||||
},
|
||||
process: function () {
|
||||
if (workers < q.concurrency && tasks.length) {
|
||||
var task = tasks.splice(0, 1)[0];
|
||||
workers += 1;
|
||||
worker(task.data, function () {
|
||||
workers -= 1;
|
||||
if (task.callback) {
|
||||
task.callback.apply(task, arguments);
|
||||
}
|
||||
q.process();
|
||||
});
|
||||
}
|
||||
},
|
||||
length: function () {
|
||||
return tasks.length;
|
||||
}
|
||||
};
|
||||
return q;
|
||||
};
|
||||
|
||||
var _console_fn = function (name) {
|
||||
return function (fn) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
fn.apply(null, args.concat([function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (typeof console !== 'undefined') {
|
||||
if (err) {
|
||||
if (console.error) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
else if (console[name]) {
|
||||
_forEach(args, function (x) {
|
||||
console[name](x);
|
||||
});
|
||||
}
|
||||
}
|
||||
}]));
|
||||
};
|
||||
};
|
||||
async.log = _console_fn('log');
|
||||
async.dir = _console_fn('dir');
|
||||
/*async.info = _console_fn('info');
|
||||
async.warn = _console_fn('warn');
|
||||
async.error = _console_fn('error');*/
|
||||
|
||||
async.memoize = function (fn, hasher) {
|
||||
var memo = {};
|
||||
hasher = hasher || function (x) {
|
||||
return x;
|
||||
};
|
||||
return function () {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var callback = args.pop();
|
||||
var key = hasher.apply(null, args);
|
||||
if (key in memo) {
|
||||
callback.apply(null, memo[key]);
|
||||
}
|
||||
else {
|
||||
fn.apply(null, args.concat([function () {
|
||||
memo[key] = arguments;
|
||||
callback.apply(null, arguments);
|
||||
}]));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}());
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
A console.log that won't leave you hanging when node exits
|
||||
90% of this file was ripped from node.js
|
||||
|
||||
License: see: https://github.com/joyent/node/blob/master/lib/console.js
|
||||
*/
|
||||
|
||||
// console object
|
||||
var formatRegExp = /%[sdj]/g;
|
||||
function format(f) {
|
||||
var util = require('util');
|
||||
|
||||
if (typeof f !== 'string') {
|
||||
var objects = [];
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
objects.push(util.inspect(arguments[i]));
|
||||
}
|
||||
return objects.join(' ');
|
||||
}
|
||||
|
||||
|
||||
var i = 1;
|
||||
var args = arguments;
|
||||
var str = String(f).replace(formatRegExp, function(x) {
|
||||
switch (x) {
|
||||
case '%s': return String(args[i++]);
|
||||
case '%d': return Number(args[i++]);
|
||||
case '%j': return JSON.stringify(args[i++]);
|
||||
default:
|
||||
return x;
|
||||
}
|
||||
});
|
||||
for (var len = args.length, x = args[i]; i < len; x = args[++i]) {
|
||||
if (x === null || typeof x !== 'object') {
|
||||
str += ' ' + x;
|
||||
} else {
|
||||
str += ' ' + util.inspect(x);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
console.log = function() {
|
||||
var res = process.stdout.write(format.apply(this, arguments) + '\n');
|
||||
|
||||
// this is the first time stdout got backed up
|
||||
if (!res && !process.stdout.pendingWrite) {
|
||||
process.stdout.pendingWrite = true;
|
||||
|
||||
// magic sauce: keep node alive until stdout has flushed
|
||||
process.stdout.once('drain', function () {
|
||||
process.stdout.draining = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
|
||||
/*!
|
||||
* EJS
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var sys = require('sys');
|
||||
|
||||
/**
|
||||
* Library version.
|
||||
*/
|
||||
|
||||
exports.version = '0.0.3';
|
||||
|
||||
/**
|
||||
* Intermediate js cache.
|
||||
*
|
||||
* @type Object
|
||||
*/
|
||||
|
||||
var cache = {};
|
||||
|
||||
/**
|
||||
* Clear intermediate js cache.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.clearCache = function(){
|
||||
cache = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Escape the given string of `html`.
|
||||
*
|
||||
* @param {String} html
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function escape(html){
|
||||
return String(html)
|
||||
.replace(/&(?!\w+;)/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given `str` of ejs, returning the function body.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
var parse = exports.parse = function(str){
|
||||
return 'var buf = [];\n'
|
||||
+ "with (locals) {\nbuf.push('"
|
||||
+ String(str)
|
||||
.replace(/[\r\t]/g, " ")
|
||||
.replace(/\n/g, "\\n")
|
||||
.split("<%").join("\t")
|
||||
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
|
||||
.replace(/\t=(.*?)%>/g, "', escape($1) ,'")
|
||||
.replace(/\t-(.*?)%>/g, "', $1 ,'")
|
||||
.split("\t").join("');")
|
||||
.split("%>").join("buf.push('")
|
||||
.split("\r").join("\\'")
|
||||
+ "');\n}\nreturn buf.join('');";
|
||||
};
|
||||
|
||||
/**
|
||||
* Compile the given `str` of ejs into a `Function`.
|
||||
*
|
||||
* @param {String} str
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
var compile = exports.compile = function(str, options){
|
||||
if (options.debug) sys.puts(parse(str));
|
||||
return new Function('locals, escape', parse(str));
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the given `str` of ejs.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `locals` Local variables object
|
||||
* - `cache` Compiled functions are cached, requires `filename`
|
||||
* - `filename` Used by `cache` to key caches
|
||||
* - `context|scope` Function execution context
|
||||
* - `debug` Output generated function body
|
||||
*
|
||||
* @param {String} str
|
||||
* @param {Object} options
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.render = function(str, options){
|
||||
var fn,
|
||||
options = options || {};
|
||||
if (options.cache) {
|
||||
if (options.filename) {
|
||||
fn = cache[options.filename] = compile(str, options);
|
||||
} else {
|
||||
throw new Error('"cache" option requires "filename".');
|
||||
}
|
||||
} else {
|
||||
fn = compile(str, options);
|
||||
}
|
||||
return fn.call(
|
||||
options.context || options.scope,
|
||||
options.locals || {},
|
||||
escape);
|
||||
};
|
||||
+483
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
http://www.JSON.org/json2.js
|
||||
2010-11-17
|
||||
|
||||
Public Domain.
|
||||
|
||||
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||||
|
||||
See http://www.JSON.org/js.html
|
||||
|
||||
|
||||
This code should be minified before deployment.
|
||||
See http://javascript.crockford.com/jsmin.html
|
||||
|
||||
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
|
||||
NOT CONTROL.
|
||||
|
||||
|
||||
This file creates a global JSON object containing two methods: stringify
|
||||
and parse.
|
||||
|
||||
JSON.stringify(value, replacer, space)
|
||||
value any JavaScript value, usually an object or array.
|
||||
|
||||
replacer an optional parameter that determines how object
|
||||
values are stringified for objects. It can be a
|
||||
function or an array of strings.
|
||||
|
||||
space an optional parameter that specifies the indentation
|
||||
of nested structures. If it is omitted, the text will
|
||||
be packed without extra whitespace. If it is a number,
|
||||
it will specify the number of spaces to indent at each
|
||||
level. If it is a string (such as '\t' or ' '),
|
||||
it contains the characters used to indent at each level.
|
||||
|
||||
This method produces a JSON text from a JavaScript value.
|
||||
|
||||
When an object value is found, if the object contains a toJSON
|
||||
method, its toJSON method will be called and the result will be
|
||||
stringified. A toJSON method does not serialize: it returns the
|
||||
value represented by the name/value pair that should be serialized,
|
||||
or undefined if nothing should be serialized. The toJSON method
|
||||
will be passed the key associated with the value, and this will be
|
||||
bound to the value
|
||||
|
||||
For example, this would serialize Dates as ISO strings.
|
||||
|
||||
Date.prototype.toJSON = function (key) {
|
||||
function f(n) {
|
||||
// Format integers to have at least two digits.
|
||||
return n < 10 ? '0' + n : n;
|
||||
}
|
||||
|
||||
return this.getUTCFullYear() + '-' +
|
||||
f(this.getUTCMonth() + 1) + '-' +
|
||||
f(this.getUTCDate()) + 'T' +
|
||||
f(this.getUTCHours()) + ':' +
|
||||
f(this.getUTCMinutes()) + ':' +
|
||||
f(this.getUTCSeconds()) + 'Z';
|
||||
};
|
||||
|
||||
You can provide an optional replacer method. It will be passed the
|
||||
key and value of each member, with this bound to the containing
|
||||
object. The value that is returned from your method will be
|
||||
serialized. If your method returns undefined, then the member will
|
||||
be excluded from the serialization.
|
||||
|
||||
If the replacer parameter is an array of strings, then it will be
|
||||
used to select the members to be serialized. It filters the results
|
||||
such that only members with keys listed in the replacer array are
|
||||
stringified.
|
||||
|
||||
Values that do not have JSON representations, such as undefined or
|
||||
functions, will not be serialized. Such values in objects will be
|
||||
dropped; in arrays they will be replaced with null. You can use
|
||||
a replacer function to replace those with JSON values.
|
||||
JSON.stringify(undefined) returns undefined.
|
||||
|
||||
The optional space parameter produces a stringification of the
|
||||
value that is filled with line breaks and indentation to make it
|
||||
easier to read.
|
||||
|
||||
If the space parameter is a non-empty string, then that string will
|
||||
be used for indentation. If the space parameter is a number, then
|
||||
the indentation will be that many spaces.
|
||||
|
||||
Example:
|
||||
|
||||
text = JSON.stringify(['e', {pluribus: 'unum'}]);
|
||||
// text is '["e",{"pluribus":"unum"}]'
|
||||
|
||||
|
||||
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
|
||||
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
|
||||
|
||||
text = JSON.stringify([new Date()], function (key, value) {
|
||||
return this[key] instanceof Date ?
|
||||
'Date(' + this[key] + ')' : value;
|
||||
});
|
||||
// text is '["Date(---current time---)"]'
|
||||
|
||||
|
||||
JSON.parse(text, reviver)
|
||||
This method parses a JSON text to produce an object or array.
|
||||
It can throw a SyntaxError exception.
|
||||
|
||||
The optional reviver parameter is a function that can filter and
|
||||
transform the results. It receives each of the keys and values,
|
||||
and its return value is used instead of the original value.
|
||||
If it returns what it received, then the structure is not modified.
|
||||
If it returns undefined then the member is deleted.
|
||||
|
||||
Example:
|
||||
|
||||
// Parse the text. Values that look like ISO date strings will
|
||||
// be converted to Date objects.
|
||||
|
||||
myData = JSON.parse(text, function (key, value) {
|
||||
var a;
|
||||
if (typeof value === 'string') {
|
||||
a =
|
||||
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
|
||||
if (a) {
|
||||
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
|
||||
+a[5], +a[6]));
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
|
||||
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
|
||||
var d;
|
||||
if (typeof value === 'string' &&
|
||||
value.slice(0, 5) === 'Date(' &&
|
||||
value.slice(-1) === ')') {
|
||||
d = new Date(value.slice(5, -1));
|
||||
if (d) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
|
||||
|
||||
This is a reference implementation. You are free to copy, modify, or
|
||||
redistribute.
|
||||
*/
|
||||
|
||||
/*jslint evil: true, strict: false, regexp: false */
|
||||
|
||||
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
|
||||
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
|
||||
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
|
||||
lastIndex, length, parse, prototype, push, replace, slice, stringify,
|
||||
test, toJSON, toString, valueOf
|
||||
*/
|
||||
|
||||
|
||||
// Create a JSON object only if one does not already exist. We create the
|
||||
// methods in a closure to avoid creating global variables.
|
||||
|
||||
if (!this.JSON) {
|
||||
this.JSON = {};
|
||||
}
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function f(n) {
|
||||
// Format integers to have at least two digits.
|
||||
return n < 10 ? '0' + n : n;
|
||||
}
|
||||
|
||||
if (typeof Date.prototype.toJSON !== 'function') {
|
||||
|
||||
Date.prototype.toJSON = function (key) {
|
||||
|
||||
return isFinite(this.valueOf()) ?
|
||||
this.getUTCFullYear() + '-' +
|
||||
f(this.getUTCMonth() + 1) + '-' +
|
||||
f(this.getUTCDate()) + 'T' +
|
||||
f(this.getUTCHours()) + ':' +
|
||||
f(this.getUTCMinutes()) + ':' +
|
||||
f(this.getUTCSeconds()) + 'Z' : null;
|
||||
};
|
||||
|
||||
String.prototype.toJSON =
|
||||
Number.prototype.toJSON =
|
||||
Boolean.prototype.toJSON = function (key) {
|
||||
return this.valueOf();
|
||||
};
|
||||
}
|
||||
|
||||
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||
gap,
|
||||
indent,
|
||||
meta = { // table of character substitutions
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
},
|
||||
rep;
|
||||
|
||||
|
||||
function quote(string) {
|
||||
|
||||
// If the string contains no control characters, no quote characters, and no
|
||||
// backslash characters, then we can safely slap some quotes around it.
|
||||
// Otherwise we must also replace the offending characters with safe escape
|
||||
// sequences.
|
||||
|
||||
escapable.lastIndex = 0;
|
||||
return escapable.test(string) ?
|
||||
'"' + string.replace(escapable, function (a) {
|
||||
var c = meta[a];
|
||||
return typeof c === 'string' ? c :
|
||||
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
}) + '"' :
|
||||
'"' + string + '"';
|
||||
}
|
||||
|
||||
|
||||
function str(key, holder) {
|
||||
|
||||
// Produce a string from holder[key].
|
||||
|
||||
var i, // The loop counter.
|
||||
k, // The member key.
|
||||
v, // The member value.
|
||||
length,
|
||||
mind = gap,
|
||||
partial,
|
||||
value = holder[key];
|
||||
|
||||
// If the value has a toJSON method, call it to obtain a replacement value.
|
||||
|
||||
if (value && typeof value === 'object' &&
|
||||
typeof value.toJSON === 'function') {
|
||||
value = value.toJSON(key);
|
||||
}
|
||||
|
||||
// If we were called with a replacer function, then call the replacer to
|
||||
// obtain a replacement value.
|
||||
|
||||
if (typeof rep === 'function') {
|
||||
value = rep.call(holder, key, value);
|
||||
}
|
||||
|
||||
// What happens next depends on the value's type.
|
||||
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
return quote(value);
|
||||
|
||||
case 'number':
|
||||
|
||||
// JSON numbers must be finite. Encode non-finite numbers as null.
|
||||
|
||||
return isFinite(value) ? String(value) : 'null';
|
||||
|
||||
case 'boolean':
|
||||
case 'null':
|
||||
|
||||
// If the value is a boolean or null, convert it to a string. Note:
|
||||
// typeof null does not produce 'null'. The case is included here in
|
||||
// the remote chance that this gets fixed someday.
|
||||
|
||||
return String(value);
|
||||
|
||||
// If the type is 'object', we might be dealing with an object or an array or
|
||||
// null.
|
||||
|
||||
case 'object':
|
||||
|
||||
// Due to a specification blunder in ECMAScript, typeof null is 'object',
|
||||
// so watch out for that case.
|
||||
|
||||
if (!value) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
// Make an array to hold the partial results of stringifying this object value.
|
||||
|
||||
gap += indent;
|
||||
partial = [];
|
||||
|
||||
// Is the value an array?
|
||||
|
||||
if (Object.prototype.toString.apply(value) === '[object Array]') {
|
||||
|
||||
// The value is an array. Stringify every element. Use null as a placeholder
|
||||
// for non-JSON values.
|
||||
|
||||
length = value.length;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
partial[i] = str(i, value) || 'null';
|
||||
}
|
||||
|
||||
// Join all of the elements together, separated with commas, and wrap them in
|
||||
// brackets.
|
||||
|
||||
v = partial.length === 0 ? '[]' :
|
||||
gap ? '[\n' + gap +
|
||||
partial.join(',\n' + gap) + '\n' +
|
||||
mind + ']' :
|
||||
'[' + partial.join(',') + ']';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
|
||||
// If the replacer is an array, use it to select the members to be stringified.
|
||||
|
||||
if (rep && typeof rep === 'object') {
|
||||
length = rep.length;
|
||||
for (i = 0; i < length; i += 1) {
|
||||
k = rep[i];
|
||||
if (typeof k === 'string') {
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// Otherwise, iterate through all of the keys in the object.
|
||||
|
||||
for (k in value) {
|
||||
if (Object.hasOwnProperty.call(value, k)) {
|
||||
v = str(k, value);
|
||||
if (v) {
|
||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Join all of the member texts together, separated with commas,
|
||||
// and wrap them in braces.
|
||||
|
||||
v = partial.length === 0 ? '{}' :
|
||||
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
|
||||
mind + '}' : '{' + partial.join(',') + '}';
|
||||
gap = mind;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
// If the JSON object does not yet have a stringify method, give it one.
|
||||
|
||||
if (typeof JSON.stringify !== 'function') {
|
||||
JSON.stringify = function (value, replacer, space) {
|
||||
|
||||
// The stringify method takes a value and an optional replacer, and an optional
|
||||
// space parameter, and returns a JSON text. The replacer can be a function
|
||||
// that can replace values, or an array of strings that will select the keys.
|
||||
// A default replacer method can be provided. Use of the space parameter can
|
||||
// produce text that is more easily readable.
|
||||
|
||||
var i;
|
||||
gap = '';
|
||||
indent = '';
|
||||
|
||||
// If the space parameter is a number, make an indent string containing that
|
||||
// many spaces.
|
||||
|
||||
if (typeof space === 'number') {
|
||||
for (i = 0; i < space; i += 1) {
|
||||
indent += ' ';
|
||||
}
|
||||
|
||||
// If the space parameter is a string, it will be used as the indent string.
|
||||
|
||||
} else if (typeof space === 'string') {
|
||||
indent = space;
|
||||
}
|
||||
|
||||
// If there is a replacer, it must be a function or an array.
|
||||
// Otherwise, throw an error.
|
||||
|
||||
rep = replacer;
|
||||
if (replacer && typeof replacer !== 'function' &&
|
||||
(typeof replacer !== 'object' ||
|
||||
typeof replacer.length !== 'number')) {
|
||||
throw new Error('JSON.stringify');
|
||||
}
|
||||
|
||||
// Make a fake root object containing our value under the key of ''.
|
||||
// Return the result of stringifying the value.
|
||||
|
||||
return str('', {'': value});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// If the JSON object does not yet have a parse method, give it one.
|
||||
|
||||
if (typeof JSON.parse !== 'function') {
|
||||
JSON.parse = function (text, reviver) {
|
||||
|
||||
// The parse method takes a text and an optional reviver function, and returns
|
||||
// a JavaScript value if the text is a valid JSON text.
|
||||
|
||||
var j;
|
||||
|
||||
function walk(holder, key) {
|
||||
|
||||
// The walk method is used to recursively walk the resulting structure so
|
||||
// that modifications can be made.
|
||||
|
||||
var k, v, value = holder[key];
|
||||
if (value && typeof value === 'object') {
|
||||
for (k in value) {
|
||||
if (Object.hasOwnProperty.call(value, k)) {
|
||||
v = walk(value, k);
|
||||
if (v !== undefined) {
|
||||
value[k] = v;
|
||||
} else {
|
||||
delete value[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return reviver.call(holder, key, value);
|
||||
}
|
||||
|
||||
|
||||
// Parsing happens in four stages. In the first stage, we replace certain
|
||||
// Unicode characters with escape sequences. JavaScript handles many characters
|
||||
// incorrectly, either silently deleting them, or treating them as line endings.
|
||||
|
||||
text = String(text);
|
||||
cx.lastIndex = 0;
|
||||
if (cx.test(text)) {
|
||||
text = text.replace(cx, function (a) {
|
||||
return '\\u' +
|
||||
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||
});
|
||||
}
|
||||
|
||||
// In the second stage, we run the text against regular expressions that look
|
||||
// for non-JSON patterns. We are especially concerned with '()' and 'new'
|
||||
// because they can cause invocation, and '=' because it can cause mutation.
|
||||
// But just to be safe, we want to reject all unexpected forms.
|
||||
|
||||
// We split the second stage into 4 regexp operations in order to work around
|
||||
// crippling inefficiencies in IE's and Safari's regexp engines. First we
|
||||
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
|
||||
// replace all simple value tokens with ']' characters. Third, we delete all
|
||||
// open brackets that follow a colon or comma or that begin the text. Finally,
|
||||
// we look to see that the remaining characters are only whitespace or ']' or
|
||||
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
|
||||
|
||||
if (/^[\],:{}\s]*$/
|
||||
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
|
||||
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
|
||||
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
||||
|
||||
// In the third stage we use the eval function to compile the text into a
|
||||
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
|
||||
// in JavaScript: it can begin a block or an object literal. We wrap the text
|
||||
// in parens to eliminate the ambiguity.
|
||||
|
||||
j = eval('(' + text + ')');
|
||||
|
||||
// In the optional fourth stage, we recursively walk the new structure, passing
|
||||
// each name/value pair to a reviver function for possible transformation.
|
||||
|
||||
return typeof reviver === 'function' ?
|
||||
walk({'': j}, '') : j;
|
||||
}
|
||||
|
||||
// If the text is not JSON parseable, then a SyntaxError is thrown.
|
||||
|
||||
throw new SyntaxError('JSON.parse');
|
||||
};
|
||||
}
|
||||
}());
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
nodeunit(1) -- simple node.js unit testing tool
|
||||
===============================================
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
nodeunit [options] <file-or-directory> [<file-or-directory> ...]
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
Nodeunit is a simple unit testing tool based on the node.js assert module.
|
||||
|
||||
* Simple to use
|
||||
* Just export the tests from a module
|
||||
* Helps you avoid common pitfalls when testing asynchronous code
|
||||
* Easy to add test cases with setUp and tearDown functions if you wish
|
||||
* Allows the use of mocks and stubs
|
||||
|
||||
## OPTIONS
|
||||
|
||||
__--config FILE__:
|
||||
Load config options from a JSON file, allows the customisation
|
||||
of color schemes for the default test reporter etc.
|
||||
See bin/nodeunit.json for current available options.
|
||||
|
||||
__--reporter FILE__:
|
||||
You can set the test reporter to a custom module or on of the modules
|
||||
in nodeunit/lib/reporters, when omitted, the default test runner is used.
|
||||
|
||||
__--list-reporters__:
|
||||
List available build-in reporters.
|
||||
|
||||
__-h__, __--help__:
|
||||
Display the help and exit.
|
||||
|
||||
__-v__, __--version__:
|
||||
Output version information and exit.
|
||||
|
||||
__<file-or-directory>__:
|
||||
You can run nodeunit on specific files or on all *\*.js* files inside
|
||||
a directory.
|
||||
|
||||
## AUTHORS
|
||||
|
||||
Written by Caolan McMahon and other nodeunit contributors.
|
||||
Contributors list: <http://github.com/caolan/nodeunit/contributors>.
|
||||
|
||||
## REPORTING BUGS
|
||||
|
||||
Report nodeunit bugs to <http://github.com/caolan/nodeunit/issues>.
|
||||
|
||||
## COPYRIGHT
|
||||
|
||||
Copyright © 2010 Caolan McMahon.
|
||||
Nodeunit has been released under the MIT license:
|
||||
<http://github.com/caolan/nodeunit/raw/master/LICENSE>.
|
||||
|
||||
## SEE ALSO
|
||||
|
||||
node(1)
|
||||
|
||||
+1757
File diff suppressed because it is too large
Load Diff
+12
@@ -0,0 +1,12 @@
|
||||
this.suite1 = {
|
||||
'test one': function (test) {
|
||||
test.ok(true, 'everythings ok');
|
||||
setTimeout(function () {
|
||||
test.done();
|
||||
}, 10);
|
||||
},
|
||||
'apples and oranges': function (test) {
|
||||
test.equal('apples', 'oranges', 'comparing apples and oranges');
|
||||
test.done();
|
||||
}
|
||||
};
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
this.suite2 = {
|
||||
'another test': function (test) {
|
||||
setTimeout(function () {
|
||||
// lots of assertions
|
||||
test.ok(true, 'everythings ok');
|
||||
test.ok(true, 'everythings ok');
|
||||
test.ok(true, 'everythings ok');
|
||||
test.ok(true, 'everythings ok');
|
||||
test.ok(true, 'everythings ok');
|
||||
test.done();
|
||||
}, 10);
|
||||
}
|
||||
};
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Example tests</title>
|
||||
<script src="nodeunit.js"></script>
|
||||
<script src="suite1.js"></script>
|
||||
<script src="suite2.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
nodeunit.run({
|
||||
'suite1': suite1,
|
||||
'suite2': suite2
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
+3
@@ -0,0 +1,3 @@
|
||||
// This file is just added for convenience so this repository can be
|
||||
// directly checked out into a project's deps folder
|
||||
module.exports = require('./lib/nodeunit');
|
||||
+316
@@ -0,0 +1,316 @@
|
||||
/**
|
||||
* This file is based on the node.js assert module, but with some small
|
||||
* changes for browser-compatibility
|
||||
* THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Added for browser compatibility
|
||||
*/
|
||||
|
||||
var _keys = function(obj){
|
||||
if(Object.keys) return Object.keys(obj);
|
||||
var keys = [];
|
||||
for(var k in obj){
|
||||
if(obj.hasOwnProperty(k)) keys.push(k);
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
|
||||
//
|
||||
// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
|
||||
//
|
||||
// Originally from narwhal.js (http://narwhaljs.org)
|
||||
// Copyright (c) 2009 Thomas Robinson <280north.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the 'Software'), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
var pSlice = Array.prototype.slice;
|
||||
|
||||
// 1. The assert module provides functions that throw
|
||||
// AssertionError's when particular conditions are not met. The
|
||||
// assert module must conform to the following interface.
|
||||
|
||||
var assert = exports;
|
||||
|
||||
// 2. The AssertionError is defined in assert.
|
||||
// new assert.AssertionError({message: message, actual: actual, expected: expected})
|
||||
|
||||
assert.AssertionError = function AssertionError (options) {
|
||||
this.name = "AssertionError";
|
||||
this.message = options.message;
|
||||
this.actual = options.actual;
|
||||
this.expected = options.expected;
|
||||
this.operator = options.operator;
|
||||
var stackStartFunction = options.stackStartFunction || fail;
|
||||
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, stackStartFunction);
|
||||
}
|
||||
};
|
||||
// code from util.inherits in node
|
||||
assert.AssertionError.super_ = Error;
|
||||
|
||||
|
||||
// EDITED FOR BROWSER COMPATIBILITY: replaced Object.create call
|
||||
// TODO: test what effect this may have
|
||||
var ctor = function () { this.constructor = assert.AssertionError; };
|
||||
ctor.prototype = Error.prototype;
|
||||
assert.AssertionError.prototype = new ctor();
|
||||
|
||||
|
||||
assert.AssertionError.prototype.toString = function() {
|
||||
if (this.message) {
|
||||
return [this.name+":", this.message].join(' ');
|
||||
} else {
|
||||
return [ this.name+":"
|
||||
, JSON.stringify(this.expected )
|
||||
, this.operator
|
||||
, JSON.stringify(this.actual)
|
||||
].join(" ");
|
||||
}
|
||||
};
|
||||
|
||||
// assert.AssertionError instanceof Error
|
||||
|
||||
assert.AssertionError.__proto__ = Error.prototype;
|
||||
|
||||
// At present only the three keys mentioned above are used and
|
||||
// understood by the spec. Implementations or sub modules can pass
|
||||
// other keys to the AssertionError's constructor - they will be
|
||||
// ignored.
|
||||
|
||||
// 3. All of the following functions must throw an AssertionError
|
||||
// when a corresponding condition is not met, with a message that
|
||||
// may be undefined if not provided. All assertion methods provide
|
||||
// both the actual and expected values to the assertion error for
|
||||
// display purposes.
|
||||
|
||||
function fail(actual, expected, message, operator, stackStartFunction) {
|
||||
throw new assert.AssertionError({
|
||||
message: message,
|
||||
actual: actual,
|
||||
expected: expected,
|
||||
operator: operator,
|
||||
stackStartFunction: stackStartFunction
|
||||
});
|
||||
}
|
||||
|
||||
// EXTENSION! allows for well behaved errors defined elsewhere.
|
||||
assert.fail = fail;
|
||||
|
||||
// 4. Pure assertion tests whether a value is truthy, as determined
|
||||
// by !!guard.
|
||||
// assert.ok(guard, message_opt);
|
||||
// This statement is equivalent to assert.equal(true, guard,
|
||||
// message_opt);. To test strictly for the value true, use
|
||||
// assert.strictEqual(true, guard, message_opt);.
|
||||
|
||||
assert.ok = function ok(value, message) {
|
||||
if (!!!value) fail(value, true, message, "==", assert.ok);
|
||||
};
|
||||
|
||||
// 5. The equality assertion tests shallow, coercive equality with
|
||||
// ==.
|
||||
// assert.equal(actual, expected, message_opt);
|
||||
|
||||
assert.equal = function equal(actual, expected, message) {
|
||||
if (actual != expected) fail(actual, expected, message, "==", assert.equal);
|
||||
};
|
||||
|
||||
// 6. The non-equality assertion tests for whether two objects are not equal
|
||||
// with != assert.notEqual(actual, expected, message_opt);
|
||||
|
||||
assert.notEqual = function notEqual(actual, expected, message) {
|
||||
if (actual == expected) {
|
||||
fail(actual, expected, message, "!=", assert.notEqual);
|
||||
}
|
||||
};
|
||||
|
||||
// 7. The equivalence assertion tests a deep equality relation.
|
||||
// assert.deepEqual(actual, expected, message_opt);
|
||||
|
||||
assert.deepEqual = function deepEqual(actual, expected, message) {
|
||||
if (!_deepEqual(actual, expected)) {
|
||||
fail(actual, expected, message, "deepEqual", assert.deepEqual);
|
||||
}
|
||||
};
|
||||
|
||||
function _deepEqual(actual, expected) {
|
||||
// 7.1. All identical values are equivalent, as determined by ===.
|
||||
if (actual === expected) {
|
||||
return true;
|
||||
// 7.2. If the expected value is a Date object, the actual value is
|
||||
// equivalent if it is also a Date object that refers to the same time.
|
||||
} else if (actual instanceof Date && expected instanceof Date) {
|
||||
return actual.getTime() === expected.getTime();
|
||||
|
||||
// 7.3. Other pairs that do not both pass typeof value == "object",
|
||||
// equivalence is determined by ==.
|
||||
} else if (typeof actual != 'object' && typeof expected != 'object') {
|
||||
return actual == expected;
|
||||
|
||||
// 7.4. For all other Object pairs, including Array objects, equivalence is
|
||||
// determined by having the same number of owned properties (as verified
|
||||
// with Object.prototype.hasOwnProperty.call), the same set of keys
|
||||
// (although not necessarily the same order), equivalent values for every
|
||||
// corresponding key, and an identical "prototype" property. Note: this
|
||||
// accounts for both named and indexed properties on Arrays.
|
||||
} else {
|
||||
return objEquiv(actual, expected);
|
||||
}
|
||||
}
|
||||
|
||||
function isUndefinedOrNull (value) {
|
||||
return value === null || value === undefined;
|
||||
}
|
||||
|
||||
function isArguments (object) {
|
||||
return Object.prototype.toString.call(object) == '[object Arguments]';
|
||||
}
|
||||
|
||||
function objEquiv (a, b) {
|
||||
if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
|
||||
return false;
|
||||
// an identical "prototype" property.
|
||||
if (a.prototype !== b.prototype) return false;
|
||||
//~~~I've managed to break Object.keys through screwy arguments passing.
|
||||
// Converting to array solves the problem.
|
||||
if (isArguments(a)) {
|
||||
if (!isArguments(b)) {
|
||||
return false;
|
||||
}
|
||||
a = pSlice.call(a);
|
||||
b = pSlice.call(b);
|
||||
return _deepEqual(a, b);
|
||||
}
|
||||
try{
|
||||
var ka = _keys(a),
|
||||
kb = _keys(b),
|
||||
key, i;
|
||||
} catch (e) {//happens when one is a string literal and the other isn't
|
||||
return false;
|
||||
}
|
||||
// having the same number of owned properties (keys incorporates hasOwnProperty)
|
||||
if (ka.length != kb.length)
|
||||
return false;
|
||||
//the same set of keys (although not necessarily the same order),
|
||||
ka.sort();
|
||||
kb.sort();
|
||||
//~~~cheap key test
|
||||
for (i = ka.length - 1; i >= 0; i--) {
|
||||
if (ka[i] != kb[i])
|
||||
return false;
|
||||
}
|
||||
//equivalent values for every corresponding key, and
|
||||
//~~~possibly expensive deep test
|
||||
for (i = ka.length - 1; i >= 0; i--) {
|
||||
key = ka[i];
|
||||
if (!_deepEqual(a[key], b[key] ))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 8. The non-equivalence assertion tests for any deep inequality.
|
||||
// assert.notDeepEqual(actual, expected, message_opt);
|
||||
|
||||
assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
|
||||
if (_deepEqual(actual, expected)) {
|
||||
fail(actual, expected, message, "notDeepEqual", assert.notDeepEqual);
|
||||
}
|
||||
};
|
||||
|
||||
// 9. The strict equality assertion tests strict equality, as determined by ===.
|
||||
// assert.strictEqual(actual, expected, message_opt);
|
||||
|
||||
assert.strictEqual = function strictEqual(actual, expected, message) {
|
||||
if (actual !== expected) {
|
||||
fail(actual, expected, message, "===", assert.strictEqual);
|
||||
}
|
||||
};
|
||||
|
||||
// 10. The strict non-equality assertion tests for strict inequality, as determined by !==.
|
||||
// assert.notStrictEqual(actual, expected, message_opt);
|
||||
|
||||
assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
|
||||
if (actual === expected) {
|
||||
fail(actual, expected, message, "!==", assert.notStrictEqual);
|
||||
}
|
||||
};
|
||||
|
||||
function _throws (shouldThrow, block, err, message) {
|
||||
var exception = null,
|
||||
threw = false,
|
||||
typematters = true;
|
||||
|
||||
message = message || "";
|
||||
|
||||
//handle optional arguments
|
||||
if (arguments.length == 3) {
|
||||
if (typeof(err) == "string") {
|
||||
message = err;
|
||||
typematters = false;
|
||||
}
|
||||
} else if (arguments.length == 2) {
|
||||
typematters = false;
|
||||
}
|
||||
|
||||
try {
|
||||
block();
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
exception = e;
|
||||
}
|
||||
|
||||
if (shouldThrow && !threw) {
|
||||
fail( "Missing expected exception"
|
||||
+ (err && err.name ? " ("+err.name+")." : '.')
|
||||
+ (message ? " " + message : "")
|
||||
);
|
||||
}
|
||||
if (!shouldThrow && threw && typematters && exception instanceof err) {
|
||||
fail( "Got unwanted exception"
|
||||
+ (err && err.name ? " ("+err.name+")." : '.')
|
||||
+ (message ? " " + message : "")
|
||||
);
|
||||
}
|
||||
if ((shouldThrow && threw && typematters && !(exception instanceof err)) ||
|
||||
(!shouldThrow && threw)) {
|
||||
throw exception;
|
||||
}
|
||||
};
|
||||
|
||||
// 11. Expected to throw an error:
|
||||
// assert.throws(block, Error_opt, message_opt);
|
||||
|
||||
assert.throws = function(block, /*optional*/error, /*optional*/message) {
|
||||
_throws.apply(this, [true].concat(pSlice.call(arguments)));
|
||||
};
|
||||
|
||||
// EXTENSION! This is annoying to write outside this module.
|
||||
assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) {
|
||||
_throws.apply(this, [false].concat(pSlice.call(arguments)));
|
||||
};
|
||||
|
||||
assert.ifError = function (err) { if (err) {throw err;}};
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
/*!
|
||||
* Nodeunit
|
||||
* Copyright (c) 2010 Caolan McMahon
|
||||
* MIT Licensed
|
||||
*
|
||||
* THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
|
||||
* You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build.
|
||||
* Only code on that line will be removed, its mostly to avoid requiring code
|
||||
* that is node specific
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
var async = require('../deps/async'), //@REMOVE_LINE_FOR_BROWSER
|
||||
types = require('./types'); //@REMOVE_LINE_FOR_BROWSER
|
||||
|
||||
|
||||
/**
|
||||
* Added for browser compatibility
|
||||
*/
|
||||
|
||||
var _keys = function(obj){
|
||||
if(Object.keys) return Object.keys(obj);
|
||||
var keys = [];
|
||||
for(var k in obj){
|
||||
if(obj.hasOwnProperty(k)) keys.push(k);
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
|
||||
|
||||
var _copy = function(obj){
|
||||
var nobj = Object();
|
||||
var keys = _keys(obj);
|
||||
for (var i = 0; i < keys.length; i++){
|
||||
nobj[keys[i]] = obj[keys[i]];
|
||||
}
|
||||
return nobj;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Runs a test function (fn) from a loaded module. After the test function
|
||||
* calls test.done(), the callback is executed with an assertionList as its
|
||||
* second argument.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Function} fn
|
||||
* @param {Object} opt
|
||||
* @param {Function} callback
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.runTest = function (name, fn, opt, callback) {
|
||||
var options = types.options(opt);
|
||||
|
||||
options.testStart(name);
|
||||
var start = new Date().getTime();
|
||||
var test = types.test(name, start, options, callback);
|
||||
|
||||
try {
|
||||
fn(test);
|
||||
}
|
||||
catch (e) {
|
||||
test.done(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes an object containing test functions or other test suites as properties
|
||||
* and runs each in series. After all tests have completed, the callback is
|
||||
* called with a list of all assertions as the second argument.
|
||||
*
|
||||
* If a name is passed to this function it is prepended to all test and suite
|
||||
* names that run within it.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Object} suite
|
||||
* @param {Object} opt
|
||||
* @param {Function} callback
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.runSuite = function (name, suite, opt, callback) {
|
||||
var keys = _keys(suite);
|
||||
|
||||
async.concatSeries(keys, function (k, cb) {
|
||||
var prop = suite[k], _name;
|
||||
|
||||
_name = name ? [].concat(name, k) : [k];
|
||||
|
||||
_name.toString = function () {
|
||||
// fallback for old one
|
||||
return this.join(' - ');
|
||||
};
|
||||
|
||||
if (typeof prop === 'function') {
|
||||
if (!opt.testspec || _name.indexOf(opt.testspec) != -1){
|
||||
if (opt.moduleStart)
|
||||
opt.moduleStart();
|
||||
exports.runTest(_name, suite[k], opt, cb);
|
||||
} else
|
||||
return cb();
|
||||
}
|
||||
else {
|
||||
exports.runSuite(_name, suite[k], opt, cb);
|
||||
}
|
||||
}, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Run each exported test function or test suite from a loaded module.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Object} mod
|
||||
* @param {Object} opt
|
||||
* @param {Function} callback
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.runModule = function (name, mod, opt, callback) {
|
||||
var options = _copy(types.options(opt));
|
||||
|
||||
var _run = false;
|
||||
var _moduleStart = options.moduleStart;
|
||||
function run_once(){
|
||||
if (!_run){
|
||||
_run = true;
|
||||
_moduleStart(name);
|
||||
}
|
||||
}
|
||||
options.moduleStart = run_once;
|
||||
|
||||
var start = new Date().getTime();
|
||||
|
||||
exports.runSuite(null, mod, options, function (err, a_list) {
|
||||
var end = new Date().getTime();
|
||||
var assertion_list = types.assertionList(a_list, end - start);
|
||||
options.moduleDone(name, assertion_list);
|
||||
callback(null, a_list);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Treats an object literal as a list of modules keyed by name. Runs each
|
||||
* module and finished with calling 'done'. You can think of this as a browser
|
||||
* safe alternative to runFiles in the nodeunit module.
|
||||
*
|
||||
* @param {Object} modules
|
||||
* @param {Object} opt
|
||||
* @api public
|
||||
*/
|
||||
|
||||
// TODO: add proper unit tests for this function
|
||||
exports.runModules = function (modules, opt) {
|
||||
var all_assertions = [];
|
||||
var options = types.options(opt);
|
||||
var start = new Date().getTime();
|
||||
|
||||
async.concatSeries(_keys(modules), function (k, cb) {
|
||||
exports.runModule(k, modules[k], options, cb);
|
||||
},
|
||||
function (err, all_assertions) {
|
||||
var end = new Date().getTime();
|
||||
options.done(types.assertionList(all_assertions, end - start));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Wraps a test function with setUp and tearDown functions.
|
||||
* Used by testCase.
|
||||
*
|
||||
* @param {Function} setUp
|
||||
* @param {Function} tearDown
|
||||
* @param {Function} fn
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var wrapTest = function (setUp, tearDown, fn) {
|
||||
return function (test) {
|
||||
var context = {};
|
||||
if (tearDown) {
|
||||
var done = test.done;
|
||||
test.done = function (err) {
|
||||
try {
|
||||
tearDown.call(context, function (err2) {
|
||||
if (err && err2) {
|
||||
test._assertion_list.push(
|
||||
types.assertion({error: err})
|
||||
);
|
||||
return done(err2);
|
||||
}
|
||||
done(err || err2);
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
done(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
if (setUp) {
|
||||
setUp.call(context, function (err) {
|
||||
if (err) {
|
||||
return test.done(err);
|
||||
}
|
||||
fn.call(context, test);
|
||||
});
|
||||
}
|
||||
else {
|
||||
fn.call(context, test);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Wraps a group of tests with setUp and tearDown functions.
|
||||
* Used by testCase.
|
||||
*
|
||||
* @param {Function} setUp
|
||||
* @param {Function} tearDown
|
||||
* @param {Object} group
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var wrapGroup = function (setUp, tearDown, group) {
|
||||
var tests = {};
|
||||
var keys = _keys(group);
|
||||
for (var i=0; i<keys.length; i++) {
|
||||
var k = keys[i];
|
||||
if (typeof group[k] === 'function') {
|
||||
tests[k] = wrapTest(setUp, tearDown, group[k]);
|
||||
}
|
||||
else if (typeof group[k] === 'object') {
|
||||
tests[k] = wrapGroup(setUp, tearDown, group[k]);
|
||||
}
|
||||
}
|
||||
return tests;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Utility for wrapping a suite of test functions with setUp and tearDown
|
||||
* functions.
|
||||
*
|
||||
* @param {Object} suite
|
||||
* @return {Object}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.testCase = function (suite) {
|
||||
var setUp = suite.setUp;
|
||||
var tearDown = suite.tearDown;
|
||||
delete suite.setUp;
|
||||
delete suite.tearDown;
|
||||
return wrapGroup(setUp, tearDown, suite);
|
||||
};
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*!
|
||||
* Nodeunit
|
||||
* Copyright (c) 2010 Caolan McMahon
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
var async = require('../deps/async'),
|
||||
types = require('./types'),
|
||||
utils = require('./utils'),
|
||||
core = require('./core'),
|
||||
reporters = require('./reporters'),
|
||||
assert = require('./assert'),
|
||||
path = require('path');
|
||||
|
||||
|
||||
/**
|
||||
* Export sub-modules.
|
||||
*/
|
||||
|
||||
exports.types = types;
|
||||
exports.utils = utils;
|
||||
exports.reporters = reporters;
|
||||
exports.assert = assert;
|
||||
|
||||
// backwards compatibility
|
||||
exports.testrunner = {
|
||||
run: function () {
|
||||
console.log(
|
||||
'WARNING: nodeunit.testrunner is going to be deprecated, please ' +
|
||||
'use nodeunit.reporters.default instead!'
|
||||
);
|
||||
return reporters['default'].run.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Export all core functions
|
||||
*/
|
||||
|
||||
for (var k in core) {
|
||||
exports[k] = core[k];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Load modules from paths array and run all exported tests in series. If a path
|
||||
* is a directory, load all supported file types inside it as modules. This only
|
||||
* reads 1 level deep in the directory and does not recurse through
|
||||
* sub-directories.
|
||||
*
|
||||
* @param {Array} paths
|
||||
* @param {Object} opt
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.runFiles = function (paths, opt) {
|
||||
var all_assertions = [];
|
||||
var options = types.options(opt);
|
||||
var start = new Date().getTime();
|
||||
|
||||
if (!paths.length) {
|
||||
return options.done(types.assertionList(all_assertions));
|
||||
}
|
||||
|
||||
utils.modulePaths(paths, function (err, files) {
|
||||
if (err) throw err;
|
||||
async.concatSeries(files, function (file, cb) {
|
||||
var name = path.basename(file);
|
||||
exports.runModule(name, require(file), options, cb);
|
||||
},
|
||||
function (err, all_assertions) {
|
||||
var end = new Date().getTime();
|
||||
options.done(types.assertionList(all_assertions, end - start));
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*!
|
||||
* Nodeunit
|
||||
* Copyright (c) 2010 Caolan McMahon
|
||||
* MIT Licensed
|
||||
*
|
||||
* THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
|
||||
* You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build.
|
||||
* Only code on that line will be removed, its mostly to avoid requiring code
|
||||
* that is node specific
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* NOTE: this test runner is not listed in index.js because it cannot be
|
||||
* used with the command-line tool, only inside the browser.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Reporter info string
|
||||
*/
|
||||
|
||||
exports.info = "Browser-based test reporter";
|
||||
|
||||
|
||||
/**
|
||||
* Run all tests within each module, reporting the results
|
||||
*
|
||||
* @param {Array} files
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.run = function (modules, options) {
|
||||
var start = new Date().getTime();
|
||||
|
||||
function setText(el, txt) {
|
||||
if ('innerText' in el) {
|
||||
el.innerText = txt;
|
||||
}
|
||||
else if ('textContent' in el){
|
||||
el.textContent = txt;
|
||||
}
|
||||
}
|
||||
|
||||
function getOrCreate(tag, id) {
|
||||
var el = document.getElementById(id);
|
||||
if (!el) {
|
||||
el = document.createElement(tag);
|
||||
el.id = id;
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
return el;
|
||||
};
|
||||
|
||||
var header = getOrCreate('h1', 'nodeunit-header');
|
||||
var banner = getOrCreate('h2', 'nodeunit-banner');
|
||||
var userAgent = getOrCreate('h2', 'nodeunit-userAgent');
|
||||
var tests = getOrCreate('ol', 'nodeunit-tests');
|
||||
var result = getOrCreate('p', 'nodeunit-testresult');
|
||||
|
||||
setText(userAgent, navigator.userAgent);
|
||||
|
||||
nodeunit.runModules(modules, {
|
||||
moduleStart: function (name) {
|
||||
/*var mheading = document.createElement('h2');
|
||||
mheading.innerText = name;
|
||||
results.appendChild(mheading);
|
||||
module = document.createElement('ol');
|
||||
results.appendChild(module);*/
|
||||
},
|
||||
testDone: function (name, assertions) {
|
||||
var test = document.createElement('li');
|
||||
var strong = document.createElement('strong');
|
||||
strong.innerHTML = name + ' <b style="color: black;">(' +
|
||||
'<b class="fail">' + assertions.failures() + '</b>, ' +
|
||||
'<b class="pass">' + assertions.passes() + '</b>, ' +
|
||||
assertions.length +
|
||||
')</b>';
|
||||
test.className = assertions.failures() ? 'fail': 'pass';
|
||||
test.appendChild(strong);
|
||||
|
||||
var aList = document.createElement('ol');
|
||||
aList.style.display = 'none';
|
||||
test.onclick = function () {
|
||||
var d = aList.style.display;
|
||||
aList.style.display = (d == 'none') ? 'block': 'none';
|
||||
};
|
||||
for (var i=0; i<assertions.length; i++) {
|
||||
var li = document.createElement('li');
|
||||
var a = assertions[i];
|
||||
if (a.failed()) {
|
||||
li.innerHTML = (a.message || a.method || 'no message') +
|
||||
'<pre>' + (a.error.stack || a.error) + '</pre>';
|
||||
li.className = 'fail';
|
||||
}
|
||||
else {
|
||||
li.innerHTML = a.message || a.method || 'no message';
|
||||
li.className = 'pass';
|
||||
}
|
||||
aList.appendChild(li);
|
||||
}
|
||||
test.appendChild(aList);
|
||||
tests.appendChild(test);
|
||||
},
|
||||
done: function (assertions) {
|
||||
var end = new Date().getTime();
|
||||
var duration = end - start;
|
||||
|
||||
var failures = assertions.failures();
|
||||
banner.className = failures ? 'fail': 'pass';
|
||||
|
||||
result.innerHTML = 'Tests completed in ' + duration +
|
||||
' milliseconds.<br/><span class="passed">' +
|
||||
assertions.passes() + '</span> assertions of ' +
|
||||
'<span class="all">' + assertions.length + '<span> passed, ' +
|
||||
assertions.failures() + ' failed.';
|
||||
}
|
||||
});
|
||||
};
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*!
|
||||
* Nodeunit
|
||||
* Copyright (c) 2010 Caolan McMahon
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
var nodeunit = require('../nodeunit'),
|
||||
utils = require('../utils'),
|
||||
fs = require('fs'),
|
||||
track = require('../track'),
|
||||
path = require('path');
|
||||
AssertionError = require('../assert').AssertionError;
|
||||
|
||||
/**
|
||||
* Reporter info string
|
||||
*/
|
||||
|
||||
exports.info = "Default tests reporter";
|
||||
|
||||
|
||||
/**
|
||||
* Run all tests within each module, reporting the results to the command-line.
|
||||
*
|
||||
* @param {Array} files
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.run = function (files, options) {
|
||||
|
||||
if (!options) {
|
||||
// load default options
|
||||
var content = fs.readFileSync(
|
||||
__dirname + '/../../bin/nodeunit.json', 'utf8'
|
||||
);
|
||||
options = JSON.parse(content);
|
||||
}
|
||||
|
||||
var error = function (str) {
|
||||
return options.error_prefix + str + options.error_suffix;
|
||||
};
|
||||
var ok = function (str) {
|
||||
return options.ok_prefix + str + options.ok_suffix;
|
||||
};
|
||||
var bold = function (str) {
|
||||
return options.bold_prefix + str + options.bold_suffix;
|
||||
};
|
||||
var assertion_message = function (str) {
|
||||
return options.assertion_prefix + str + options.assertion_suffix;
|
||||
};
|
||||
|
||||
var start = new Date().getTime();
|
||||
var paths = files.map(function (p) {
|
||||
return path.join(process.cwd(), p);
|
||||
});
|
||||
var tracker = track.createTracker(function (tracker) {
|
||||
if (tracker.unfinished()) {
|
||||
console.log('');
|
||||
console.log(error(bold(
|
||||
'FAILURES: Undone tests (or their setups/teardowns): '
|
||||
)));
|
||||
var names = tracker.names();
|
||||
for (var i = 0; i < names.length; i += 1) {
|
||||
console.log('- ' + names[i]);
|
||||
}
|
||||
console.log('');
|
||||
console.log('To fix this, make sure all tests call test.done()');
|
||||
process.reallyExit(tracker.unfinished());
|
||||
}
|
||||
});
|
||||
|
||||
nodeunit.runFiles(paths, {
|
||||
testspec: options.testspec,
|
||||
moduleStart: function (name) {
|
||||
console.log('\n' + bold(name));
|
||||
},
|
||||
testDone: function (name, assertions) {
|
||||
tracker.remove(name);
|
||||
|
||||
if (!assertions.failures()) {
|
||||
console.log('✔ ' + name);
|
||||
}
|
||||
else {
|
||||
console.log(error('✖ ' + name) + '\n');
|
||||
assertions.forEach(function (a) {
|
||||
if (a.failed()) {
|
||||
a = utils.betterErrors(a);
|
||||
if (a.error instanceof AssertionError && a.message) {
|
||||
console.log(
|
||||
'Assertion Message: ' +
|
||||
assertion_message(a.message)
|
||||
);
|
||||
}
|
||||
console.log(a.error.stack + '\n');
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
done: function (assertions, end) {
|
||||
var end = end || new Date().getTime();
|
||||
var duration = end - start;
|
||||
if (assertions.failures()) {
|
||||
console.log(
|
||||
'\n' + bold(error('FAILURES: ')) + assertions.failures() +
|
||||
'/' + assertions.length + ' assertions failed (' +
|
||||
assertions.duration + 'ms)'
|
||||
);
|
||||
}
|
||||
else {
|
||||
console.log(
|
||||
'\n' + bold(ok('OK: ')) + assertions.length +
|
||||
' assertions (' + assertions.duration + 'ms)'
|
||||
);
|
||||
}
|
||||
},
|
||||
testStart: function(name) {
|
||||
tracker.put(name);
|
||||
}
|
||||
});
|
||||
};
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*!
|
||||
* Nodeunit
|
||||
* Copyright (c) 2010 Caolan McMahon
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
var nodeunit = require('../nodeunit'),
|
||||
utils = require('../utils'),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
AssertionError = require('assert').AssertionError;
|
||||
|
||||
/**
|
||||
* Reporter info string
|
||||
*/
|
||||
|
||||
exports.info = "Report tests result as HTML";
|
||||
|
||||
/**
|
||||
* Run all tests within each module, reporting the results to the command-line.
|
||||
*
|
||||
* @param {Array} files
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.run = function (files, options) {
|
||||
|
||||
var start = new Date().getTime();
|
||||
var paths = files.map(function (p) {
|
||||
return path.join(process.cwd(), p);
|
||||
});
|
||||
|
||||
console.log('<html>');
|
||||
console.log('<head>');
|
||||
console.log('<title></title>');
|
||||
console.log('<style type="text/css">');
|
||||
console.log('body { font: 12px Helvetica Neue }');
|
||||
console.log('h2 { margin:0 ; padding:0 }');
|
||||
console.log('pre { font: 11px Andale Mono; margin-left: 1em; padding-left: 1em; margin-top:0; font-size:smaller;}');
|
||||
console.log('.assertion_message { margin-left: 1em; }');
|
||||
console.log(' ol {' +
|
||||
' list-style: none;' +
|
||||
' margin-left: 1em;' +
|
||||
' padding-left: 1em;' +
|
||||
' text-indent: -1em;' +
|
||||
'}');
|
||||
console.log(' ol li.pass:before { content: "\\2714 \\0020"; }');
|
||||
console.log(' ol li.fail:before { content: "\\2716 \\0020"; }');
|
||||
console.log('</style>');
|
||||
console.log('</head>');
|
||||
console.log('<body>');
|
||||
nodeunit.runFiles(paths, {
|
||||
testspec: options.testspec,
|
||||
moduleStart: function (name) {
|
||||
console.log('<h2>' + name + '</h2>');
|
||||
console.log('<ol>');
|
||||
},
|
||||
testDone: function (name, assertions) {
|
||||
if (!assertions.failures()) {
|
||||
console.log('<li class="pass">' + name + '</li>');
|
||||
}
|
||||
else {
|
||||
console.log('<li class="fail">' + name);
|
||||
assertions.forEach(function (a) {
|
||||
if (a.failed()) {
|
||||
a = utils.betterErrors(a);
|
||||
if (a.error instanceof AssertionError && a.message) {
|
||||
console.log('<div class="assertion_message">' +
|
||||
'Assertion Message: ' + a.message +
|
||||
'</div>');
|
||||
}
|
||||
console.log('<pre>');
|
||||
console.log(a.error.stack);
|
||||
console.log('</pre>');
|
||||
}
|
||||
});
|
||||
console.log('</li>');
|
||||
}
|
||||
},
|
||||
moduleDone: function () {
|
||||
console.log('</ol>');
|
||||
},
|
||||
done: function (assertions) {
|
||||
var end = new Date().getTime();
|
||||
var duration = end - start;
|
||||
if (assertions.failures()) {
|
||||
console.log(
|
||||
'<h3>FAILURES: ' + assertions.failures() +
|
||||
'/' + assertions.length + ' assertions failed (' +
|
||||
assertions.duration + 'ms)</h3>'
|
||||
);
|
||||
}
|
||||
else {
|
||||
console.log(
|
||||
'<h3>OK: ' + assertions.length +
|
||||
' assertions (' + assertions.duration + 'ms)</h3>'
|
||||
);
|
||||
}
|
||||
console.log('</body>');
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
'junit': require('./junit'),
|
||||
'default': require('./default'),
|
||||
'skip_passed': require('./skip_passed'),
|
||||
'minimal': require('./minimal'),
|
||||
'html': require('./html')
|
||||
// browser test reporter is not listed because it cannot be used
|
||||
// with the command line tool, only inside a browser.
|
||||
};
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
/*!
|
||||
* Nodeunit
|
||||
* Copyright (c) 2010 Caolan McMahon
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
var nodeunit = require('../nodeunit'),
|
||||
utils = require('../utils'),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
async = require('../../deps/async'),
|
||||
AssertionError = require('assert').AssertionError,
|
||||
child_process = require('child_process'),
|
||||
ejs = require('../../deps/ejs');
|
||||
|
||||
|
||||
/**
|
||||
* Reporter info string
|
||||
*/
|
||||
|
||||
exports.info = "jUnit XML test reports";
|
||||
|
||||
|
||||
/**
|
||||
* Ensures a directory exists using mkdir -p.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Function} callback
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var ensureDir = function (path, callback) {
|
||||
var mkdir = child_process.spawn('mkdir', ['-p', path]);
|
||||
mkdir.on('error', function (err) {
|
||||
callback(err);
|
||||
callback = function(){};
|
||||
});
|
||||
mkdir.on('exit', function (code) {
|
||||
if (code === 0) callback();
|
||||
else callback(new Error('mkdir exited with code: ' + code));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns absolute version of a path. Relative paths are interpreted
|
||||
* relative to process.cwd() or the cwd parameter. Paths that are already
|
||||
* absolute are returned unaltered.
|
||||
*
|
||||
* @param {String} p
|
||||
* @param {String} cwd
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
var abspath = function (p, /*optional*/cwd) {
|
||||
if (p[0] === '/') return p;
|
||||
cwd = cwd || process.cwd();
|
||||
return path.normalize(path.join(cwd, p));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Run all tests within each module, reporting the results to the command-line,
|
||||
* then writes out junit-compatible xml documents.
|
||||
*
|
||||
* @param {Array} files
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.run = function (files, opts, callback) {
|
||||
if (!opts.output) {
|
||||
console.error(
|
||||
'Error: No output directory defined.\n' +
|
||||
'\tEither add an "output" property to your nodeunit.json config ' +
|
||||
'file, or\n\tuse the --output command line option.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
opts.output = abspath(opts.output);
|
||||
var error = function (str) {
|
||||
return opts.error_prefix + str + opts.error_suffix;
|
||||
};
|
||||
var ok = function (str) {
|
||||
return opts.ok_prefix + str + opts.ok_suffix;
|
||||
};
|
||||
var bold = function (str) {
|
||||
return opts.bold_prefix + str + opts.bold_suffix;
|
||||
};
|
||||
|
||||
var start = new Date().getTime();
|
||||
var paths = files.map(function (p) {
|
||||
return path.join(process.cwd(), p);
|
||||
});
|
||||
|
||||
var modules = {}
|
||||
var curModule;
|
||||
|
||||
nodeunit.runFiles(paths, {
|
||||
testspec: opts.testspec,
|
||||
moduleStart: function (name) {
|
||||
curModule = {
|
||||
errorCount: 0,
|
||||
failureCount: 0,
|
||||
tests: 0,
|
||||
testcases: [],
|
||||
name: name
|
||||
};
|
||||
modules[name] = curModule;
|
||||
},
|
||||
testDone: function (name, assertions) {
|
||||
var testcase = {name: name};
|
||||
for (var i=0; i<assertions.length; i++) {
|
||||
var a = assertions[i];
|
||||
if (a.failed()) {
|
||||
a = utils.betterErrors(a);
|
||||
testcase.failure = {
|
||||
message: a.message,
|
||||
backtrace: a.error.stack
|
||||
};
|
||||
|
||||
if (a.error instanceof AssertionError) {
|
||||
curModule.failureCount++;
|
||||
}
|
||||
else {
|
||||
curModule.errorCount++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
curModule.tests++;
|
||||
curModule.testcases.push(testcase);
|
||||
},
|
||||
done: function (assertions) {
|
||||
var end = new Date().getTime();
|
||||
var duration = end - start;
|
||||
|
||||
ensureDir(opts.output, function (err) {
|
||||
var tmpl = __dirname + "/../../share/junit.xml.ejs";
|
||||
fs.readFile(tmpl, function (err, data) {
|
||||
if (err) throw err;
|
||||
var tmpl = data.toString();
|
||||
|
||||
async.forEach(Object.keys(modules), function (k, cb) {
|
||||
var module = modules[k];
|
||||
var rendered = ejs.render(tmpl, {
|
||||
locals: {suites: [module]}
|
||||
});
|
||||
var filename = path.join(
|
||||
opts.output,
|
||||
module.name + '.xml'
|
||||
);
|
||||
console.log('Writing ' + filename);
|
||||
fs.writeFile(filename, rendered, cb);
|
||||
},
|
||||
function (err) {
|
||||
if (err) throw err;
|
||||
else if (assertions.failures()) {
|
||||
console.log(
|
||||
'\n' + bold(error('FAILURES: ')) +
|
||||
assertions.failures() + '/' +
|
||||
assertions.length + ' assertions failed (' +
|
||||
assertions.duration + 'ms)'
|
||||
);
|
||||
}
|
||||
else {
|
||||
console.log(
|
||||
'\n' + bold(ok('OK: ')) + assertions.length +
|
||||
' assertions (' + assertions.duration + 'ms)'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*!
|
||||
* Nodeunit
|
||||
* Copyright (c) 2010 Caolan McMahon
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
var nodeunit = require('../nodeunit'),
|
||||
utils = require('../utils'),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
AssertionError = require('assert').AssertionError;
|
||||
|
||||
/**
|
||||
* Reporter info string
|
||||
*/
|
||||
|
||||
exports.info = "Pretty minimal output";
|
||||
|
||||
/**
|
||||
* Run all tests within each module, reporting the results to the command-line.
|
||||
*
|
||||
* @param {Array} files
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.run = function (files, options) {
|
||||
|
||||
if (!options) {
|
||||
// load default options
|
||||
var content = fs.readFileSync(
|
||||
__dirname + '/../../bin/nodeunit.json', 'utf8'
|
||||
);
|
||||
options = JSON.parse(content);
|
||||
}
|
||||
|
||||
var red = function (str) {
|
||||
return options.error_prefix + str + options.error_suffix;
|
||||
};
|
||||
var green = function (str) {
|
||||
return options.ok_prefix + str + options.ok_suffix;
|
||||
};
|
||||
var magenta = function (str) {
|
||||
return options.assertion_prefix + str + options.assertion_suffix;
|
||||
};
|
||||
var bold = function (str) {
|
||||
return options.bold_prefix + str + options.bold_suffix;
|
||||
};
|
||||
|
||||
var start = new Date().getTime();
|
||||
var paths = files.map(function (p) {
|
||||
return path.join(process.cwd(), p);
|
||||
});
|
||||
|
||||
nodeunit.runFiles(paths, {
|
||||
testspec: options.testspec,
|
||||
moduleStart: function (name) {
|
||||
process.stdout.write(bold(name) + ': ');
|
||||
},
|
||||
moduleDone: function (name, assertions) {
|
||||
console.log('');
|
||||
if (assertions.failures()) {
|
||||
assertions.forEach(function (a) {
|
||||
if (a.failed()) {
|
||||
a = utils.betterErrors(a);
|
||||
if (a.error instanceof AssertionError && a.message) {
|
||||
console.log(
|
||||
'Assertion in test ' + bold(a.testname) + ': ' +
|
||||
magenta(a.message)
|
||||
);
|
||||
}
|
||||
console.log(a.error.stack + '\n');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
testStart: function () {
|
||||
},
|
||||
testDone: function (name, assertions) {
|
||||
if (!assertions.failures()) {
|
||||
process.stdout.write('.');
|
||||
}
|
||||
else {
|
||||
process.stdout.write(red('F'));
|
||||
assertions.forEach(function (assertion) {
|
||||
assertion.testname = name;
|
||||
});
|
||||
}
|
||||
},
|
||||
done: function (assertions) {
|
||||
var end = new Date().getTime();
|
||||
var duration = end - start;
|
||||
if (assertions.failures()) {
|
||||
console.log(
|
||||
'\n' + bold(red('FAILURES: ')) + assertions.failures() +
|
||||
'/' + assertions.length + ' assertions failed (' +
|
||||
assertions.duration + 'ms)'
|
||||
);
|
||||
}
|
||||
else {
|
||||
console.log(
|
||||
'\n' + bold(green('OK: ')) + assertions.length +
|
||||
' assertions (' + assertions.duration + 'ms)'
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*!
|
||||
* Nodeunit
|
||||
* Copyright (c) 2010 Caolan McMahon
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
var nodeunit = require('../nodeunit'),
|
||||
utils = require('../utils'),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
AssertionError = require('assert').AssertionError;
|
||||
|
||||
/**
|
||||
* Reporter info string
|
||||
*/
|
||||
|
||||
exports.info = "Skip passed tests output";
|
||||
|
||||
/**
|
||||
* Run all tests within each module, reporting the results to the command-line.
|
||||
*
|
||||
* @param {Array} files
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.run = function (files, options) {
|
||||
|
||||
if (!options) {
|
||||
// load default options
|
||||
var content = fs.readFileSync(
|
||||
__dirname + '/../../bin/nodeunit.json', 'utf8'
|
||||
);
|
||||
options = JSON.parse(content);
|
||||
}
|
||||
|
||||
var error = function (str) {
|
||||
return options.error_prefix + str + options.error_suffix;
|
||||
};
|
||||
var ok = function (str) {
|
||||
return options.ok_prefix + str + options.ok_suffix;
|
||||
};
|
||||
var bold = function (str) {
|
||||
return options.bold_prefix + str + options.bold_suffix;
|
||||
};
|
||||
var assertion_message = function (str) {
|
||||
return options.assertion_prefix + str + options.assertion_suffix;
|
||||
};
|
||||
|
||||
var start = new Date().getTime();
|
||||
var paths = files.map(function (p) {
|
||||
return path.join(process.cwd(), p);
|
||||
});
|
||||
|
||||
nodeunit.runFiles(paths, {
|
||||
testspec: options.testspec,
|
||||
moduleStart: function (name) {
|
||||
console.log('\n' + bold(name));
|
||||
},
|
||||
testDone: function (name, assertions) {
|
||||
if (assertions.failures()) {
|
||||
console.log(error('✖ ' + name) + '\n');
|
||||
assertions.forEach(function (a) {
|
||||
if (a.failed()) {
|
||||
a = utils.betterErrors(a);
|
||||
if (a.error instanceof AssertionError && a.message) {
|
||||
console.log(
|
||||
'Assertion Message: ' + assertion_message(a.message)
|
||||
);
|
||||
}
|
||||
console.log(a.error.stack + '\n');
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
moduleDone: function (name, assertions) {
|
||||
if (!assertions.failures()) {
|
||||
console.log('✔ all tests passed');
|
||||
}
|
||||
else {
|
||||
console.log(error('✖ some tests failed'));
|
||||
}
|
||||
},
|
||||
done: function (assertions) {
|
||||
var end = new Date().getTime();
|
||||
var duration = end - start;
|
||||
if (assertions.failures()) {
|
||||
console.log(
|
||||
'\n' + bold(error('FAILURES: ')) + assertions.failures() +
|
||||
'/' + assertions.length + ' assertions failed (' +
|
||||
assertions.duration + 'ms)'
|
||||
);
|
||||
}
|
||||
else {
|
||||
console.log(
|
||||
'\n' + bold(ok('OK: ')) + assertions.length +
|
||||
' assertions (' + assertions.duration + 'ms)'
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*!
|
||||
* Simple util module to track tests. Adds a process.exit hook to print
|
||||
* the undone tests.
|
||||
*/
|
||||
|
||||
|
||||
exports.createTracker = function (on_exit) {
|
||||
var names = {};
|
||||
var tracker = {
|
||||
names: function () {
|
||||
var arr = [];
|
||||
for (var k in names) {
|
||||
if (names.hasOwnProperty(k)) {
|
||||
arr.push(k);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
},
|
||||
unfinished: function () {
|
||||
return tracker.names().length;
|
||||
},
|
||||
put: function (testname) {
|
||||
names[testname] = testname;
|
||||
},
|
||||
remove: function (testname) {
|
||||
delete names[testname];
|
||||
}
|
||||
};
|
||||
|
||||
process.on('exit', function() {
|
||||
on_exit = on_exit || exports.default_on_exit;
|
||||
on_exit(tracker);
|
||||
});
|
||||
|
||||
return tracker;
|
||||
};
|
||||
|
||||
exports.default_on_exit = function (tracker) {
|
||||
if (tracker.unfinished()) {
|
||||
console.log('');
|
||||
console.log('Undone tests (or their setups/teardowns): ');
|
||||
var names = tracker.names();
|
||||
for (var i = 0; i < names.length; i += 1) {
|
||||
console.log(names[i]);
|
||||
}
|
||||
process.reallyExit(tracker.unfinished());
|
||||
}
|
||||
};
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
/*!
|
||||
* Nodeunit
|
||||
* Copyright (c) 2010 Caolan McMahon
|
||||
* MIT Licensed
|
||||
*
|
||||
* THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
|
||||
* You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build.
|
||||
* Only code on that line will be removed, its mostly to avoid requiring code
|
||||
* that is node specific
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
var assert = require('./assert'), //@REMOVE_LINE_FOR_BROWSER
|
||||
async = require('../deps/async'); //@REMOVE_LINE_FOR_BROWSER
|
||||
|
||||
|
||||
/**
|
||||
* Creates assertion objects representing the result of an assert call.
|
||||
* Accepts an object or AssertionError as its argument.
|
||||
*
|
||||
* @param {object} obj
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.assertion = function (obj) {
|
||||
return {
|
||||
method: obj.method || '',
|
||||
message: obj.message || (obj.error && obj.error.message) || '',
|
||||
error: obj.error,
|
||||
passed: function () {
|
||||
return !this.error;
|
||||
},
|
||||
failed: function () {
|
||||
return Boolean(this.error);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an assertion list object representing a group of assertions.
|
||||
* Accepts an array of assertion objects.
|
||||
*
|
||||
* @param {Array} arr
|
||||
* @param {Number} duration
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.assertionList = function (arr, duration) {
|
||||
var that = arr || [];
|
||||
that.failures = function () {
|
||||
var failures = 0;
|
||||
for (var i=0; i<this.length; i++) {
|
||||
if (this[i].failed()) failures++;
|
||||
}
|
||||
return failures;
|
||||
};
|
||||
that.passes = function () {
|
||||
return that.length - that.failures();
|
||||
};
|
||||
that.duration = duration || 0;
|
||||
return that;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a wrapper function for assert module methods. Executes a callback
|
||||
* after the it's complete with an assertion object representing the result.
|
||||
*
|
||||
* @param {Function} callback
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var assertWrapper = function (callback) {
|
||||
return function (new_method, assert_method, arity) {
|
||||
return function () {
|
||||
var message = arguments[arity-1];
|
||||
var a = exports.assertion({method: new_method, message: message});
|
||||
try {
|
||||
assert[assert_method].apply(null, arguments);
|
||||
}
|
||||
catch (e) {
|
||||
a.error = e;
|
||||
}
|
||||
callback(a);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the 'test' object that gets passed to every test function.
|
||||
* Accepts the name of the test function as its first argument, followed by
|
||||
* the start time in ms, the options object and a callback function.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Number} start
|
||||
* @param {Object} options
|
||||
* @param {Function} callback
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.test = function (name, start, options, callback) {
|
||||
var expecting;
|
||||
var a_list = [];
|
||||
|
||||
var wrapAssert = assertWrapper(function (a) {
|
||||
a_list.push(a);
|
||||
if (options.log) {
|
||||
async.nextTick(function () {
|
||||
options.log(a);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var test = {
|
||||
done: function (err) {
|
||||
if (expecting !== undefined && expecting !== a_list.length) {
|
||||
var e = new Error(
|
||||
'Expected ' + expecting + ' assertions, ' +
|
||||
a_list.length + ' ran'
|
||||
);
|
||||
var a1 = exports.assertion({method: 'expect', error: e});
|
||||
a_list.push(a1);
|
||||
if (options.log) {
|
||||
async.nextTick(function () {
|
||||
options.log(a1);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
var a2 = exports.assertion({error: err});
|
||||
a_list.push(a2);
|
||||
if (options.log) {
|
||||
async.nextTick(function () {
|
||||
options.log(a2);
|
||||
});
|
||||
}
|
||||
}
|
||||
var end = new Date().getTime();
|
||||
async.nextTick(function () {
|
||||
var assertion_list = exports.assertionList(a_list, end - start);
|
||||
options.testDone(name, assertion_list);
|
||||
callback(null, a_list);
|
||||
});
|
||||
},
|
||||
ok: wrapAssert('ok', 'ok', 2),
|
||||
same: wrapAssert('same', 'deepEqual', 3),
|
||||
equals: wrapAssert('equals', 'equal', 3),
|
||||
expect: function (num) {
|
||||
expecting = num;
|
||||
},
|
||||
_assertion_list: a_list
|
||||
};
|
||||
// add all functions from the assert module
|
||||
for (var k in assert) {
|
||||
if (assert.hasOwnProperty(k)) {
|
||||
test[k] = wrapAssert(k, k, assert[k].length);
|
||||
}
|
||||
}
|
||||
return test;
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensures an options object has all callbacks, adding empty callback functions
|
||||
* if any are missing.
|
||||
*
|
||||
* @param {Object} opt
|
||||
* @return {Object}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.options = function (opt) {
|
||||
var optionalCallback = function (name) {
|
||||
opt[name] = opt[name] || function () {};
|
||||
};
|
||||
|
||||
optionalCallback('moduleStart');
|
||||
optionalCallback('moduleDone');
|
||||
optionalCallback('testStart');
|
||||
optionalCallback('testDone');
|
||||
//optionalCallback('log');
|
||||
|
||||
// 'done' callback is not optional.
|
||||
|
||||
return opt;
|
||||
};
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
/*!
|
||||
* Nodeunit
|
||||
* Copyright (c) 2010 Caolan McMahon
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
var async = require('../deps/async'),
|
||||
fs = require('fs'),
|
||||
util = require('util'),
|
||||
Script = process.binding('evals').Script,
|
||||
http = require('http');
|
||||
|
||||
|
||||
/**
|
||||
* Detect if coffee-script is available and search for .coffee as an
|
||||
* extension in modulePaths if it is.
|
||||
*/
|
||||
|
||||
var extensionPattern;
|
||||
try {
|
||||
require('coffee-script');
|
||||
extensionPattern = /\.(?:js|coffee)$/;
|
||||
}
|
||||
catch (e) {
|
||||
extensionPattern = /\.js$/;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds all modules at each path in an array, If a path is a directory, it
|
||||
* returns all supported file types inside it. This only reads 1 level deep in
|
||||
* the directory and does not recurse through sub-directories.
|
||||
*
|
||||
* The extension (.js, .coffee etc) is stripped from the filenames so they can
|
||||
* simply be require()'ed.
|
||||
*
|
||||
* @param {Array} paths
|
||||
* @param {Function} callback
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.modulePaths = function (paths, callback) {
|
||||
async.concat(paths, function (p, cb) {
|
||||
fs.stat(p, function (err, stats) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
if (stats.isFile()) {
|
||||
return cb(null, [p]);
|
||||
}
|
||||
if (stats.isDirectory()) {
|
||||
fs.readdir(p, function (err, files) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
// filter out any filenames with unsupported extensions
|
||||
var modules = files.filter(function (filename) {
|
||||
return extensionPattern.exec(filename);
|
||||
});
|
||||
|
||||
// remove extension from module name and prepend the
|
||||
// directory path
|
||||
var fullpaths = modules.map(function (filename) {
|
||||
var mod_name = filename.replace(extensionPattern, '');
|
||||
return [p, mod_name].join('/');
|
||||
});
|
||||
|
||||
// sort filenames here, because Array.map changes order
|
||||
fullpaths.sort();
|
||||
|
||||
cb(null, fullpaths);
|
||||
});
|
||||
}
|
||||
});
|
||||
}, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluates JavaScript files in a sandbox, returning the context. The first
|
||||
* argument can either be a single filename or an array of filenames. If
|
||||
* multiple filenames are given their contents are concatenated before
|
||||
* evalution. The second argument is an optional context to use for the sandbox.
|
||||
*
|
||||
* @param files
|
||||
* @param {Object} sandbox
|
||||
* @return {Object}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.sandbox = function (files, /*optional*/sandbox) {
|
||||
var source, script, result;
|
||||
if (!(files instanceof Array)) {
|
||||
files = [files];
|
||||
}
|
||||
source = files.map(function (file) {
|
||||
return fs.readFileSync(file, 'utf8');
|
||||
}).join('');
|
||||
|
||||
if (!sandbox) {
|
||||
sandbox = {};
|
||||
}
|
||||
script = new Script(source);
|
||||
result = script.runInNewContext(sandbox);
|
||||
return sandbox;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides a http request, response testing environment.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* var httputil = require('nodeunit').utils.httputil
|
||||
* exports.testSomething = function(test) {
|
||||
* httputil(function (req, resp) {
|
||||
* resp.writeHead(200, {});
|
||||
* resp.end('test data');
|
||||
* },
|
||||
* function(server, client) {
|
||||
* client.fetch('GET', '/', {}, function(resp) {
|
||||
* test.equal('test data', resp.body);
|
||||
* server.close();
|
||||
* test.done();
|
||||
* })
|
||||
* });
|
||||
* };
|
||||
*
|
||||
* @param {Function} cgi
|
||||
* @param {Function} envReady
|
||||
* @api public
|
||||
*/
|
||||
exports.httputil = function (cgi, envReady) {
|
||||
var hostname = process.env.HOSTNAME || 'localhost';
|
||||
var port = process.env.PORT || 3000;
|
||||
|
||||
var server = http.createServer(cgi);
|
||||
server.listen(port, hostname);
|
||||
|
||||
var client = http.createClient(port, hostname);
|
||||
client.fetch = function (method, path, headers, respReady) {
|
||||
var request = this.request(method, path, headers);
|
||||
request.end();
|
||||
request.on('response', function (response) {
|
||||
response.setEncoding('utf8');
|
||||
response.on('data', function (chunk) {
|
||||
if (response.body) {
|
||||
response.body += chunk;
|
||||
} else {
|
||||
response.body = chunk;
|
||||
}
|
||||
});
|
||||
response.on('end', function () {
|
||||
if (response.headers['content-type'] === 'application/json') {
|
||||
response.bodyAsObject = JSON.parse(response.body);
|
||||
}
|
||||
respReady(response);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
process.nextTick(function () {
|
||||
if (envReady && typeof envReady === 'function') {
|
||||
envReady(server, client);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Improves formatting of AssertionError messages to make deepEqual etc more
|
||||
* readable.
|
||||
*
|
||||
* @param {Object} assertion
|
||||
* @return {Object}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.betterErrors = function (assertion) {
|
||||
if (!assertion.error) return;
|
||||
|
||||
var e = assertion.error;
|
||||
// deepEqual error message is a bit sucky, lets improve it!
|
||||
// e.actual and e.expected could be null or undefined, so
|
||||
// using getOwnPropertyDescriptor to see if they exist:
|
||||
if (Object.getOwnPropertyDescriptor(e, 'actual') &&
|
||||
Object.getOwnPropertyDescriptor(e, 'expected')) {
|
||||
|
||||
// alexgorbatchev 2010-10-22 :: Added a bit of depth to inspection
|
||||
var actual = util.inspect(e.actual, false, 10).replace(/\n$/, '');
|
||||
var expected = util.inspect(e.expected, false, 10).replace(/\n$/, '');
|
||||
var multiline = (
|
||||
actual.indexOf('\n') !== -1 ||
|
||||
expected.indexOf('\n') !== -1
|
||||
);
|
||||
var spacing = (multiline ? '\n' : ' ');
|
||||
e._message = e.message;
|
||||
e.stack = (
|
||||
e.name + ':' + spacing +
|
||||
actual + spacing + e.operator + spacing +
|
||||
expected + '\n' +
|
||||
e.stack.split('\n').slice(1).join('\n')
|
||||
);
|
||||
}
|
||||
return assertion;
|
||||
};
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
.\" Generated with Ronnjs/v0.1
|
||||
.\" http://github.com/kapouer/ronnjs/
|
||||
.
|
||||
.TH "NODEUNIT" "1" "October 2010" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBnodeunit\fR \-\- simple node\.js unit testing tool
|
||||
.
|
||||
.SH "SYNOPSIS"
|
||||
.
|
||||
.nf
|
||||
nodeunit [options] <file\-or\-directory> [<file\-or\-directory> \.\.\.]
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.SH "DESCRIPTION"
|
||||
Nodeunit is a simple unit testing tool based on the node\.js assert module\.
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Simple to use
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Just export the tests from a module
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Helps you avoid common pitfalls when testing asynchronous code
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Easy to add test cases with setUp and tearDown functions if you wish
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Allows the use of mocks and stubs
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "OPTIONS"
|
||||
\fB\-\-config FILE\fR:
|
||||
.
|
||||
.br
|
||||
Load config options from a JSON file, allows the customisation
|
||||
of color schemes for the default test reporter etc\.
|
||||
See bin/nodeunit\.json for current available options\.
|
||||
.
|
||||
.P
|
||||
\fB\-\-reporter FILE\fR:
|
||||
.
|
||||
.br
|
||||
You can set the test reporter to a custom module or on of the modules
|
||||
in nodeunit/lib/reporters, when omitted, the default test runner is used\.
|
||||
.
|
||||
.P
|
||||
\fB\-\-list\-reporters\fR:
|
||||
.
|
||||
.br
|
||||
List available build\-in reporters\.
|
||||
.
|
||||
.P
|
||||
\fB\-h\fR, \fB\-\-help\fR:
|
||||
.
|
||||
.br
|
||||
Display the help and exit\.
|
||||
.
|
||||
.P
|
||||
\fB\-v\fR, \fB\-\-version\fR:
|
||||
.
|
||||
.br
|
||||
Output version information and exit\.
|
||||
.
|
||||
.P
|
||||
\fB<file\-or\-directory>\fR:
|
||||
You can run nodeunit on specific files or on all \fI*\.js\fR files inside
|
||||
.
|
||||
.br
|
||||
a directory\.
|
||||
.
|
||||
.SH "AUTHORS"
|
||||
Written by Caolan McMahon and other nodeunit contributors\.
|
||||
.
|
||||
.br
|
||||
Contributors list: \fIhttp://github\.com/caolan/nodeunit/contributors\fR\|\.
|
||||
.
|
||||
.SH "REPORTING BUGS"
|
||||
Report nodeunit bugs to \fIhttp://github\.com/caolan/nodeunit/issues\fR\|\.
|
||||
.
|
||||
.SH "COPYRIGHT"
|
||||
Copyright © 2010 Caolan McMahon\.
|
||||
.
|
||||
.br
|
||||
Nodeunit has been released under the MIT license:
|
||||
.
|
||||
.br
|
||||
\fIhttp://github\.com/caolan/nodeunit/raw/master/LICENSE\fR\|\.
|
||||
.
|
||||
.SH "SEE ALSO"
|
||||
node(1)
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
var options = {
|
||||
indent: 4,
|
||||
onevar: false
|
||||
};
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
{ "name": "nodeunit"
|
||||
, "description": "Easy unit testing for node.js and the browser."
|
||||
, "maintainers":
|
||||
[ { "name": "Caolan McMahon"
|
||||
, "web": "https://github.com/caolan"
|
||||
}
|
||||
]
|
||||
, "contributors" :
|
||||
[ { "name": "Alex Gorbatchev"
|
||||
, "web": "https://github.com/alexgorbatchev"
|
||||
}
|
||||
, { "name": "Alex Wolfe"
|
||||
, "web": "https://github.com/alexkwolfe"
|
||||
}
|
||||
, { "name": "Carl Fürstenberg"
|
||||
, "web": "https://github.com/azatoth"
|
||||
}
|
||||
, { "name": "Gerad Suyderhoud"
|
||||
, "web": "https://github.com/gerad"
|
||||
}
|
||||
, { "name": "Kadir Pekel"
|
||||
, "web": "https://github.com/coffeemate"
|
||||
}
|
||||
, { "name": "Oleg Efimov"
|
||||
, "web": "https://github.com/Sannis"
|
||||
}
|
||||
, { "name": "Orlando Vazquez"
|
||||
, "web": "https://github.com/orlandov"
|
||||
}
|
||||
, { "name": "Ryan Dahl"
|
||||
, "web": "https://github.com/ry"
|
||||
}
|
||||
, { "name": "Sam Stephenson"
|
||||
, "web": "https://github.com/sstephenson"
|
||||
}
|
||||
, { "name": "Thomas Mayfield"
|
||||
, "web": "https://github.com/thegreatape"
|
||||
}
|
||||
, { "name": "Elijah Insua <tmpvar@gmail.com>",
|
||||
"web": "http://tmpvar.com"
|
||||
}
|
||||
]
|
||||
, "version": "0.5.3"
|
||||
, "repository" :
|
||||
{ "type" : "git"
|
||||
, "url" : "http://github.com/caolan/nodeunit.git"
|
||||
}
|
||||
, "bugs" : { "web" : "http://github.com/caolan/nodeunit/issues" }
|
||||
, "licenses" :
|
||||
[ { "type" : "MIT"
|
||||
, "url" : "http://github.com/caolan/nodeunit/raw/master/LICENSE"
|
||||
}
|
||||
]
|
||||
, "directories" : { "lib": "./lib", "doc" : "./doc", "man" : "./man1" }
|
||||
, "bin" : { "nodeunit" : "./bin/nodeunit" }
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<% for (var i=0; i < suites.length; i++) { %>
|
||||
<% var suite=suites[i]; %>
|
||||
<testsuite name="<%= suite.name %>"
|
||||
errors="<%= suite.errorCount %>"
|
||||
failures="<%= suite.failureCount %>"
|
||||
tests="<%= suite.tests %>">
|
||||
<% for (var j=0; j < suite.testcases.length; j++) { %>
|
||||
<% var testcase=suites[i].testcases[j]; %>
|
||||
<testcase name="<%= testcase.name %>">
|
||||
<% if (testcase.failure) { %>
|
||||
<failure message="<%= testcase.failure.message %>">
|
||||
<% if (testcase.failure.backtrace) { %><%= testcase.failure.backtrace %><% } %>
|
||||
</failure>
|
||||
<% } %>
|
||||
</testcase>
|
||||
<% } %>
|
||||
</testsuite>
|
||||
<% } %>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*!
|
||||
* Nodeunit
|
||||
* https://github.com/caolan/nodeunit
|
||||
* Copyright (c) 2010 Caolan McMahon
|
||||
* MIT Licensed
|
||||
*
|
||||
* json2.js
|
||||
* http://www.JSON.org/json2.js
|
||||
* Public Domain.
|
||||
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||||
*/
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*!
|
||||
* Styles taken from qunit.css
|
||||
*/
|
||||
|
||||
h1#nodeunit-header, h1.nodeunit-header {
|
||||
padding: 15px;
|
||||
font-size: large;
|
||||
background-color: #06b;
|
||||
color: white;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1#nodeunit-header a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
h2#nodeunit-banner {
|
||||
height: 2em;
|
||||
border-bottom: 1px solid white;
|
||||
background-color: #eee;
|
||||
margin: 0;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
}
|
||||
h2#nodeunit-banner.pass {
|
||||
background-color: green;
|
||||
}
|
||||
h2#nodeunit-banner.fail {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
h2#nodeunit-userAgent, h2.nodeunit-userAgent {
|
||||
padding: 10px;
|
||||
background-color: #eee;
|
||||
color: black;
|
||||
margin: 0;
|
||||
font-size: small;
|
||||
font-weight: normal;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
div#nodeunit-testrunner-toolbar {
|
||||
background: #eee;
|
||||
border-top: 1px solid black;
|
||||
padding: 10px;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
margin: 0;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
ol#nodeunit-tests {
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
font-size: 10pt;
|
||||
}
|
||||
ol#nodeunit-tests li strong {
|
||||
cursor:pointer;
|
||||
}
|
||||
ol#nodeunit-tests .pass {
|
||||
color: green;
|
||||
}
|
||||
ol#nodeunit-tests .fail {
|
||||
color: red;
|
||||
}
|
||||
|
||||
p#nodeunit-testresult {
|
||||
margin-left: 1em;
|
||||
font-size: 10pt;
|
||||
font-family: 'trebuchet ms', verdana, arial;
|
||||
}
|
||||
Reference in New Issue
Block a user