├── .gitignore ├── .npmignore ├── src ├── wru.phantom.js ├── wru.DOM.cursor.js ├── wru.shared.js ├── wru.DOM.log.js ├── wru.console.cursor.js ├── wru.DOM.variables.js ├── wru.debug.js ├── wru.console.variables.js ├── wru.console.log.js ├── wru.DOM.node.js ├── wru.global.shortcuts.js ├── wru.outro.js ├── template.css ├── template.js ├── wru.functions.js ├── wru.export.js ├── wru.variables.js ├── template.html ├── wru.intro.js ├── program.js ├── test.html ├── rhinoTimers.js ├── wru.DOM.functions.js ├── wru.console.functions.js └── wru.js ├── Makefile ├── test ├── test.html ├── base.js ├── phantom.js ├── testnode.js ├── testrhino.js ├── test.js └── solutions.html ├── package.json ├── LICENSE.txt ├── node ├── program.js └── wru.console.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | builder/* 2 | test/* 3 | src/* 4 | Makefile -------------------------------------------------------------------------------- /src/wru.phantom.js: -------------------------------------------------------------------------------- 1 | window.phantomExit=false; 2 | window.quit=function(){window.phantomExit=true}; 3 | window.require=function(){return{wru:window.wru}}; 4 | window.global=window; -------------------------------------------------------------------------------- /src/wru.DOM.cursor.js: -------------------------------------------------------------------------------- 1 | // "THE CURSOR" http://3site.eu/cursor 2 | window.setInterval(function () { 3 | waitForIt && passTheInfo(charAt.call(cursor, ci++%4)); 4 | }, TIMEOUT); 5 | -------------------------------------------------------------------------------- /src/wru.shared.js: -------------------------------------------------------------------------------- 1 | 2 | // shared across the whole private scope 3 | Math, abs, random, setTimeout, clearTimeout, 4 | current, node, pass, fail, fatal, tmp, called 5 | ; 6 | -------------------------------------------------------------------------------- /src/wru.DOM.log.js: -------------------------------------------------------------------------------- 1 | 2 | wru.log = function log(obj, alertOnly) { 3 | alertOnly ? 4 | alert(obj) : 5 | (typeof console != "undefined") && console.log(obj) 6 | ; 7 | }; 8 | -------------------------------------------------------------------------------- /src/wru.console.cursor.js: -------------------------------------------------------------------------------- 1 | // "THE CURSOR" http://3site.eu/cursor 2 | window.setInterval(function () { 3 | waitForIt && log(EMPTY + charAt.call(cursor, ci++%4) + "\b\b", true); 4 | }, TIMEOUT); 5 | -------------------------------------------------------------------------------- /src/wru.DOM.variables.js: -------------------------------------------------------------------------------- 1 | 2 | // these variables are used on DOM version only 3 | INNERHTML = "innerHTML", 4 | document = window.document, 5 | createElement = document.createElement, 6 | -------------------------------------------------------------------------------- /src/wru.debug.js: -------------------------------------------------------------------------------- 1 | 2 | //^ this is useful to test internals on non minified version 3 | wru.debug = function (O_o) { 4 | return eval("(" + O_o + ")"); 5 | }; 6 | //$ and this block is removed at build time 7 | -------------------------------------------------------------------------------- /src/wru.console.variables.js: -------------------------------------------------------------------------------- 1 | 2 | // these variables are used on console version only 3 | ERROR = "\x1B[1;31mERROR\x1B[0m", 4 | FAILURE = "\x1B[0;31mFAILURE\x1B[0m", 5 | OK = "\x1B[0;32mOK\x1B[0m", 6 | OUTPUT_SEPARATOR = "------------------------------", -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean test 2 | 3 | # default build task 4 | build: 5 | mkdir -p build 6 | node builder/build.js 7 | make test 8 | 9 | # clean/remove build folder 10 | clean: 11 | rm -rf build 12 | 13 | # test build folder 14 | test: 15 | node node/program.js test/base.js 16 | -------------------------------------------------------------------------------- /src/wru.console.log.js: -------------------------------------------------------------------------------- 1 | 2 | wru.log = function (obj, printOnly) { 3 | try { 4 | if (printOnly) { 5 | throw new Error; 6 | } 7 | console.log(obj); 8 | } catch(o_O) { 9 | log(obj, 0); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /src/wru.DOM.node.js: -------------------------------------------------------------------------------- 1 | 2 | // the default node to use for tests 3 | // feel free to specify another one 4 | // before wru.test() call 5 | wru.node = ( 6 | document.getElementById("wru") || 7 | document.body || 8 | document.documentElement 9 | ); 10 | -------------------------------------------------------------------------------- /src/wru.global.shortcuts.js: -------------------------------------------------------------------------------- 1 | // these are window/global object dependent 2 | // must be eventually defined after wru.export.js, if used 3 | Math = window.Math; 4 | abs = Math.abs; 5 | random = Math.random; 6 | setTimeout = window.setTimeout; 7 | clearTimeout = window.clearTimeout; 8 | -------------------------------------------------------------------------------- /test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | wru :: essential unit test framework 5 | 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /src/wru.outro.js: -------------------------------------------------------------------------------- 1 | 2 | TIMEOUT *= TIMEOUT; // by default, timeout is 10000 (10 seconds) 3 | // this is the place you can set it, e.g. 4 | // TIMEOUT = 2000; // 2 seconds 5 | 6 | wru.random = FALSE; // by default tests order is preseverd 7 | // set wru.random = TRUE to randomly sort them 8 | 9 | return wru; 10 | 11 | }(this); -------------------------------------------------------------------------------- /src/template.css: -------------------------------------------------------------------------------- 1 | #wru { 2 | font-family: sans-serif; 3 | font-size: 11pt; 4 | border: 1px solid #333; 5 | } 6 | #wru div { 7 | cursor: default; 8 | padding: 0; 9 | color: #000; 10 | } 11 | #wru div span, 12 | #wru div strong { 13 | display: block; 14 | padding: 4px; 15 | margin: 0; 16 | } 17 | #wru div ul { 18 | margin: 0; 19 | padding-bottom: 4px; 20 | } 21 | #wru div.pass { 22 | background: #90EE90; 23 | } 24 | #wru div.fail { 25 | background: #FF6347; 26 | } 27 | #wru div.error { 28 | background: #000; 29 | color: #FFF; 30 | } -------------------------------------------------------------------------------- /src/template.js: -------------------------------------------------------------------------------- 1 | // https://github.com/WebReflection/wru 2 | function wru(wru){var assert=wru.assert,async=wru.async,log=wru.log; 3 | 4 | // enojy your tests! 5 | 6 | 7 | 8 | wru.test([ 9 | { 10 | name: "it works!", 11 | test: function () { 12 | // sync 13 | wru.assert(1); 14 | 15 | // async 16 | setTimeout(async(function () { // wru.async 17 | assert("called"); // wru.assert 18 | }), 500); 19 | } 20 | } 21 | ]); 22 | 23 | 24 | 25 | } 26 | 27 | 28 | 29 | 30 | 31 | // wru related code 32 | {{JS}} -------------------------------------------------------------------------------- /test/base.js: -------------------------------------------------------------------------------- 1 | test([{ 2 | name: "base 1", 3 | test: function () { 4 | setTimeout(async(function () { 5 | assert("OK"); 6 | })); 7 | } 8 | }]); 9 | wru.test([{ 10 | name: "base 2", 11 | test: function () { 12 | wru.assert("OK"); 13 | } 14 | }]); 15 | wru.test([{ 16 | setup: function (tmp) { 17 | tmp.timeout = wru.timeout; 18 | wru.timeout = 5000; 19 | }, 20 | name: "base 3", 21 | test: function () { 22 | setTimeout(wru.async(function(){ 23 | wru.assert(true); 24 | }), 4500); 25 | }, 26 | teardown: function (tmp) { 27 | wru.timeout = tmp.timeout; 28 | } 29 | }]); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wru" 3 | , "version": "0.3.0" 4 | , "description": "sync and async JavaScript tests have never been that easy" 5 | , "homepage": "https://github.com/WebReflection/wru" 6 | , "keywords": ["test", "general", "purpose", "async", "sync", "asynchronous", "phantom", "rhino", "browser"] 7 | , "author": { 8 | "name": "Andrea Giammarchi", 9 | "web": "http://webreflection.blogspot.com/" 10 | } 11 | , "repository":{ 12 | "type": "git" 13 | , "url": "https://github.com/WebReflection/wru.git" 14 | } 15 | , "main": "./node/wru.console.js" 16 | , "engines": { "node": ">= 0.4.0" } 17 | , "bin": { 18 | "wru": "./node/program.js" 19 | } 20 | } -------------------------------------------------------------------------------- /src/wru.functions.js: -------------------------------------------------------------------------------- 1 | // common functions for all versions 2 | function giveItATry(name) { 3 | if (iHasIt(current, name)) { 4 | try { 5 | current[name](tmp); 6 | } catch(doooodeThisIsBAD) { 7 | push.call(fatal, EMPTY + doooodeThisIsBAD); 8 | } 9 | } 10 | } 11 | 12 | function iHasIt(object, name) { 13 | return hasOwnProperty.call(object, name); 14 | } 15 | 16 | function messItUp() { 17 | return random() < .5 ? -1 : 1; 18 | } 19 | 20 | function clearDaryTimeou() { 21 | if (daryTimeout) { 22 | clearTimeout(daryTimeout); 23 | daryTimeout = 0; 24 | } 25 | giveItATry("teardown"); 26 | } 27 | -------------------------------------------------------------------------------- /src/wru.export.js: -------------------------------------------------------------------------------- 1 | // node.js exports 2 | if (typeof __dirname != "undefined") { 3 | 4 | window.wru = wru; 5 | window.assert = wru.assert; 6 | window.async = wru.async; 7 | window.test = wru.test; 8 | window.log = wru.log; 9 | window.random = false; 10 | Object.defineProperty(window, "status", {get: function () { 11 | return wru.status; 12 | }}); 13 | Object.defineProperty(window, "timeout", { 14 | get: function () { 15 | return wru.timeout; 16 | }, 17 | set: function (value) { 18 | wru.timeout = parseInt(value, 10) || wru.timeout; 19 | } 20 | }); 21 | 22 | // re-assign window to make it global 23 | window = global; 24 | } 25 | -------------------------------------------------------------------------------- /src/wru.variables.js: -------------------------------------------------------------------------------- 1 | // common private variables / constants / shortcuts 2 | TRUE = true, 3 | FALSE = !TRUE, 4 | TIMEOUT = 100, 5 | EMPTY = " ", 6 | UNKNOWN = "unknown", 7 | LENGTH = "length", 8 | NAME = "name", 9 | DESCRIPTION = "description", 10 | LISTART = "
  • ", 11 | LIEND = "
  • ", 12 | cursor = "\\|/-", 13 | hasOwnProperty = wru.hasOwnProperty, 14 | prefix = EMPTY, 15 | charAt = prefix.charAt, 16 | slice = prefix.slice, 17 | queue = [], 18 | concat = queue.concat, 19 | join = queue.join, 20 | push = queue.push, 21 | shift = queue.shift, 22 | sort = queue.sort, 23 | waitForIt = 0, 24 | ci = 0, 25 | overallPass = 0, 26 | overallFail = 0, 27 | overallFatal = 0, 28 | daryTimeout = 0, 29 | -------------------------------------------------------------------------------- /test/phantom.js: -------------------------------------------------------------------------------- 1 | var page = new WebPage; 2 | 3 | page.onConsoleMessage = function(msg) { 4 | // cursor removed from phantomjs 5 | if (!/^\s+(?:\\|\/|\||\-)/.test(msg)) { 6 | console.log(msg.replace("\n", "")); 7 | } 8 | }; 9 | 10 | page.open(phantom.args[0] || "about:blank", function(status) { 11 | if (status === "success") { 12 | page.evaluate(function () { 13 | window.phantomExit = false; 14 | window.quit = function () { 15 | window.phantomExit = true; 16 | }; 17 | window.require = function () { 18 | return {wru: window.wru}; 19 | }; 20 | window.global = window; 21 | }); 22 | page.injectJs("../build/wru.console.js"); 23 | page.injectJs("test.js"); 24 | } else { 25 | phantom.exit(1); 26 | } 27 | setInterval(function () { 28 | page.evaluate(function () { 29 | return window.phantomExit; 30 | }) && phantom.exit(); 31 | }, 50); 32 | }); -------------------------------------------------------------------------------- /test/testnode.js: -------------------------------------------------------------------------------- 1 | // node.js specific test 2 | var wru = require("./../node/wru.console").wru; 3 | wru.test([{ 4 | name: "test that should pass", 5 | test: function () { 6 | wru.assert("it passes", 1); 7 | } 8 | },{ 9 | name: "async test", 10 | test: function () { 11 | setTimeout(wru.async(function (arg) { 12 | wru.assert("OK", "OK" === arg); 13 | }), 2000, "OK"); 14 | } 15 | },{ 16 | name: "test that should fail", 17 | test: function () { 18 | wru.assert("this passes", true); 19 | wru.assert("this fails", 0); 20 | } 21 | },{ 22 | name: "test that should throw an error", 23 | test: function () { 24 | wru.assert("it's an error", 1); 25 | WTF++; 26 | } 27 | }]); 28 | -------------------------------------------------------------------------------- /test/testrhino.js: -------------------------------------------------------------------------------- 1 | // Rhino specific test 2 | 3 | load( 4 | new java.io.File(".").getCanonicalPath() + 5 | "/build/wru.console.js" 6 | ); 7 | 8 | wru.test([{ 9 | name: "test that should pass", 10 | test: function () { 11 | wru.assert("it passes", 1); 12 | } 13 | },{ 14 | name: "async test", 15 | test: function () { 16 | setTimeout(wru.async(function (arg) { 17 | wru.assert("OK", "OK" === arg); 18 | }), 2000, "OK"); 19 | } 20 | },{ 21 | name: "test that should fail", 22 | test: function () { 23 | wru.assert("this passes", true); 24 | wru.assert("this fails", 0); 25 | } 26 | },{ 27 | name: "test that should throw an error", 28 | test: function () { 29 | wru.assert("it's an error", 1); 30 | WTF++; 31 | } 32 | }]); 33 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 by Andrea Giammarchi, @WebReflection 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | wru 5 | 6 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | 40 |
    41 | 44 | 45 | -------------------------------------------------------------------------------- /src/wru.intro.js: -------------------------------------------------------------------------------- 1 | var wru = function (window) {"use strict"; 2 | 3 | /** 4 | * Copyright (C) 2011 by Andrea Giammarchi, @WebReflection 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | -------------------------------------------------------------------------------- /node/program.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | //! wru :: (C) Andrea Giammarchi - Mit Style License 3 | var 4 | CWD = process.cwd(), 5 | path = require('path'), 6 | args = resolveArguments(process.argv), 7 | wru = require(path.join(__dirname, 'wru.console.js')), 8 | inline = [], 9 | test = [consoleAssert], 10 | wru_test = wru.test, 11 | interval = 0 12 | ; 13 | function consoleAssert() { 14 | inline.forEach(function (args) { 15 | args.length < 2 ? 16 | wru.assert(args[0]) : 17 | wru.assert(args[1], args[0]) 18 | ; 19 | }); 20 | } 21 | function execute() { 22 | inline.length || test.shift(); 23 | wru_test.call(wru, test); 24 | } 25 | function resolveArguments(args, keepProgramName) { 26 | var a = [].slice.call(args, 0); 27 | if (/(?:^|\/|\\)node(?:\.exe)?$/.test(a[0])) a.shift(); 28 | keepProgramName || a.shift(); 29 | return a; 30 | } 31 | global.wru = wru; 32 | global.assert = wru.assert; 33 | global.async = wru.async; 34 | global.test = wru.test = function () { 35 | clearTimeout(interval); 36 | test = test.concat.apply(test, arguments); 37 | interval = setTimeout(execute, 10); 38 | }; 39 | global.log = wru.log; 40 | /* maybe too obtrusive ... 41 | global.console.assert = function assert() { 42 | inline.push(arguments); 43 | }; 44 | */ 45 | if (args.length) { 46 | args.forEach(function (fileName) { 47 | require(path.join(CWD, fileName)); 48 | }); 49 | } else { 50 | console.log(''); 51 | console.log('Usage:'); 52 | console.log('wru ~/path/with/test.js ~/more?if/necessary.js'); 53 | console.log(''); 54 | process.exit(); 55 | } 56 | -------------------------------------------------------------------------------- /src/program.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | //! wru :: (C) Andrea Giammarchi - Mit Style License 3 | var 4 | CWD = process.cwd(), 5 | path = require('path'), 6 | args = resolveArguments(process.argv), 7 | wru = require(path.join(__dirname, 'wru.console.js')), 8 | inline = [], 9 | test = [consoleAssert], 10 | wru_test = wru.test, 11 | interval = 0 12 | ; 13 | function consoleAssert() { 14 | inline.forEach(function (args) { 15 | args.length < 2 ? 16 | wru.assert(args[0]) : 17 | wru.assert(args[1], args[0]) 18 | ; 19 | }); 20 | } 21 | function execute() { 22 | inline.length || test.shift(); 23 | wru_test.call(wru, test); 24 | } 25 | function resolveArguments(args, keepProgramName) { 26 | var a = [].slice.call(args, 0); 27 | if (/(?:^|\/|\\)node(?:\.exe)?$/.test(a[0])) a.shift(); 28 | keepProgramName || a.shift(); 29 | return a; 30 | } 31 | global.wru = wru; 32 | global.assert = wru.assert; 33 | global.async = wru.async; 34 | global.test = wru.test = function () { 35 | clearTimeout(interval); 36 | test = test.concat.apply(test, arguments); 37 | interval = setTimeout(execute, 10); 38 | }; 39 | global.log = wru.log; 40 | /* maybe too obtrusive ... 41 | global.console.assert = function assert() { 42 | inline.push(arguments); 43 | }; 44 | */ 45 | if (args.length) { 46 | args.forEach(function (fileName) { 47 | require(path.join(CWD, fileName)); 48 | }); 49 | } else { 50 | console.log(''); 51 | console.log('Usage:'); 52 | console.log('wru ~/path/with/test.js ~/more?if/necessary.js'); 53 | console.log(''); 54 | process.exit(); 55 | } 56 | -------------------------------------------------------------------------------- /src/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | wru test 5 | 21 | 52 | 53 | 54 | 57 | 58 | 59 |
    60 | 63 | 64 | -------------------------------------------------------------------------------- /src/rhinoTimers.js: -------------------------------------------------------------------------------- 1 | // revisited by Andrea Giammarchi, @WebReflection 2 | // compatible with both Rhino and Node 3 | // now it is possible to include this file in the server console without rhinoTimers dependencies 4 | // @link http://stackoverflow.com/questions/2261705/how-to-run-a-javascript-function-asynchronously-without-using-settimeout 5 | // glory and fortune to to Weston C for the inital hint 6 | // but it's also RIDICULOUS Rhino does not implement in core timers properly! 7 | 8 | // condition to avoid problems with jsc 9 | if (typeof global != "undefined") { 10 | 11 | var 12 | setTimeout = global.setTimeout, 13 | setInterval = global.setInterval, 14 | clearInterval = global.clearInterval, 15 | clearTimeout = global.clearTimeout 16 | ; 17 | 18 | setTimeout || (function (timer, ids, slice, counter) { 19 | 20 | // did you know? 21 | // all browsers but IE accept one or more arguments 22 | // to pass to the callbacl after the timer/delay number 23 | // ... so does Rhino now! 24 | 25 | setInterval = global.setInterval = function setInterval(fn, delay) { 26 | return schedule(fn, delay, slice.call(arguments, 2), 1); 27 | }; 28 | 29 | setTimeout = global.setTimeout = function setTimeout(fn, delay) { 30 | return schedule(fn, delay, slice.call(arguments, 2)); 31 | }; 32 | 33 | clearInterval = global.clearInterval = 34 | clearTimeout = global.clearTimeout = function clearInterval(id) { 35 | ids[id].cancel(); 36 | timer.purge(); 37 | delete ids[id]; 38 | }; 39 | 40 | function schedule(fn, delay, args, interval) { 41 | var id = ++counter; 42 | ids[id] = new JavaAdapter(java.util.TimerTask,{run: function () { 43 | fn.apply(null, args); 44 | }}); 45 | interval ? 46 | timer.schedule(ids[id], delay, delay) 47 | : 48 | timer.schedule(ids[id], delay) 49 | ; 50 | return id; 51 | } 52 | 53 | })(new java.util.Timer(), {}, [].slice, 0); 54 | 55 | } else { // jsc specific hack 56 | !function (global, i, cbs, slice) { 57 | function setTimeout(cb, delay) { 58 | var t = new Date; 59 | while (new Date - t < delay); 60 | cb.apply(null, slice.call(arguments, 2)); 61 | } 62 | slice = cbs.slice; 63 | global.setTimeout = global.setInterval = setTimeout; 64 | global.clearInterval = global.clearTimeout = function () {}; 65 | }(this, 0, []); 66 | } 67 | -------------------------------------------------------------------------------- /node/wru.console.js: -------------------------------------------------------------------------------- 1 | /*! 2 | (C) Andrea Giammarchi, @WebReflection - Mit Style License 3 | */ 4 | if(typeof global!="undefined"){var setTimeout=global.setTimeout,setInterval=global.setInterval,clearInterval=global.clearInterval,clearTimeout=global.clearTimeout;setTimeout||(function(h,c,g,a){setInterval=global.setInterval=function b(j,i){return e(j,i,g.call(arguments,2),1)};setTimeout=global.setTimeout=function d(j,i){return e(j,i,g.call(arguments,2))};clearInterval=global.clearInterval=clearTimeout=global.clearTimeout=function f(i){c[i].cancel();h.purge();delete c[i]};function e(l,k,j,i){var m=++a;c[m]=new JavaAdapter(java.util.TimerTask,{run:function(){l.apply(null,j)}});i?h.schedule(c[m],k,k):h.schedule(c[m],k);return m}})(new java.util.Timer(),{},[].slice,0)}else{!function(c,b,a,e){function d(f,g){var h=new Date;while(new Date-h"; 69 | node.className = className; 70 | } 71 | 72 | function showTheProblem() { 73 | var style = this.lastChild.style; 74 | style.display = style.display == "none" ? "block" : "none"; 75 | } 76 | 77 | function writeItOrdered(fail) { 78 | node[INNERHTML] += ""; 79 | (node.onclick = showTheProblem).call(node); 80 | } 81 | 82 | function Dary() { 83 | clearDaryTimeou(); 84 | overallPass += pass[LENGTH]; 85 | overallFail += fail[LENGTH]; 86 | overallFatal += fatal[LENGTH]; 87 | passTheInfo("(" + join.call([ 88 | pass[LENGTH], 89 | ci = fail[LENGTH], 90 | fatal[LENGTH] 91 | ], ", ") + ")"); 92 | node = node.parentNode; 93 | fatal[LENGTH] ? 94 | writeItOrdered(fatal, prefix = "error") 95 | : ( 96 | ci ? 97 | writeItOrdered(fail, prefix = "fail") 98 | : 99 | prefix = "pass" 100 | ) 101 | ; 102 | node.className = prefix; 103 | ci = 0; 104 | prefix = EMPTY; 105 | isGonnaBeLegen(); 106 | } 107 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 2 | var 3 | setup, teardown, jsc 4 | ; 5 | 6 | // node, rhino, and web 7 | try { 8 | // node and phantom js 9 | var wru = this.wru.assert ? 10 | this.wru : 11 | require("./../build/wru.console") 12 | ; 13 | go(wru); 14 | } catch(wru) { 15 | // rhino 16 | try { 17 | load( 18 | new java.io.File(".").getCanonicalPath() + 19 | "/build/wru.console.js" 20 | ); 21 | go(wru); 22 | } catch(wru) { 23 | try { 24 | // jsc test/test.js 25 | load( 26 | "build/wru.console.js" 27 | ); 28 | jsc = true; 29 | go(wru); 30 | } catch(wru) { 31 | // html (assuming test.html is used in same folders structure) 32 | (function(xhr){ 33 | try { 34 | xhr.open("get", "./../build/wru.min.js", true); 35 | xhr.onreadystatechange = function () { 36 | if (xhr.readyState == 4) { 37 | try { 38 | Function(xhr.responseText.replace(/var wru=/,"this.wru=")).call(window); 39 | } catch(e) { 40 | alert(e); 41 | } 42 | go(window.wru); 43 | } 44 | }; 45 | xhr.send(null); 46 | } catch(e) { 47 | alert(e.message || e); 48 | } 49 | }(new XMLHttpRequest)); 50 | } 51 | } 52 | } 53 | 54 | function go(wru) { 55 | 56 | wru.test([{ 57 | name: "test that should pass", 58 | test: function () { 59 | wru.assert("it passes", 1); 60 | } 61 | },{ 62 | name: "async test", 63 | test: function () { 64 | if (jsc) { 65 | wru.log("JavaScriptCore does not support timers (yet)"); 66 | wru.assert("OK"); 67 | } else { 68 | setTimeout(wru.async(function (arg) { 69 | wru.assert("OK", "OK" === arg); 70 | wru.assert(setup === 1); 71 | wru.assert(teardown == null); 72 | }), 2000, "OK"); 73 | } 74 | }, 75 | setup: function () { 76 | setup = 1; 77 | }, 78 | teardown: function () { 79 | teardown = 1; 80 | } 81 | },{ 82 | name: "test that should fail", 83 | test: function () { 84 | wru.assert("this passes", true); 85 | wru.assert("this fails", 0); 86 | } 87 | },{ 88 | name: "test that should throw an error", 89 | test: function () { 90 | wru.assert("it's an error", 1); 91 | WTF++; 92 | } 93 | },{ 94 | name: "test that should be pass synchronously even if the callback was created via async", 95 | test: function () { 96 | function sync() {wru.assert(++executed)} 97 | var executed = 0; 98 | jsc ? sync() : wru.async(sync)(); 99 | wru.assert(executed); 100 | } 101 | }]); 102 | } 103 | -------------------------------------------------------------------------------- /src/wru.console.functions.js: -------------------------------------------------------------------------------- 1 | // console specific version 2 | function isGonnaBeLegen() { 3 | current = shift.call(queue); 4 | if (current) { 5 | if (typeof current == "function") { 6 | current = {name: current[NAME] || "anonymous", test: current}; 7 | } 8 | log(OUTPUT_SEPARATOR); 9 | log( 10 | (iHasIt(current, NAME) && current[NAME]) 11 | || 12 | (iHasIt(current, DESCRIPTION) && current[DESCRIPTION]) 13 | || 14 | UNKNOWN 15 | ); 16 | pass = []; 17 | fail = []; 18 | fatal = []; 19 | tmp = {}; 20 | giveItATry("setup"); 21 | fatal[LENGTH] || giveItATry("test"); 22 | waitForIt || Dary(); 23 | } else { 24 | showSummary(); 25 | } 26 | } 27 | 28 | function log(info, avoidNewLine) { 29 | info = info + (avoidNewLine ? "" : "\n"); 30 | try { 31 | // node 0.11+ alternative ... 32 | process.stdout.write(info); 33 | } catch(up) { 34 | try { 35 | // node 0.6 36 | require("util").print(info); 37 | } catch(up) { 38 | try { 39 | // node 0.4 40 | require("sys").print(info); 41 | } catch(up) { 42 | try { 43 | // hello Rhino 44 | // print uses println ... while we need print without \n 45 | java.lang.System.out.print(info); 46 | } catch(up) { 47 | try { 48 | // phantomjs or default fallback 49 | console.log(info); 50 | } catch(up) { 51 | // jsc and others 52 | print(info); 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | function showSummary() { 61 | var code = 0, status; 62 | log(EMPTY); 63 | log(OUTPUT_SEPARATOR); 64 | switch (true) { 65 | case !!overallFatal: 66 | code++; 67 | status = "error"; 68 | log(ERROR + " " + overallFatal + " Errors"); 69 | break; 70 | case !!overallFail: 71 | code++; 72 | status = "fail"; 73 | log(FAILURE + EMPTY + overallFail + " Failures"); 74 | break; 75 | default: 76 | status = "pass"; 77 | log(OK + " " + overallPass + " Passes"); 78 | } 79 | wru.status = status; 80 | log(OUTPUT_SEPARATOR); 81 | log(EMPTY); 82 | wru.after(); 83 | try { 84 | // node.js 85 | process.exit(code); 86 | } catch(up) { 87 | // rhino 88 | quit(); 89 | } 90 | } 91 | 92 | function writeItOrdered(fail) { 93 | for (var 94 | i = 0, length = fail[LENGTH]; 95 | i < length; 96 | log(" " + (++i) + ". " + fail[i - 1]) 97 | ); 98 | } 99 | 100 | function Dary() { 101 | clearDaryTimeou(); 102 | overallPass += pass[LENGTH]; 103 | overallFail += fail[LENGTH]; 104 | overallFatal += fatal[LENGTH]; 105 | if (fatal[LENGTH]) { 106 | prefix = ERROR; 107 | writeItOrdered(fatal); 108 | } else if(fail[LENGTH]) { 109 | prefix = FAILURE; 110 | writeItOrdered(fail); 111 | } else { 112 | prefix = OK; 113 | } 114 | log(prefix + " passes: " + pass[LENGTH] + ", fails: " + fail[LENGTH] + ", errors: " + fatal[LENGTH]); 115 | ci = 0; 116 | prefix = EMPTY; 117 | isGonnaBeLegen(); 118 | } 119 | -------------------------------------------------------------------------------- /src/wru.js: -------------------------------------------------------------------------------- 1 | 2 | var // wru library core 3 | wru = { 4 | timeout: TIMEOUT, 5 | assert: function assert(description, result) { 6 | 7 | // if no description provided, variables are shifted 8 | // these are both valid wru.assert calls indeed 9 | // wru.assert(truishValue); 10 | // wru.assert("test description", truishValue); 11 | if (arguments[LENGTH] == 1) { 12 | result = description; 13 | description = UNKNOWN; 14 | } 15 | 16 | // flag used in wru.async to verify at least 17 | // one assertion was performed 18 | called = TRUE; 19 | 20 | // store the result in the right collection 21 | push.call(result ? pass : fail, prefix + description); 22 | 23 | // just to add a bit of sugar 24 | return result; 25 | }, 26 | async: function async(description, callback, timeout, p) { 27 | var r, delay = timeout || wru.timeout || (wru.timeout = TIMEOUT); 28 | // p is used as sentinel 29 | // it defines the anonymous name 30 | // if necessary and it's used to flag the timeout 31 | p = ++waitForIt; 32 | 33 | // if no description provided, variables are shifted 34 | // these are all valid wru.async calls indeed, timeout is optional 35 | // wru.async(function () { ... }) 36 | // wru.async("test description", function () { ... }) 37 | // wru.async(function () { ... }, timeout) 38 | // wru.async("test description", function () { ... }, timeout) 39 | if (typeof description == "function") { 40 | delay = callback || wru.timeout; 41 | callback = description; 42 | description = "asynchronous test #" + p; 43 | } 44 | 45 | // if in *TIMEOUT* time nothing happens ... 46 | timeout = setTimeout(function () { 47 | 48 | // p is flagged as 0 49 | p = 0; 50 | 51 | // timeout is handled as failure, not error (could be the server) 52 | push.call(fail, description); 53 | 54 | // if there is no reason to waitForIt then is time to call Dary() 55 | --waitForIt || (daryTimeout = setTimeout(Dary, 0)); 56 | }, 57 | // timeout can be specified 58 | // this procedure ensure that it's 59 | // a number and it's greater than 0 60 | abs(delay) || wru.timeout 61 | ); 62 | 63 | // the async function is a wrap of the passed callback 64 | return function async() { 65 | 66 | // if it's executed after the timeout nothing happens 67 | // since the failure has been already notified 68 | if (!p) return; 69 | 70 | // called is always set as *TRUE* during any assertion 71 | // this indicates if the callback made at least one assertion 72 | // as example, in this case the callback could be called many time 73 | // with different readyState ... however, only on readyState 4 74 | // there is the assertion we are interested about, e.g. 75 | // 76 | // xhr.onreadystatechange = wru.async(function (){ 77 | // if (this.readyState == 4) 78 | // wru.assert("content", this.responseText.length) 79 | // ; 80 | // }); 81 | // 82 | // in above example called will be flagged as true 83 | // only during last readyState call 84 | called = FALSE; 85 | 86 | // simply recycled "string" variable 87 | // prefix will be internally used by assert during function execution 88 | prefix = description + ": "; 89 | 90 | // the original callback is called with proper *this* if specified 91 | try { 92 | r = callback.apply(this, arguments); 93 | } catch(doooodeThisIsBAD) { 94 | // if there is an Error 95 | // the test is screwed up 96 | // called has to be set as *TRUE* to invalidate the test 97 | called = TRUE; 98 | // message is "casted" to avoid IE host objects errors problem 99 | // (or any other possible edge case) 100 | push.call(fatal, prefix + doooodeThisIsBAD); 101 | } 102 | 103 | // prefix can be *EMPTY* string again now 104 | prefix = EMPTY; 105 | 106 | // a failure or at least an assertion 107 | if (called) { 108 | 109 | // timeout not necessary anymore 110 | clearTimeout(timeout); 111 | 112 | // if there is no reason to waitForIt then is time to call Dary() 113 | --waitForIt || (daryTimeout = setTimeout(Dary, 0)); 114 | } 115 | 116 | // return the eventual callback value 117 | return r; 118 | }; 119 | }, 120 | 121 | // wru.test({...test...}) 122 | // wru.test([{...test...}, {...test...}, ...]) 123 | // the {...test...} object should have a string name and a function test property 124 | // optionally a function setup and a function teardown too 125 | test: function test(list, after) { 126 | 127 | // in case you need to do something after 128 | wru.after = after || function () {}; 129 | 130 | // test may be called multiple times 131 | // queue should simply concatenate other calls 132 | queue = concat.apply(queue, [list]); 133 | 134 | // if wru.random is true, the queue is ranodomized 135 | // this is to make tests indipendent from each others 136 | wru.random && sort.call(queue, messItUp); 137 | 138 | // if there is no test to waitForIt 139 | // Dary() has been called already 140 | // we can procede with next test 141 | // invoking isGonnaBeLegen() 142 | waitForIt || isGonnaBeLegen(); 143 | } 144 | }, 145 | -------------------------------------------------------------------------------- /test/solutions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | wru 5 | 118 | 119 | 120 | 121 | 122 | 123 | 155 | 156 | 157 |
    158 | 164 | 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | wru :: JavaScript tests have never been that easy 2 | ================================================= 3 | 4 | *wru* is an **essential general purpose test framework** compatible with **web** environment, [node.js](http://nodejs.org/), [Rhino](http://www.mozilla.org/rhino/), and now [PhantomJS](http://www.phantomjs.org/) too. 5 | 6 | 7 | features 8 | -------- 9 | 10 | * **runs in both client and server environments**, compatible with html files, node.js, Rhino, PhantomJS, and JavaScriptCore. 11 | * **both synchronous and asynchronous tests** in an absolutely intuitive way 12 | * **ES5 and JS.next ready**, compatible with `"use strict"` directive which means no `with` statements, `eval`, or misused `this` references 13 | * **easy**, probably the easiest way to test JS code out there thanks to its simplified API: `test`, `assert`, `async`, and `log` ... you already remember "*all of them*", isn't it? 14 | * **unobtrusive** and **self defensive**, since everything that could possibly change in such dynamic environment as JS is, is "*sandboxed*" inside the *wru closure*. This means no matter how "*nasty*" your code is, *wru* won't pollute or change the global environment, neither it will rely in native *constructor.prototypes* changes (`Array.prototype.push = ...` or `Object.prototype.hasOwnProperty = ...`? not a problem!) 15 | * **cursor included in both web and console** ... you gonna realize how much "[THE CURSOR](http://www.3site.eu/cursor/)" is important, specially to understand if your test is **stuck** or simply "*waiting for*" ... cursor is working in both Unix and OSX consoles (unfortunately PhantomJS does not support the cursor while jsc does not support timers at all) 16 | * **tiny**, even if it's not important in tests world, *wru* fits into about 2Kb (1.2Kb *minzpped*) which means not much to fix or change here, just a simple, reliable, and essential framework for your tests 17 | * **under your control**, since there is absolutely **no magic behind the *wru* scene**. You assert what you want, you async what you need, you describe what's needed, and you are *ready to go* in less than 5 minutes 18 | 19 | If you can't believe it check [html](https://github.com/WebReflection/wru/blob/master/test/test.html), [node.js](https://github.com/WebReflection/wru/blob/master/test/testnode.js), [Rhino](https://github.com/WebReflection/wru/blob/master/test/testrhino.js), or [PhantomJS](https://github.com/WebReflection/wru/blob/master/test/phantom.js) test and see how *wru* does work ;-) 20 | 21 | 22 | compatibility 23 | ------------- 24 | 25 | *wru* is compatible with basically all possible browsers out there included **IE5.5**, **IE6**, **IE7**, **IE8**, **IE9**, **IE10**, **Chrome**, **Firefox**, **Safari**, **Webkit** based, **Mobile Browsers**, and **Opera**. 26 | 27 | On server side *wru* is compatible with latest **node.js**, **Rhino**, **PhantomJS**, and **JavaScriptCore** versions. I swear if *I find an easy way to* easily *test Spider/Iron/JagerMonkey I will support them* too. 28 | 29 | 30 | how to test wru 31 | --------------- 32 | 33 | The simplest way to test wru is to use [template.html](https://raw.github.com/WebReflection/wru/master/build/template.html) for **web** tests or [template.js](https://raw.github.com/WebReflection/wru/master/build/template.js) for **node**, **rhino**, and **jsc** tests or [template.phantom.js](https://github.com/WebReflection/wru/blob/master/build/template.phantom.js) for **PhantomJS** tests. 34 | 35 | With these 3 options you don't even need to fork or download the entire repository ... but if you do that ... 36 | 37 | From *wru* root directory, simply run these commands accordingly with what you want to test: 38 | 39 | // node.js test 40 | node test/test.js 41 | 42 | // Rhino 43 | java -jar builder/jar/js.jar test/test.js 44 | 45 | // PhantomJS test 46 | phantomjs test/phantom.js 47 | 48 | // JavaScriptCore test 49 | jsc test/test.js 50 | 51 | // web (through Mac OSX but you can open test.html with any browser) 52 | open test/test.html 53 | 54 | **PhantomJS** supports tests for both plain JavaScript in a blank page, or any url adding it as argument. 55 | 56 | // PhantomJS test on about:blank 57 | phantomjs build/template.phantom.js 58 | 59 | // PhantomJS test on any url 60 | phantomjs build/template.phantom.js "http://yourwebsite.com" 61 | 62 | **PhantomJS** tests should always starts when the DOM has been already parsed. 63 | 64 | **JavaScriptCore** does not implement (yet) setTimeout and setInterval so it's not possible to test via `async()` calls. 65 | 66 | If you forked the project, you made some change, and you want to **rebuild wru**, this is all you have to do: 67 | 68 | // still inside wru folder 69 | python builder/build.py 70 | 71 | // or now with node 72 | node builder/build.js 73 | 74 | After the build process is finished, no more than 3 seconds with forced waiting time included to read stats if build has been *double-clicked*, you should be able to run again the test for your own environment. 75 | 76 | Please bear in mind **JSbuilder.(js|py)** works with **node-js 0.4+** or **Python < 3** (2.6 or 2.7 are fine) so be sure you have it (you should by default on Mac or Linux). 77 | 78 | 79 | wru basics 80 | ---------- 81 | 82 | // probably all you need as "one shot" test 83 | wru.test({ 84 | name: "Hello wru!", 85 | test: function () { 86 | wru.assert("it works!", 1); 87 | } 88 | }); 89 | 90 | // for multiple tests ... pass an Array 91 | wru.test([{ 92 | name: "test #1", 93 | setup: function () { 94 | // setup before the test 95 | }, 96 | test: function () { 97 | // async test example 98 | setTimeout(wru.async(function () { 99 | wru.assert("executed", true); 100 | }), 1000); 101 | }, 102 | teardown: function () { 103 | // clean up after the test 104 | } 105 | },{ 106 | name: "test #2", 107 | test: function () { 108 | // do other stuf here 109 | } 110 | }]); 111 | 112 | To know more about *wru* possibilities ... please keep reading ;-) 113 | 114 | 115 | wru API 116 | ======= 117 | 118 | There are truly few things you need to know, and even less properties you need to configure! 119 | 120 | 121 | methods 122 | ------- 123 | 124 | * `test(object)` or `test([object, ..., object])` to execute one or more tests. A generic test object may have one or more properties: 125 | * `test` property, as **function**, to execute the test with one or more `wru.assert()` or `wru.async()` calls. **optional** but recommended 126 | * `name` or `description` property, as **string**, to have visual knowledge of the current test **optional** 127 | * `setup` property, as **function**, that will be executed right before the test: **optional** 128 | * `teardown` property, as **function**, that will be executed right after the test: **optopnal** 129 | * `assert("description", truishOrFalsyValue)` to manually assert whatever you want where **description is optional** (but suggested) and the assertion is compatible with *truish* or *falsy* values. You are in charge of strictly compared results if necessary by *===* operator, nothing new to learn here 130 | * `async("description", callback, timeout)` to tell *wru* that a test will be executed at some point later and where **both description and timeout are optionals** 131 | * `log(anything, forceFallback)` the equivalent of *console.log(obj)* where supported, the equivalent of *alert()* or *print()* if the *forceFallback* flag is set to true (or better, *truish*) 132 | 133 | 134 | properties 135 | ---------- 136 | 137 | * `random`, as `true` or `false`, to make tests order execution random (by default `false`) 138 | * `node` on **web version only** to set a different node from the default one (which is an element with `id == "wru"`or the `document.body` or the `document.documentElement` if `body` is not present yet) 139 | 140 | 141 | test properties 142 | --------------- 143 | Each test can be either an object or, if you are that lazy typer, a function. 144 | 145 | * `name` as test *title* or *test name*, if the test is a function the function name (expression or declared) will be used where available, anonymous otherwise. 146 | * `setup` as function to do something before the test is executed. Bear in mind every test will receive a freshly baked object as argument, from setup, to test, and teardown, the same object. Use it if you need. 147 | * `test` as function to execute if the test is not a function itself. Receives the shared object per test as first argument. 148 | * `teardown` as function to do something after the test is executed. Receives the same shared object setup and test receive as argument. 149 | 150 | 151 | how does wru work 152 | ================= 153 | 154 | following a list of explained tasks that are possible with *wru* 155 | 156 | 157 | synchronous tests and wru.assert() 158 | ---------------------------------- 159 | Every test **may** have one or more `wru.assert()` calls inside. The method itself accepts one or two arguments. Following a sequence of valid operations. 160 | 161 | // the test object ... 162 | { 163 | name: "existance test", 164 | test: function () { 165 | 166 | // example only: if next property is not 167 | // null, undefined, 0, false, "", or NaN 168 | // the assertion will pass the test 169 | wru.assert("callback exists", window.onload); 170 | 171 | // if necessary, assertion can be strict without problems 172 | wru.assert( 173 | "it is a callback", 174 | typeof window.onload === "function" 175 | ); 176 | 177 | // the description is visually useful 178 | // if the test fails but it's not mandatory 179 | // next example is still valid, no description 180 | wru.assert("isArray" in Array); 181 | 182 | // if a condition supposes to be truish 183 | // wru.assert can make test life easier 184 | // returning the asserted value 185 | if (wru.assert("defineProperty" in Object)) { 186 | wru.assert( 187 | Object.defineProperty({}, "_", {value: true})._ 188 | ); 189 | } 190 | 191 | } 192 | } 193 | 194 | 195 | asynchronous tests and wru.async() 196 | ---------------------------------- 197 | Every test is performed synchronously unless there is one or more `wru.async()` calls. In latter case all tests after the current one will be waiting for the asynchronous call to be executed. 198 | When it happens, if the asynchronous call performed one or more assertions, the framework keep going without requiring any extra step: **is that easy!** 199 | 200 | // the test object ... 201 | { 202 | name: "load content", 203 | test: function () { 204 | // asynchronous test example 205 | 206 | // this will be synchronous 207 | wru.assert("condition accepted", true); 208 | 209 | // this will be asynchronous 210 | var xhr = new XMLHttpRequest; 211 | xhr.open("get", "file.txt", true); 212 | xhr.onreadystatechange = wru.async(function () { 213 | if (this.readyState === 4) { 214 | 215 | // only on readyState 4 there is an assertion 216 | wru.assert("text is not empty", this.responseText.length); 217 | 218 | // if necessary, async call can be nested 219 | setTimeout(wru.async(function () { 220 | wru.assert( 221 | "DOM changed in the meanwhile", 222 | docment.body.innerHTML != storedLayout 223 | ); 224 | }, 500)); 225 | } 226 | }); 227 | xhr.send(null); 228 | 229 | // this will be performed regardless 230 | wru.assert("something else to check", 1); 231 | } 232 | } 233 | 234 | In above example, the `onreadystatechange` function may be executed many times on different `readyState`. The *wru* logic cannot care less about it since an asynchronous callback is considered *done* when **at least one assertion has been performed**. 235 | If this does not happen the internal `TIMEOUT` constant, by default 10 seconds, will kill the procedure. 236 | You have to admit there is no reason to create an asynchronous test without performing some assertion inside the callback ... and this is where *wru* is smart. 237 | If many assertions have been defined and one of them is not reached is most likely because there was an error or a failure in the test. 238 | *wru* tracks all tests without problems so forget things such `lib.expectedAssertions(3)` and "*friends*" ... you really may not need that! 239 | 240 | 241 | the temporary object 242 | -------------------- 243 | 244 | If needed, every `setup`, `test`, or `teardown` function will receive a "*freshly new backed*" object for the current test. 245 | This can be handy to store some reference or value on `setup`, use them during the `test`, and drop them during the `teardown` if necessary. 246 | 247 | // the test object ... 248 | { 249 | name: "tmp object all over", 250 | setup: function (tmp) { 251 | tmp.global = window; 252 | tmp.global.random = Math.random(); 253 | }, 254 | test: function (tmp) { 255 | wru.assert( 256 | tmp.global === window // true 257 | ); 258 | wru.assert( 259 | typeof tmp.global.random == "number" // true again 260 | ); 261 | }, 262 | teardown: function (tmp) { 263 | delete tmp.global.random; 264 | delete tmp.global; 265 | } 266 | } 267 | 268 | 269 | the build process 270 | ================= 271 | 272 | *wru* is based on [javascript-builder](http://code.google.com/p/javascript-builder/) which is able to aggregate distributed files in order to produce the final library/framework even if the source/JS logic is split in more files. 273 | 274 | This is the *wru* case, where some file is dedicated for web environment rather than console/shell one. 275 | If you fork the project and you make some change/improvement, first of all let me know :-), secondly remember to re-build the script. 276 | This is the list of files actually created by *wru build process* inside the *build* folder: 277 | 278 | * **wru.console.max.js** is the full script console/shell related, suitable for *node.js* or *rhino* tests 279 | * **wru.console.js** is the minified version of the precedent one with `wru.debug()` stripped out 280 | * **wru.dom.js** is the full script DOM related, suitable for *web* and *browsers* 281 | * **wru.min.js** is the minified version of the precedent one with `wru.debug()` stripped out 282 | 283 | `wru.debug()` is a method used to export, track, test, or change internals. You should never use this method unless strictly necessary but it's there for debugging purpose. 284 | `wru.debug()` is automatically removed from built versions so that no evaluation of internals can be possible. 285 | 286 | If you want to have an overall view of the framework check already built output since if not familiar with this build process it may be hard at the beginning. 287 | 288 | This is the [HTML version](https://github.com/WebReflection/wru/blob/master/build/wru.dom.js), and this is the [console one](https://github.com/WebReflection/wru/blob/master/build/wru.console.max.js), you will notice things make sense there since the order is specified in the [build.py](https://github.com/WebReflection/wru/blob/master/builder/build.py) file. 289 | 290 | Please remember all you have to do to build *wru* is this call in the *wru* project root 291 | 292 | python builder/build.py 293 | 294 | 295 | wru against others 296 | ================== 297 | 298 | Other tests frameworks may offer more than what *wru* does but this rarely comes for free. 299 | Some of them may have such complicated/articulated logic that it may happens is the test framework itself that's failing rather than your code. 300 | Also you need to read a lot of documentation and most likely to obtain something already possible with *wru*. 301 | I am not saying *wru* is the best test framework out there, I am just saying you should consider your requirements before you chose a test framework ;-) 302 | In any case, *wru* aim is to make any sort of test simplified and under control. 303 | 304 | As example: "*do you really need so much 'magic' to perform these tasks?*" 305 | 306 | // rather than specify expected arguments 307 | // via magic/complicated operations 308 | function (a, b, c) { 309 | wru.assert("received numbers", 310 | typeof a == "number" 311 | && 312 | typeof b == "number" 313 | && 314 | typeof c == "number" 315 | ); 316 | } 317 | 318 | // rather than specify returned values 319 | // via magic/complicated operations 320 | wru.assert(typeof callbac() != "undefined"); 321 | 322 | // did you know the console object 323 | // may have already an assert() method 324 | // since that's basically all you need? 325 | wru.assert( 326 | "if true, I can get rid of wru since all I need is 'assert'", 327 | "assert" in console 328 | ); 329 | 330 | // the only reason wru may be handy is the 331 | // cross platform/environment compatibility 332 | // and its async method interlaced with 333 | // current environment layout (HTML or shell/console/bash) 334 | wru.assert("oh come on but this is so easy!", 1); 335 | 336 | Just give it a try ;-) 337 | 338 | 339 | HTML VS console 340 | =============== 341 | 342 | *wru* core functionality is exactly the same in both environments ... it cannot be easier to maintain, imo. 343 | However, there are few substantial differences between HTML results and those shown in the console 344 | 345 | 346 | HTML tests 347 | ---------- 348 | 349 | * based on classes, easily to customize via [CSS](https://github.com/WebReflection/wru/blob/master/test/wru.css) 350 | * differential colors accordingly with test results: green if successful, red if failed, black if cryptical (e.g. unmanaged exceptions) 351 | * failures or errors are not shown by default, **one click** to see the list of problems in any of those non green tests 352 | * summary on top, no need to scroll 'till the end of the document to understand how it was. A nice summary with all passed, failed, or unmanaged errors test is shown on top 353 | 354 | 355 | console tests 356 | ------------- 357 | 358 | * based on `stdout` 359 | * differential prefixes accordingly with test results: `[OK]` if successful, `[FAILURE]` if failed, `[ERROR]` if cryptical (e.g. unmanaged exceptions) 360 | * failures and errors are always listed in the output 361 | * summary always at the end of the test 362 | 363 | 364 | I need a setup per each test! 365 | ============================= 366 | 367 | Sure you do :-) 368 | 369 | // just create a simple wrapper before your tests 370 | // to fully automate the procedure 371 | wru.test = (function (test) { 372 | 373 | // we got a closure here, do whaveter you want! 374 | function whateverSetupIsNeeded(tmp) { 375 | // do setup stuff 376 | } 377 | 378 | return function (testObjects) { 379 | // be sure it's an array, convert otherwise 380 | testObjects = [].conca(testObjects); 381 | 382 | // per each object 383 | for (var 384 | // reassign the setup if present 385 | reassign = function (setup) { 386 | testObjects[i].setup = function (tmp) { 387 | whateverSetupIsNeeded(tmp); 388 | setup && setup(tmp); 389 | }; 390 | }, 391 | i = testObjects.length; i--; 392 | reassign(testObjects[i].setup) 393 | ); 394 | 395 | // invoke wru.test() which is self bound 396 | test(list); 397 | 398 | // that's pretty much it 399 | }; 400 | 401 | }(wru.test)); 402 | 403 | Similar technique if you need same teardown call per each test. 404 | 405 | 406 | I need other edge cases too ! 407 | ============================= 408 | 409 | The cool part is that being simple, *wru* is also highly customizable. 410 | Please keep an eye in the [solutions.html](https://github.com/WebReflection/wru/blob/master/test/solutions.html) file. 411 | I will try to update it as soon as some *request or edge case* comes up. 412 | 413 | 414 | wrap it if you want 415 | =================== 416 | 417 | If you think *wru* is too simple, you still have a chance to improve it wrapping its basic methods and create something wonderful out of it. 418 | Arguments automations? Returned values? Expected number of calls per callback? 419 | 420 | The *wru* cross environment core is easy to hack for anybody, check [wru.js](https://github.com/WebReflection/wru/blob/master/src/wru.js) and your are already half way through ;-) 421 | 422 | 423 | license 424 | ======= 425 | 426 | *wru* general purpose test framework and the rest of the project is under Mit Style License 427 | 428 | Copyright (C) 2011 by Andrea Giammarchi, @WebReflection 429 | 430 | Permission is hereby granted, free of charge, to any person obtaining a copy 431 | of this software and associated documentation files (the "Software"), to deal 432 | in the Software without restriction, including without limitation the rights 433 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 434 | copies of the Software, and to permit persons to whom the Software is 435 | furnished to do so, subject to the following conditions: 436 | 437 | The above copyright notice and this permission notice shall be included in 438 | all copies or substantial portions of the Software. 439 | 440 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 441 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 442 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 443 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 444 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 445 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 446 | THE SOFTWARE. 447 | 448 | 449 | OT: some fun during wru development 450 | ----------------------------------- 451 | 452 | If you check the built source you will realize that a `wru.test()` lifecycle is between a call to internal `isGonnaBeLegen()` function, passing through the `waitForIt` variable if some asynchronous call has been required, and ending up into the `Dary()` callback. 453 | 454 | I know you don't care but at least now you know how is the `wru.test()` lifecycle :{D --------------------------------------------------------------------------------