├── .gitignore
├── .travis.yml
├── README.md
├── bin
└── specify
├── package.json
├── reporters
├── compact.js
├── default.js
└── json.js
├── specify.js
├── test.sh
└── test
├── fixtures
├── all.txt
├── default_reporter.txt
├── filters.txt
├── reporter.txt
└── single.txt
├── specify.js
└── specify
├── compact.js
├── parallel.js
└── singletest.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | *.log
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: "node_js"
2 | node_js:
3 | - 0.8
4 | - 0.9
5 | branches:
6 | only:
7 | - master
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # specify
3 |
4 | `specify` is the simplest way i could think to do node.js testing.
5 |
6 | It works with sync code and async code all the same.
7 |
8 | Please use versions `~0.6.x` for node `0.6` and `~1.0.x` for node `0.8` or higher.
9 |
10 | If you don't like reading and want to see some code you can look at [nano's tests](https://github.com/dscape/nano/tree/master/tests) and learn there. Also read the `specify` source code; it's just a couple of lines of code.
11 |
12 | ``` js
13 | var specify = require('specify');
14 |
15 | specify('create_by_secret', function (assert) {
16 | user.create_by_secret({invitation_code: "1234321!!"}, function (err) {
17 | assert.equal(err.eid, "ec:api:user:create_by_secret:wrong_code");
18 | assert.equal(err.status_code, 400);
19 | });
20 | });
21 |
22 | specify.run();
23 | ```
24 |
25 | The assert calls are callback functions that wrap the assert module. `specify` figures out how many callbacks you are calling by [static-analysis]. To put it simply, it counts the number of times you wrote `assert`. When that number of assertions is met, or when a timeout occurs, that test is complete and we can execute the next one.
26 |
27 | Static analysis does not work for a `for` loop and some other flow control constructs. In that case you can use `assert.expect(nr)` to tell specify how many assertions to expect:
28 |
29 | ``` js
30 | specify('more_assertions_than_asserts', function(assert) {
31 | assert.expect(5);
32 | for(var i in [1,2,3,4,5]) {
33 | assert.equal(i,i);
34 | }
35 | });
36 | ```
37 |
38 | `specify` runs tests one by one, not in parallel. If you set `assert.expect` higher than the number of `assert` function calls the execution will stop, and your current test will never finish. You can circumvent this by setting a timeout:
39 |
40 | ``` js
41 | specify('foo', 50, function (assert) {
42 | call_to_db_that_takes_a_long_time(function (data) {
43 | assert.equal(data, 'foo');
44 | });
45 | });
46 | ```
47 |
48 | Because tests are serialized, `specify` can catch uncaught exceptions and continue to run. You will get a report about the error that was thrown somewhere in your stack. This is analogous to the functionality the community refers to as `domains`.
49 |
50 | `specify` is standalone; you don't need any special binaries to run it.
51 |
52 | If you think all these `specify` functions make your code look bloated, you can also run a single function:
53 |
54 | ``` js
55 | var specify = require('specify')
56 | , request = require('request')
57 | ;
58 |
59 | specify.run(
60 |
61 | function (assert) {
62 |
63 | var get = { uri: "http://someservice.com/1/apps/dscape", json: true }
64 | , del = { uri: "http://someservice.com/1/apps/dscape", method: "DELETE"
65 | , json : true }
66 | , app_name
67 | ;
68 |
69 | request(get, function (err, _, body) {
70 |
71 | assert.equal(err,null);
72 | assert.ok(body.rows);
73 | assert.ok(body.total_rows >= 1);
74 | assert.ok(body.rows.length >= 1);
75 |
76 | app_name = body.rows[0].value.app_name;
77 | del.uri += "/" + app_name;
78 |
79 | request(del, function (err, resp, body) {
80 |
81 | assert.equal(err,null);
82 | assert.equal(resp.statusCode, 200);
83 | assert.equal(body.app.name, app_name);
84 | assert.equal(body.app.user,"dscape");
85 |
86 | });
87 |
88 | });
89 |
90 | }
91 |
92 | );
93 | ```
94 |
95 |
96 | # installation
97 |
98 |
99 | ## node.js
100 |
101 | 1. install [npm]
102 | 2. `npm install specify`
103 | 3. `var specify = require('specify');`
104 |
105 |
106 | # filtering
107 |
108 | In `specify` you specify which tests you want to run:
109 |
110 | ``` js
111 | var specify = require('specify')
112 | , filters = process.argv.slice(2)
113 | ;
114 |
115 | specify('foo', function (assert) {
116 | assert.equal('foo', 'foo');
117 | });
118 |
119 | specify('bar', function (assert) {
120 | assert.equal('bar', 'baz', 'bar failed');
121 | });
122 |
123 | specify('baz', function (assert) {
124 | assert.equal('baz', 'baz');
125 | });
126 |
127 | specify.run(filters);
128 | ```
129 |
130 |
131 | # reporters
132 |
133 | If you feel like the output sent to `stdout` is ugly, you can write your own reporter and send in a pull request.
134 |
135 | Now use it:
136 |
137 | ``` js
138 | specify('specify#ask_for_a_specific_reporter', function(assert) {
139 | specify.reporter('my_awesome_reporter');
140 | setTimeout(function (){
141 | assert.ok(true);
142 | },1);
143 | });
144 | ```
145 |
146 | You can also do this with a function if you like:
147 |
148 | ``` js
149 | specify('specify#custom_reporter_from_function', function(assert) {
150 | specify.reporter(function (name, report, errors) {
151 | console.log(name);
152 | });
153 | setTimeout(function () {
154 | assert.ok(false, 'i see dead people');
155 | assert.ok(true);
156 | },1);
157 | });
158 | ```
159 |
160 |
161 | # samples
162 |
163 | Samples are available in the `/test` folder.
164 |
165 |
166 | # contribute
167 |
168 | Everyone is welcome to contribute. Patches, bug-fixes, reporters, new features.
169 |
170 | 1. Create an [issue][issues] so the community can comment on your idea
171 | 2. Fork `specify`
172 | 3. Create a new branch `git checkout -b feature_name`
173 | 4. Create tests for the changes you made
174 | 5. Make sure you pass both existing and newly inserted tests
175 | 6. Commit your changes
176 | 7. Push to your branch `git push origin feature_name`
177 | 8. Create a pull request
178 |
179 |
180 | # meta
181 |
182 | * Code: `git clone git://github.com/dscape/specify.git`
183 | * Home:
184 | * Bugs:
185 | * Build: [](http://travis-ci.org/dscape/specify)
186 |
187 |
188 | `(oO)--',-` in [caos]
189 |
190 |
191 | # license
192 |
193 | Copyright 2012 nuno job `(oO)--',--`
194 |
195 | Licensed under the apache license, version 2.0 (the "license");
196 | You may not use this file except in compliance with the license.
197 | You may obtain a copy of the license at
198 |
199 | http://www.apache.org/licenses/LICENSE-2.0
200 |
201 | Unless required by applicable law or agreed to in writing, software
202 | distributed under the license is distributed on an "as is" basis,
203 | without warranties or conditions of any kind, either express or implied.
204 | see the license for the specific language governing permissions and
205 | limitations under the license.
206 |
207 | [npm]: http://npmjs.org
208 | [issues]: http://github.com/dscape/specify/issues
209 | [caos]: http://caos.di.uminho.pt/
210 | [static-analysis]: http://en.wikipedia.org/wiki/Static_program_analysis
211 |
--------------------------------------------------------------------------------
/bin/specify:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | var fs = require('fs')
3 | , path = require('path')
4 | , tty = require('tty')
5 | , spawn = require('child_process').spawn
6 | , argv = require('optimist').argv
7 | , stack = []
8 | , success = true
9 | , args = {env: process.env}
10 | , counts = {ok: 0, total: 0}
11 | , buffer = ''
12 | , base_path
13 | , dirs
14 | , files
15 | , test
16 | ;
17 |
18 | //
19 | // Will try to find tests in `tests` and `test`
20 | // Throws otherwise
21 | //
22 | try {
23 | base_path = path.resolve('.', 'tests');
24 | dirs = fs.readdirSync(base_path);
25 | } catch (exc) {
26 | base_path = path.resolve('.', 'test');
27 | dirs = fs.readdirSync(base_path);
28 | }
29 |
30 | if (argv._.length > 0) {
31 | dirs = argv._;
32 | }
33 |
34 | //
35 | // Set env vars in a platform agnostic way
36 | //
37 | (Array.isArray(argv.e) ? argv.e : argv.e ? [argv.e] : [])
38 | .forEach(function (env_var) {
39 | if(~env_var.indexOf('=')) {
40 | var split = env_var.split('=');
41 | args.env[split[0]] = split[1];
42 | }
43 | });
44 |
45 | //
46 | // Try to get tty size to give to child process
47 | // This allows shell to be resized to whatever is the current window size
48 | //
49 | var isatty = tty.isatty(1) && tty.isatty(2)
50 | , width = isatty
51 | ? process.stdout.getWindowSize
52 | ? process.stdout.getWindowSize(1)[0]
53 | : tty.getWindowSize
54 | ? tty.getWindowSize()[1]
55 | : null
56 | : null
57 | ;
58 |
59 | //
60 | // If we found something define it as MAXCOLS internal var
61 | //
62 | if(typeof width === 'number') {
63 | args.env.SPECIFY_MAXCOLS = width;
64 | }
65 |
66 | //
67 | // This is being executed from a runner
68 | //
69 | args.env.SPECIFY_FROM_RUNNER = true;
70 |
71 | //
72 | // Set a reporter
73 | //
74 | args.env.SPECIFY_REPORTER = argv.r
75 | || process.env.SPECIFY_REPORTER
76 | || 'default';
77 |
78 | //
79 | // For spawn
80 | //
81 | args.stdio = ['pipe', 'pipe', 'pipe'];
82 |
83 | //
84 | // Recurse each file/subdirectory in our test folder
85 | //
86 | for(var dir in dirs) {
87 | //
88 | // Get that file/subdirectory path
89 | //
90 | var dir_path = path.resolve(base_path, dirs[dir]);
91 | //
92 | // If its indeed a subdirectory
93 | //
94 | if(fs.statSync(dir_path).isDirectory()) {
95 | //
96 | // Read the files in the subdirectory
97 | //
98 | files = fs.readdirSync(dir_path);
99 | //
100 | // For each file
101 | //
102 | for (var file in files) {
103 | //
104 | // Get the file path and run it if the extension is `js` or `node`
105 | //
106 | var file_path = path.resolve(dir_path, files[file]);
107 | if(/\.js$|\.node$/.test(file_path)) {
108 | //
109 | // Put the test in the stack yo.
110 | //
111 | stack.push(file_path);
112 | }
113 | }
114 | }
115 | }
116 |
117 | //
118 | // Let's start running our tests
119 | //
120 | runTests(stack, true);
121 |
122 | //
123 | // ### function runTests(tests, previousStatus)
124 | // #### @tests {Array} Tests the need some runnin
125 | // #### @previousStatus {Boolean} To set failure from a previous call
126 | //
127 | // Runs all the tests.
128 | //
129 | function runTests(tests, previousStatus) {
130 | //
131 | // Get the file name we want to run
132 | //
133 | var test_path = tests[0];
134 |
135 | //
136 | // If we have a file name
137 | //
138 | if(test_path) {
139 |
140 | //
141 | // Start a child process to run that test
142 | //
143 | var test_stream = spawn('node', [test_path], args);
144 |
145 | //
146 | // Reset our stdio buffer
147 | //
148 | buffer = '';
149 |
150 | //
151 | // Pipe the stdout from the child process to what the user can see
152 | // on his screen
153 | //
154 | test_stream.stdout.pipe(process.stdout);
155 | test_stream.stderr.pipe(process.stdout);
156 |
157 | //
158 | // Buffer the data so we can use it to make global counts across different
159 | // child processes
160 | //
161 | test_stream.stdout.on('data', function (chunk) {
162 | buffer += chunk;
163 | });
164 |
165 | //
166 | // Calculate these totals for this child
167 | //
168 | test_stream.on('close', function (exit) {
169 | //
170 | // The exit code from the child not being `0` means error
171 | //
172 | if(exit !== 0) {
173 | success = false;
174 | }
175 | //
176 | // Do the calculations, print out the results, recurse to runTests
177 | //
178 | ppTest(tests, buffer);
179 | });
180 |
181 | test_stream.on('error', function (err) {
182 | //
183 | // We failed
184 | //
185 | success = false;
186 | console.log(err);
187 | });
188 | }
189 |
190 | //
191 | // Since we have run all the tests and have no filename left
192 | // its time to run the summary of the suite
193 | //
194 | else {
195 | //
196 | // If we have data to summarize
197 | //
198 | if (counts.ok > 1 && counts.total > 1) {
199 | try {
200 | //
201 | // Get out reporter
202 | //
203 | var summarize = require('../reporters/' + args.env.SPECIFY_REPORTER);
204 | summarize('totals');
205 | summarize('summary',
206 | { ok: counts.ok
207 | , fail: counts.total-counts.ok
208 | , notrun: 0
209 | , thrown: 0
210 | , _nostdout: true
211 | });
212 | } catch (exc) {}
213 | }
214 | //
215 | // Make sure it fails if at least one test failed
216 | //
217 | return process.exit(success ? 0 : 1);
218 | }
219 | }
220 |
221 | //
222 | // ### ppTest (tests)
223 | // #### @tests {Array} Tests to prepare
224 | //
225 | // Prepate tests for `exec`
226 | //
227 | function ppTest (tests, stdout) {
228 | //
229 | // Get the path from current tests
230 | //
231 | var file_path = tests.shift();
232 |
233 | //
234 | // Update totals, if possible and provided by reporter
235 | //
236 | var lines = stdout.split('\n');
237 |
238 | //
239 | // If we actually got some new line separated test results
240 | //
241 | if(lines.length>0) {
242 | //
243 | // Get the line before the last (last is simply a new line in specify)
244 | //
245 | var last_line = lines[lines.length-2];
246 |
247 | //
248 | // If we have a count in our last line that was spit out by the test
249 | // we want to see if there's a `/`
250 | //
251 | // Like in:
252 | // /tests/shared/nano.js
253 | //
254 | // ✔ 2/2 shared_nano:test
255 | // ✔ 2/2 summary
256 | //
257 | // or
258 | // ✔ /tests/design/query.js .............................. 12/12
259 | //
260 | // we are interested in the \d+\/\d+ part of it, so lets break it there
261 | //
262 | if(!last_line) {
263 | console.log(stdout);
264 | }
265 | else if(~last_line.indexOf('/')) {
266 | //
267 | // Get the last line and try to understand if we can extract counts
268 | // like 1/5
269 | //
270 | var values = last_line.split('/');
271 |
272 | if(values.length > 1) {
273 | //
274 | // Transform values to integer. the worst way possible
275 | //
276 | // > +9
277 | // 9
278 | // > +'9'
279 | // 9
280 | // > +'9n'
281 | // NaN
282 | //
283 | // We split by space to trim out potential non numbers
284 | //
285 | // For `worked` the thing we want in the last word (length-1) on the
286 | // part of the split in the position before last (values length-2)
287 | //
288 | var worked = values[values.length-2].split(' ');
289 | worked = +worked[worked.length-1];
290 |
291 | //
292 | // For total this is the first word (0) in the last position of the
293 | // split (values length-1)
294 | //
295 | var total = +values[values.length-1].split(' ')[0];
296 |
297 | //
298 | // If these things are not numbers
299 | //
300 | if(!isNaN(worked) && !isNaN(total)) {
301 | //
302 | // Update our counts using the result from child process
303 | // When we end we can summarize all tests
304 | //
305 | counts.ok += worked;
306 | counts.total += total;
307 | //
308 | // Next!
309 | //
310 | lines.pop();
311 | }
312 | }
313 | }
314 | }
315 |
316 | //
317 | // To iterate is human, <>
318 | //
319 | runTests(tests);
320 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "specify",
3 | "description": "bite sized node.js testing",
4 | "author": "nuno job (http://nunojob.com/)",
5 | "contributors": [
6 | "David Trejo (http://dtrejo.com)",
7 | "Jarrett Cruger (https://github.com/jcrugzz)",
8 | "Isaac Z. Schlueter (http://izs.me)",
9 | "Jon Buckley (http://jbuckley.ca)"
10 | ],
11 | "version": "1.3.1",
12 | "main": "./specify.js",
13 | "homepage": "https://github.com/dscape/specify",
14 | "repository": {
15 | "type": "git",
16 | "url": "http://github.com/dscape/specify.git"
17 | },
18 | "bugs": "http://github.com/dscape/specify/issues",
19 | "keywords": [
20 | "test",
21 | "assert",
22 | "should",
23 | "tdd"
24 | ],
25 | "engines": {
26 | "node": ">=0.8.0"
27 | },
28 | "scripts": {
29 | "test": "./test.sh"
30 | },
31 | "bin": {
32 | "specify": "./bin/specify"
33 | },
34 | "dependencies": {
35 | "difflet": "0.2.x",
36 | "colors": "0.6.x",
37 | "cycle": "1.0.x",
38 | "optimist": "0.3.x",
39 | "esprima": "1.0.x"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/reporters/compact.js:
--------------------------------------------------------------------------------
1 | var colors = require('colors')
2 | , tty = require('tty')
3 | , difflet = require('difflet')
4 | , decycle = require('cycle').decycle
5 | ;
6 |
7 | var isatty = tty.isatty(1) && tty.isatty(2)
8 | , width = process.env.SPECIFY_MAXCOLS
9 | ? process.env.SPECIFY_MAXCOLS
10 | : isatty
11 | ? process.stdout.getWindowSize
12 | ? process.stdout.getWindowSize(1)[0]
13 | : tty.getWindowSize
14 | ? tty.getWindowSize()[1]
15 | : 70
16 | : 70
17 | , current_test = ""
18 | , current_errors = {}
19 | ;
20 |
21 | module.exports = function compact_reporter(name, report, errors) {
22 | // starting a new file
23 | if(typeof report === "undefined") {
24 | current_test = name;
25 | current_errors = {};
26 | } else if (name === "summary") { // finished testing
27 | var failed = report.fail + report.notrun
28 | , left = failed === 0 ? ('✔'.green) : ('✗'.red)
29 | , right = report.ok + '/' + (report.ok+failed)
30 | ;
31 | left += " " + current_test;
32 | var dots = new Array(
33 | Math.max(1, width - left.length - right.length)).join(".");
34 | console.log("%s %s %s", left, dots, right);
35 | current_test = "";
36 | current_errors = {};
37 | } else { // errors
38 |
39 | }
40 | };
--------------------------------------------------------------------------------
/reporters/default.js:
--------------------------------------------------------------------------------
1 | var colors = require('colors')
2 | , difflet = require('difflet')
3 | , decycle = require('cycle').decycle
4 | ;
5 |
6 | module.exports = function default_reporter(name, report, errors) {
7 | if(typeof report === "undefined") {
8 | console.log();
9 | console.log(" " + name);
10 | return console.log();
11 | }
12 | errors = errors || [];
13 | var failed = report.fail + report.notrun;
14 | var symbol = failed === 0 ? ('✔'.green) : ('✗'.red);
15 | var right = report.ok + '/' + (report.ok+failed);
16 | process.stdout.write(symbol + ' ');
17 | process.stdout.write(right + ' ');
18 | var d = '';
19 | if(report.duration) {
20 | d = 'took '.grey + (report.duration + 'ms').green;
21 | }
22 | console.log(name.cyan + " " + d);
23 | errors.forEach(function(err) {
24 | if(typeof err === "string") {
25 | console.log('└───── '.grey + (err || "Error"));
26 | } else {
27 | console.log('└───── '.grey + (err.msg || "Error"));
28 | process.stdout.write('❝ '.grey + err.assert + ' ');
29 | if(err.assert === "ok") {
30 | console.log(err.args[0]);
31 | } else {
32 | console.log();
33 | if(err.args[0] && err.args[1] && typeof err.args[0]==="object" &&
34 | typeof err.args[1]==="object") {
35 | console.log(
36 | difflet({ indent : 2, comment : true })
37 | .compare(err.args[0], err.args[1]));
38 | } else {
39 | var indexzero = decycle(err.args[0] || "undefined")
40 | , indexone = decycle(err.args[1] || "undefined")
41 | ;
42 | try {
43 | process.stdout.write(JSON.stringify(indexzero, null, 1).magenta);
44 | console.log((" // " +
45 | JSON.stringify(indexone, null, 1)).cyan);
46 | } catch (ex) {
47 | console.log(" // { \"circular\": \"⥁\"}".cyan);
48 | }
49 | }
50 | }
51 | }
52 | });
53 | };
--------------------------------------------------------------------------------
/reporters/json.js:
--------------------------------------------------------------------------------
1 | var decycle = require('cycle').decycle;
2 |
3 | module.exports = function json_reporter(name, report, errors) {
4 | var json_report =
5 | { name : name
6 | , report : report
7 | , errors : errors
8 | };
9 | console.log(JSON.stringify(decycle(json_report)));
10 | };
--------------------------------------------------------------------------------
/specify.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert')
2 | , domain = require('domain')
3 | , esprima = require('esprima')
4 | , path = require('path'), colors
5 | , reporters = {}
6 | , assertions =
7 | [ 'ok', 'equal', 'notEqual', 'deepEqual', 'notDeepEqual'
8 | , 'strictEqual', 'notStrictEqual', 'ifError']
9 | , err_count = 0
10 | , MAX_ERRORS = process.env.SPECIFY_MAX_ERRORS || 1000
11 | , CHECK_GLOBALS = process.env.SPECIFY_CHECK_GLOBALS ? true : false
12 | , GLOBALS = Object.keys(global)
13 | , startTime
14 | ;
15 |
16 | // read available reporters
17 | require('fs').readdirSync(path.join(__dirname, 'reporters'))
18 | .forEach(function(reporter) {
19 | reporters[reporter]=require(path.join(__dirname, 'reporters', reporter));
20 | });
21 |
22 | module.exports = (function specify() {
23 | var cache = []
24 | , counts = { _totals: {ok: 0, fail: 0, notrun: 0, thrown: 0} }
25 | , spec, summary, def_summary, timer, current_domain
26 | , default_reporter = process.env.SPECIFY_REPORTER
27 | ? Object.keys(reporters)
28 | .indexOf(process.env.SPECIFY_REPORTER + ".js") === -1
29 | ? 'default'
30 | : process.env.SPECIFY_REPORTER
31 | : 'default'
32 | ;
33 |
34 | def_summary = summary = reporters[default_reporter + '.js'];
35 |
36 | function ensure_for(test, expect, tests, done) {
37 | var ensure = {}, count = expect, errored = [];
38 | counts[test] = {ok: 0, fail: 0, notrun: 0, thrown: 0};
39 | counts[test].meta = {name: test, expected: expect, remaining: tests};
40 | counts[test].meta.errored = errored;
41 | counts[test].meta.remaining_assertions = expect;
42 |
43 | assertions.forEach(function(assertion) {
44 | ensure[assertion] = function () {
45 | if(counts[test].thrown > 0) {
46 | return;
47 | }
48 | try {
49 | assert[assertion].apply(this,arguments);
50 | counts._totals.ok++;
51 | counts[test].ok++;
52 | }
53 | catch (err) {
54 | errored.push({ msg: err.message
55 | , assert: assertion, args: [].slice.call(arguments,0)});
56 | counts._totals.fail++;
57 | counts[test].fail++;
58 | }
59 | count--;
60 | counts[test].meta.remaining_assertions = count;
61 | if(count === 0) {
62 | done(errored);
63 | }
64 | };
65 | });
66 |
67 | ensure.expect = function (nr) { count = nr; };
68 | return ensure;
69 |
70 | }
71 |
72 | function check_globals () {
73 | var end_globals = Object.keys(global)
74 | , leaks = end_globals.filter(function (eg) {
75 | return !~GLOBALS.indexOf(eg);
76 | });
77 | return leaks;
78 | }
79 |
80 | //
81 | // lifted from esprima's `detectnestedternary` example
82 | //
83 | function traverse(object, visitor) {
84 | var key
85 | , child
86 | ;
87 |
88 | visitor.call(null, object);
89 | for (key in object) {
90 | if (object.hasOwnProperty(key)) {
91 | child = object[key];
92 | if (typeof child === 'object' && child !== null) {
93 | traverse(child, visitor);
94 | }
95 | }
96 | }
97 | }
98 |
99 | function run_tests(tests) {
100 | if(timer) {
101 | clearTimeout(timer);
102 | timer = undefined;
103 | }
104 | if(tests.length === 0) {
105 | var leaks = check_globals();
106 | leaks.forEach(function (leak) {
107 | console.log('leak detected: ' + leak);
108 | });
109 | counts._totals.duration = Date.now() - startTime;
110 | summary('summary', counts._totals);
111 | process.exit(counts._totals.fail === 0 ? 0 : -1);
112 | }
113 | else {
114 | var test = tests.shift()
115 | , name = test[0]
116 | , timeout = test[1]
117 | , f = test[2]
118 | , expect
119 | ;
120 | if(typeof timeout === "function") {
121 | f = timeout;
122 | timeout = undefined;
123 | }
124 | // Need to add () so that it's a complete JS script
125 | var program = esprima.parse("(" + f.toString() + ")")
126 | , vari
127 | ;
128 |
129 | // Get argument identifiers from the function passed to specify
130 | if (program.body[0].expression.type === "FunctionExpression") {
131 | vari = program.body[0].expression.params.map(function(value) {
132 | return value.name;
133 | });
134 | }
135 |
136 | if(Array.isArray(vari) && vari.length > 0) {
137 | // Traverse program looking for assert.* function calls
138 | var numberOfAsserts = 0
139 | , expected = false
140 | ;
141 | traverse(program, function(node) {
142 | if (node.type === 'MemberExpression' &&
143 | node.object.name === vari[0] &&
144 | assertions.indexOf(node.property.name) !== -1) {
145 | numberOfAsserts++;
146 | }
147 | else if (node.type === 'MemberExpression' &&
148 | node.object.name === vari[0] &&
149 | node.property.name === 'expect') {
150 | // will be specified later on but before nextTick
151 | expected=true;
152 | }
153 | });
154 |
155 | if(numberOfAsserts || expected) {
156 | expect = numberOfAsserts;
157 | current_domain = domain.create();
158 | current_domain.on('error', domainHandler(name));
159 | return current_domain.run(function () {
160 | if(timeout) {
161 | timer = setTimeout(function (){
162 | throw new Error("Timeout");
163 | }, timeout);
164 | }
165 | process.nextTick(function (){
166 | var start = Date.now();
167 | f(ensure_for(name, expect, tests, function (errors) {
168 | counts[name].duration = Date.now() - start;
169 | summary(name, counts[name], errors);
170 | run_tests(tests);
171 | }));
172 | });
173 | });
174 | } else {
175 | summary(name, {ok: 0, fail: 1, notrun: 0, thrown: 0},
176 | [' you need to add at least one `'+ vari[0] + '.*` call']);
177 | }
178 | } else {
179 | summary(name, {ok: 0, fail: 1, notrun: 0, thrown: 0},
180 | [' `assert` must be the first argument of your callback']);
181 | }
182 | counts._totals.fail++;
183 | run_tests(tests);
184 | }
185 | }
186 |
187 | spec = function specify_test(name, f) {
188 | cache.push([].slice.call(arguments,0));
189 | };
190 |
191 | spec.reporter = function (f) {
192 | if (typeof f === 'function') {
193 | summary = f;
194 | return;
195 | }
196 | else if (typeof f === 'string') {
197 | var reporter = reporters[f + '.js'];
198 | if(typeof reporter === 'function') {
199 | summary = reporter;
200 | return;
201 | }
202 | }
203 | summary = def_summary;
204 | };
205 |
206 | spec.run = function run_all_tests(filter) {
207 | if(typeof filter === "function") {
208 | cache = [["main", filter]];
209 | filter = [];
210 | }
211 | summary(module.parent.filename.replace(process.cwd(), ""));
212 | startTime = Date.now();
213 | filter = typeof filter === "string" ? [filter] : filter;
214 | if(filter && filter.length !== 0) {
215 | var filtered_cache = [];
216 | filter.forEach(function (e) {
217 | cache.forEach(function (c){
218 | var name = c[0];
219 | if(name===e) filtered_cache.push(c);
220 | });
221 | });
222 | run_tests(filtered_cache);
223 | }
224 | else {
225 | run_tests(cache);
226 | }
227 | };
228 |
229 | function domainHandler(test_name) {
230 | return function (err) {
231 | var current_test = counts[test_name].meta;
232 | if(counts[test_name].thrown > 0) {
233 | // ignore, this test already thrown at least once
234 | // lets just wait for the socket to c
235 | return;
236 | }
237 | err_count++;
238 | if(MAX_ERRORS === err_count) {
239 | err.message = "You have reached " + MAX_ERRORS +
240 | " errors so we decided to abort your tests\noriginal: " +
241 | err.message;
242 | throw err;
243 | }
244 | err = typeof err === "string" ? new Error(err) : err; // idiotpatching
245 | err.stacktrace = err.stack.split("\n").splice(1)
246 | .map(function (l) { return l.replace(/^\s+/,""); });
247 | counts[current_test.name].notrun += current_test.remaining_assertions;
248 | counts._totals.notrun += current_test.remaining_assertions;
249 | counts[current_test.name].thrown++;
250 | counts._totals.thrown++;
251 | current_test.errored.push(
252 | {msg: err.message || err, assert: "equal", args: ["domain", err]});
253 | summary(current_test.name, counts[current_test.name]
254 | , current_test.errored);
255 | run_tests(current_test.remaining);
256 | };
257 | }
258 |
259 | return spec;
260 | })();
261 |
--------------------------------------------------------------------------------
/test.sh:
--------------------------------------------------------------------------------
1 | node test/specify.js \
2 | | sed 's/.\[[0-9][0-9]m//g' \
3 | | sed 's/"at.*\(.*\)//' \
4 | | sed 's/took.*ms//' \
5 | | sed 's/"duration":[0-9]*/"duration":0/' \
6 | | sed 's/"_idleStart".*//' \
7 | | sed '/^[ \t]*$/d' \
8 | > test/all.log
9 | node test/specify.js specify#throws specify#cascading_sync \
10 | | sed 's/.\[[0-9][0-9]m//g' \
11 | | sed 's/"at.*\(.*\)//' \
12 | | sed 's/took.*ms//' \
13 | | sed 's/"duration":[0-9]*/"duration":0/' \
14 | | sed 's/"_idleStart".*//' \
15 | | sed '/^[ \t]*$/d' \
16 | > test/filters.log
17 | node test/specify/singletest.js \
18 | | sed 's/.\[[0-9][0-9]m//g' \
19 | | sed 's/took.*ms//' \
20 | | sed 's/"duration":[0-9]*/"duration":0/' \
21 | | sed 's/"_idleStart".*//' \
22 | | sed '/^[ \t]*$/d' \
23 | > test/single.log
24 | node bin/specify -e SPECIFY_REPORTER=compact \
25 | > test/reporter.log
26 | node bin/specify -r compact \
27 | > test/reporter2.log
28 | node bin/specify -r default \
29 | | sed 's/.\[[0-9][0-9]m//g' \
30 | | sed 's/"at.*\(.*\)//' \
31 | | sed 's/took.*ms//' \
32 | | sed 's/"duration":[0-9]*/"duration":0/' \
33 | | sed 's/"_idleStart".*//' \
34 | | sed '/^[ \t]*$/d' \
35 | > test/default_reporter.log
36 | echo "all#1"
37 | diff test/all.log test/fixtures/all.txt
38 | if [ $? -eq 0 ]; then
39 | echo "filters#2"
40 | diff test/filters.log test/fixtures/filters.txt
41 | if [ $? -eq 0 ]; then
42 | echo "single#3"
43 | diff test/single.log test/fixtures/single.txt
44 | if [ $? -eq 0 ]; then
45 | echo "reporter#4"
46 | diff test/reporter.log test/fixtures/reporter.txt
47 | if [ $? -eq 0 ]; then
48 | echo "reporter_alt#5"
49 | diff test/reporter.log test/reporter2.log
50 | if [ $? -eq 0 ]; then
51 | echo "default_reporter#6"
52 | diff test/default_reporter.log test/fixtures/default_reporter.txt
53 | if [ $? -eq 0 ]; then
54 | echo "ok";
55 | else
56 | exit 1;
57 | fi
58 | else
59 | exit 1;
60 | fi
61 | else
62 | exit 1;
63 | fi
64 | else
65 | exit 1;
66 | fi
67 | else
68 | exit 1;
69 | fi
70 | else
71 | exit 1;
72 | fi
--------------------------------------------------------------------------------
/test/fixtures/all.txt:
--------------------------------------------------------------------------------
1 | /test/specify.js
2 | ✗ 0/1 specify:no_arguments_in_cb
3 | └───── `assert` must be the first argument of your callback
4 | ✗ 0/1 specify#no_assertions
5 | └───── you need to add at least one `assert.*` call
6 | ✔ 1/1 specify#dinosaurs
7 | ✗ 0/1 specify#wrong_var
8 | └───── you need to add at least one `dino.*` call
9 | ✔ 1/1 specify#sync
10 | ✔ 7/7 specify#all_assertions
11 | ✗ 1/3 specify#assertion_with_optional_message
12 | └───── this is the error you are looking for
13 | ❝ ok false
14 | └───── this will
15 | ❝ ok false
16 | specify#custom_pretty_print :: [{"msg":"i see dead people","assert":"ok","args":[false,"i see dead people"]}]
17 | ✗ 0/1 specify#ask_for_a_specific_reporter
18 | └───── back to default
19 | ❝ ok false
20 | specify#custom_pretty_print_just_name
21 | ✔ 1/1 specify#async
22 | ✗ 1/2 specify#timeout
23 | └───── Timeout
24 | ❝ equal
25 | "domain" // {
26 | "domain_thrown": true,
27 | "domain": {
28 | "members": [],
29 | "_events": {}
30 | },
31 | "stacktrace": [
32 | ]
33 | }
34 | ✔ 2/2 specify#timeout_after
35 | ✔ 5/5 specify#more_assertions_than_asserts
36 | ✗ 0/1 specify#differences:ok
37 | └───── Should be true
38 | ❝ ok false
39 | ✗ 0/1 specify#differences:equal
40 | └───── One is love
41 | ❝ equal
42 | 13 // 1
43 | ✗ 0/1 specify#differences:equal_undefined
44 | └───── One is love
45 | ❝ equal
46 | "undefined" // 1
47 | ✗ 0/1 specify#differences:notequal
48 | └───── One two
49 | ❝ notEqual
50 | 2 // 2
51 | ✗ 0/1 specify#differences:deepequal
52 | └───── Blooper
53 | ❝ deepEqual
54 | {
55 | "a" : {
56 | "b" : [1m3[0m[1m // != 1[0m
57 | }
58 | }
59 | ✗ 0/1 specify#differences:notdeepequal
60 | └───── Not Deep
61 | ❝ notDeepEqual
62 | {
63 | "a" : 1
64 | }
65 | ✗ 0/1 specify#differences:strictequal
66 | └───── Dont be like that
67 | ❝ strictEqual
68 | 5 // 3
69 | ✗ 0/1 specify#differences:notstrictequal
70 | └───── 3 4 knock on the door
71 | ❝ notStrictEqual
72 | 4 // 4
73 | ✗ 1/5 specify#circular_reference
74 | └───── Error
75 | ❝ equal
76 | {
77 | "abc": "Hello",
78 | "go": {
79 | "$ref": "$"
80 | }
81 | } // "undefined"
82 | └───── Error
83 | ❝ equal
84 | "undefined" // {
85 | "abc": "Hello",
86 | "go": {
87 | "$ref": "$"
88 | }
89 | }
90 | └───── Error
91 | ❝ equal
92 | {[1m
93 | "abc" : "Hello",[0m[1m // != undefined[0m[1m
94 | "go" : {
95 | }[0m[1m // != undefined[0m
96 | }
97 | └───── Error
98 | ❝ equal
99 | {
100 | [1m[0m[1m// [0m[1m"abc" : "Hello",
101 | [0m[1m// [0m[1m"go" : {"abc":"Hello","go":{}}[0m
102 | }
103 | ✗ 0/2 specify#cascading_sync
104 | └───── No error
105 | ❝ ok false
106 | └───── Cannot read property 'name' of undefined
107 | ❝ equal
108 | "domain" // {
109 | "domain_thrown": true,
110 | "domain": {
111 | "members": [],
112 | "_events": {}
113 | },
114 | "stacktrace": [
115 | ]
116 | }
117 | ✗ 0/1 specify#throws
118 | └───── bla
119 | ❝ equal
120 | "domain" // {
121 | "stacktrace": [
122 | ]
123 | }
124 | {"name":"specify#json_reporter","report":{"ok":1,"fail":0,"notrun":0,"thrown":0,"meta":{"name":"specify#json_reporter","expected":1,"remaining":[["specify#comments",null],["specify#comments_arent_detected",null]],"errored":[],"remaining_assertions":0},"duration":0},"errors":{"$ref":"$[\"report\"][\"meta\"][\"errored\"]"}}
125 | {"name":"specify#comments","report":{"ok":2,"fail":0,"notrun":0,"thrown":0,"meta":{"name":"specify#comments","expected":2,"remaining":[["specify#comments_arent_detected",null]],"errored":[],"remaining_assertions":0},"duration":0},"errors":{"$ref":"$[\"report\"][\"meta\"][\"errored\"]"}}
126 | {"name":"specify#comments_arent_detected","report":{"ok":1,"fail":0,"notrun":0,"thrown":0,"meta":{"name":"specify#comments_arent_detected","expected":1,"remaining":[],"errored":[],"remaining_assertions":0},"duration":0},"errors":{"$ref":"$[\"report\"][\"meta\"][\"errored\"]"}}
127 | {"name":"summary","report":{"ok":25,"fail":21,"notrun":3,"thrown":3,"duration":0}}
128 |
--------------------------------------------------------------------------------
/test/fixtures/default_reporter.txt:
--------------------------------------------------------------------------------
1 | /test/specify/compact.js
2 | ✗ 1/5 specify#circular_reference
3 | └───── Error
4 | ❝ equal
5 | {
6 | "abc": "Hello",
7 | "go": {
8 | "$ref": "$"
9 | }
10 | } // "undefined"
11 | └───── Error
12 | ❝ equal
13 | "undefined" // {
14 | "abc": "Hello",
15 | "go": {
16 | "$ref": "$"
17 | }
18 | }
19 | └───── Error
20 | ❝ equal
21 | {[1m
22 | "abc" : "Hello",[0m[1m // != undefined[0m[1m
23 | "go" : {
24 | }[0m[1m // != undefined[0m
25 | }
26 | └───── Error
27 | ❝ equal
28 | {
29 | [1m[0m[1m// [0m[1m"abc" : "Hello",
30 | [0m[1m// [0m[1m"go" : {"abc":"Hello","go":{}}[0m
31 | }
32 | ✗ 0/2 specify#cascading_sync
33 | └───── No error
34 | ❝ ok false
35 | └───── Cannot read property 'name' of undefined
36 | ❝ equal
37 | "domain" // {
38 | "domain_thrown": true,
39 | "domain": {
40 | "members": [],
41 | "_events": {}
42 | },
43 | "stacktrace": [
44 | ]
45 | }
46 | ✗ 1/2 specify#cascading_sync_first_works
47 | └───── Cannot read property 'name' of undefined
48 | ❝ equal
49 | "domain" // {
50 | "domain_thrown": true,
51 | "domain": {
52 | "members": [],
53 | "_events": {}
54 | },
55 | "stacktrace": [
56 | ]
57 | }
58 | ✗ 0/2 specify#does_this_run
59 | └───── Cannot read property 'name' of undefined
60 | ❝ equal
61 | "domain" // {
62 | "domain_thrown": true,
63 | "domain": {
64 | "members": [],
65 | "_events": {}
66 | },
67 | "stacktrace": [
68 | ]
69 | }
70 | ✗ 0/3 specify#they_all_blow
71 | └───── No error
72 | ❝ ok false
73 | └───── Cannot read property 'foobar' of undefined
74 | ❝ equal
75 | "domain" // {
76 | "domain_thrown": true,
77 | "domain": {
78 | "members": [],
79 | "_events": {}
80 | },
81 | "stacktrace": [
82 | ]
83 | }
84 | ✗ 0/1 specify#throws
85 | └───── bla
86 | ❝ equal
87 | "domain" // {
88 | "stacktrace": [
89 | ]
90 | }
91 | ✗ 2/15 summary
92 | /test/specify/parallel.js
93 | ✗ 0/6 specify#parallel_uncaught
94 | └───── Error
95 | ❝ ok false
96 | └───── Cannot read property 'dog' of undefined
97 | ❝ equal
98 | "domain" // {
99 | "domain_thrown": true,
100 | "domain": {
101 | "members": [],
102 | "_events": {}
103 | },
104 | "stacktrace": [
105 | ]
106 | }
107 | ✗ 0/2 specify#also_throws
108 | └───── Error
109 | ❝ ok false
110 | └───── Cannot read property 'mouse' of undefined
111 | ❝ equal
112 | "domain" // {
113 | "domain_thrown": true,
114 | "domain": {
115 | "members": [],
116 | "_events": {}
117 | },
118 | "stacktrace": [
119 | ]
120 | }
121 | ✔ 1/1 specify#ok
122 | ✗ 1/9 summary
123 | /test/specify/singletest.js
124 | ✔ 1/1 main
125 | leak detected: LEAKING
126 | ✔ 1/1 summary
127 | totals
128 | ✗ 4/25 summary
129 |
--------------------------------------------------------------------------------
/test/fixtures/filters.txt:
--------------------------------------------------------------------------------
1 | /test/specify.js
2 | ✗ 0/1 specify#throws
3 | └───── bla
4 | ❝ equal
5 | "domain" // {
6 | "stacktrace": [
7 | ]
8 | }
9 | ✗ 0/2 specify#cascading_sync
10 | └───── No error
11 | ❝ ok false
12 | └───── Cannot read property 'name' of undefined
13 | ❝ equal
14 | "domain" // {
15 | "domain_thrown": true,
16 | "domain": {
17 | "members": [],
18 | "_events": {}
19 | },
20 | "stacktrace": [
21 | ]
22 | }
23 | ✗ 0/3 summary
24 |
--------------------------------------------------------------------------------
/test/fixtures/reporter.txt:
--------------------------------------------------------------------------------
1 | [31m✗[39m /test/specify/compact.js ............................. 2/15
2 | [31m✗[39m /test/specify/parallel.js ............................. 1/9
3 | leak detected: LEAKING
4 | [32m✔[39m /test/specify/singletest.js ........................... 1/1
5 | [31m✗[39m totals ............................................... 4/25
6 |
--------------------------------------------------------------------------------
/test/fixtures/single.txt:
--------------------------------------------------------------------------------
1 | /test/specify/singletest.js
2 | ✔ 1/1 main
3 | leak detected: LEAKING
4 | ✔ 1/1 summary
5 |
--------------------------------------------------------------------------------
/test/specify.js:
--------------------------------------------------------------------------------
1 | var specify = require('../specify')
2 | , filters = process.argv.slice(2)
3 | ;
4 |
5 | specify('specify:no_arguments_in_cb', function() {});
6 |
7 | specify('specify#no_assertions', function (assert) {
8 | return;
9 | });
10 |
11 | specify('specify#dinosaurs', function(dino) {
12 | dino.ok({trex:true});
13 | });
14 |
15 | specify('specify#wrong_var', function(dino) {
16 | assert.ok({trex:"sad"});
17 | });
18 |
19 | specify('specify#sync', function(assert) {
20 | assert.ok(true);
21 | });
22 |
23 | specify('specify#all_assertions', function(assert) {
24 | assert.ok(true);
25 | assert.equal(1,1);
26 | assert.notEqual(1,2);
27 | assert.deepEqual({a:1}, {a:1});
28 | assert.notDeepEqual({a:1}, {a:2});
29 | assert.strictEqual(3, 3);
30 | assert.notStrictEqual(3, 4);
31 | });
32 |
33 | specify('specify#assertion_with_optional_message', function(assert) {
34 | assert.ok(false, "this is the error you are looking for");
35 | assert.ok(true, "this won't appear");
36 | assert.ok(false, "this will");
37 | });
38 |
39 | specify('specify#custom_pretty_print', function(assert) {
40 | // set a custom reporter pretty print function
41 | specify.reporter(function (name, report, errors) {
42 | console.log(name + ' :: ' + JSON.stringify(errors));
43 | });
44 | assert.ok(false, 'i see dead people');
45 | });
46 |
47 | specify('specify#ask_for_a_specific_reporter', function(assert) {
48 | // reset reporter function
49 | specify.reporter('default');
50 | assert.ok(false, 'back to default');
51 | });
52 |
53 | specify('specify#custom_pretty_print_just_name', function(assert) {
54 | // set a custom repoter pretty print function
55 | specify.reporter(function (name, report, errors) {
56 | console.log(name);
57 | });
58 | assert.ok(false, 'i see dead people');
59 | assert.ok(true);
60 | });
61 |
62 | specify('specify#async', function(assert) {
63 | // reset reporter function
64 | specify.reporter();
65 | setTimeout(function () {
66 | assert.ok(true, "was true");
67 | }, 1);
68 | });
69 |
70 | specify('specify#timeout', 50, function(assert) {
71 | assert.ok(true);
72 | setTimeout(function () {
73 | assert.ok(true, "was true");
74 | }, 100);
75 | });
76 |
77 | specify('specify#timeout_after', 100, function(assert) {
78 | assert.ok(true);
79 | setTimeout(function () {
80 | assert.ok(true, "was true");
81 | }, 10);
82 | });
83 |
84 | specify('specify#more_assertions_than_asserts', function(assert) {
85 | assert.expect(5);
86 | for(var i in [1,2,3,4,5]) {
87 | assert.equal(i,i);
88 | }
89 | });
90 |
91 | specify('specify#differences:ok', function(assert) {
92 | assert.ok(false, "Should be true");
93 | });
94 |
95 | specify('specify#differences:equal', function(assert) {
96 | assert.equal(13,1, "One is love");
97 | });
98 |
99 | specify('specify#differences:equal_undefined', function(assert) {
100 | assert.equal(undefined,1, "One is love");
101 | });
102 |
103 | specify('specify#differences:notequal', function(assert) {
104 | assert.notEqual(2,2, "One two");
105 | });
106 |
107 | specify('specify#differences:deepequal', function(assert) {
108 | assert.deepEqual({a: {b: 1}}, {a: {b: 3}}, "Blooper");
109 | });
110 |
111 | specify('specify#differences:notdeepequal', function(assert) {
112 | assert.notDeepEqual({a:1}, {a:1}, "Not Deep");
113 | });
114 |
115 | specify('specify#differences:strictequal', function(assert) {
116 | assert.strictEqual(5, 3, "Dont be like that");
117 | });
118 |
119 | specify('specify#differences:notstrictequal', function(assert) {
120 | assert.notStrictEqual(4, 4, "3 4 knock on the door");
121 | });
122 |
123 | specify('specify#circular_reference', function(assert) {
124 | function foo() {
125 | this.abc = "Hello"; this.go = this;
126 | return this;
127 | }
128 | var c = new foo();
129 | assert.equal(c,c);
130 | assert.equal(c,undefined);
131 | assert.equal(undefined,c);
132 | assert.equal({},c);
133 | assert.equal(c,{});
134 | });
135 |
136 | specify('specify#cascading_sync', function(assert) {
137 | var err = new Error("Testing")
138 | , body
139 | ;
140 | assert.ok(!err, "No error");
141 | assert.equal(body.name, "Body has name");
142 | });
143 |
144 | specify('specify#throws', function(assert) {
145 | throw "bla";
146 | assert.ok(true);
147 | });
148 |
149 | specify('specify#json_reporter', function (assert) {
150 | specify.reporter('json');
151 | assert.ok(true);
152 | });
153 |
154 | specify('specify#comments', function (assert) {
155 | assert.expect(2);
156 | assert.ok(true);
157 | //assert.ok(false);
158 | assert.ok(true);
159 | });
160 |
161 | specify('specify#comments_arent_detected', function (assert) {
162 | assert.ok(true);
163 | //assert.ok(true);
164 | /*assert.ok(true);*/
165 | });
166 |
167 | specify.run(filters);
168 |
--------------------------------------------------------------------------------
/test/specify/compact.js:
--------------------------------------------------------------------------------
1 | var specify = require('../../specify');
2 |
3 | specify('specify#circular_reference', function(assert) {
4 | function foo() {
5 | this.abc = "Hello"; this.go = this;
6 | return this;
7 | }
8 | var c = new foo();
9 | assert.equal(c,c);
10 | assert.equal(c,undefined);
11 | assert.equal(undefined,c);
12 | assert.equal({},c);
13 | assert.equal(c,{});
14 | });
15 |
16 | specify('specify#cascading_sync', function(assert) {
17 | var err = new Error("Testing")
18 | , body
19 | ;
20 | assert.ok(!err, "No error");
21 | assert.equal(body.name, "Body has name");
22 | });
23 |
24 | specify('specify#cascading_sync_first_works', function(assert) {
25 | var err
26 | , body
27 | ;
28 | assert.ok(!err, "No error");
29 | assert.equal(body.name, "Body has name");
30 | });
31 |
32 |
33 | specify('specify#does_this_run', function(assert) {
34 | var err = new Error("Testing")
35 | , body
36 | ;
37 | assert.equal(body.name, "Body has name");
38 | setTimeout(function() { assert.ok(!err, "No error"); }, 10);
39 | });
40 |
41 | specify('specify#they_all_blow', function(assert) {
42 | var err = new Error("Testing")
43 | , body
44 | ;
45 | assert.ok(!err, "No error");
46 | assert.equal(body.foobar, "Body has foobar");
47 | assert.equal(body.name, "Body has name");
48 | });
49 |
50 | specify('specify#throws', function(assert) {
51 | throw "bla";
52 | assert.ok(true);
53 | });
54 |
55 | specify.run();
--------------------------------------------------------------------------------
/test/specify/parallel.js:
--------------------------------------------------------------------------------
1 | var specify = require('../../specify')
2 | , error = new Error("Sockets. Not really")
3 | ;
4 |
5 | specify('specify#parallel_uncaught', 50, function(assert) {
6 | // both throw, in different times, same test
7 | setTimeout(function (restaurant) {
8 | assert.ok(!error);
9 | assert.ok(restaurant.dog);
10 | assert.ok(false);
11 | }, 10);
12 | setTimeout(function (bar) {
13 | assert.ok(!error);
14 | assert.ok(bar.cat);
15 | assert.ok(true);
16 | }, 20);
17 | });
18 |
19 | specify('specify#also_throws', 50, function(assert) {
20 | setTimeout(function (coffeeplace) {
21 | assert.ok(!error);
22 | assert.ok(coffeeplace.mouse);
23 | }, 10);
24 | });
25 |
26 | specify('specify#ok', 50, function(assert) {
27 | assert.ok(true);
28 | });
29 |
30 | specify.run();
--------------------------------------------------------------------------------
/test/specify/singletest.js:
--------------------------------------------------------------------------------
1 | var specify = require('../../specify');
2 |
3 | specify.run(
4 | function(assert) {
5 | LEAKING = true;
6 | setTimeout(function () { assert.ok(true, "was true"); }, 1);
7 | // assert.ok(true, 'this is a comment')
8 | /*
9 | assert.ok('multiline too');
10 | */
11 | }
12 | );
--------------------------------------------------------------------------------