├── .gitignore ├── .npmignore ├── LICENSE.txt ├── Makefile ├── README.md ├── build ├── template.html ├── template.js ├── template.phantom.js ├── test.html ├── wru.console.js ├── wru.console.max.js ├── wru.dom.js └── wru.min.js ├── builder ├── JSBuilder.js ├── JSBuilder.py_deprecated ├── JSBuilder.pyc ├── build.js ├── build.py_deprecated └── jar │ ├── js.jar │ └── yuicompressor-2.4.6.jar ├── node ├── program.js └── wru.console.js ├── package.json ├── src ├── program.js ├── rhinoTimers.js ├── template.css ├── template.html ├── template.js ├── test.html ├── wru.DOM.cursor.js ├── wru.DOM.functions.js ├── wru.DOM.log.js ├── wru.DOM.node.js ├── wru.DOM.variables.js ├── wru.console.cursor.js ├── wru.console.functions.js ├── wru.console.log.js ├── wru.console.variables.js ├── wru.debug.js ├── wru.export.js ├── wru.functions.js ├── wru.global.shortcuts.js ├── wru.intro.js ├── wru.js ├── wru.outro.js ├── wru.phantom.js ├── wru.shared.js └── wru.variables.js └── test ├── base.js ├── phantom.js ├── solutions.html ├── test.html ├── test.js ├── testnode.js └── testrhino.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | builder/* 2 | test/* 3 | src/* 4 | Makefile -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /build/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | wru 5 | 6 | 30 | 31 | 32 | 33 | 34 | 35 | 67 | 68 | 69 |
70 | 76 | 77 | -------------------------------------------------------------------------------- /build/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 | /*! 33 | (C) Andrea Giammarchi, @WebReflection - Mit Style License 34 | */ 35 | 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 2 | 3 | 4 | wru test 5 | 21 | 52 | 53 | 54 | 86 | 87 | 88 |
89 | 95 | 96 | -------------------------------------------------------------------------------- /build/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"; 98 | node.className = className; 99 | } 100 | 101 | function showTheProblem() { 102 | var style = this.lastChild.style; 103 | style.display = style.display == "none" ? "block" : "none"; 104 | } 105 | 106 | function writeItOrdered(fail) { 107 | node[INNERHTML] += ""; 108 | (node.onclick = showTheProblem).call(node); 109 | } 110 | 111 | function Dary() { 112 | clearDaryTimeou(); 113 | overallPass += pass[LENGTH]; 114 | overallFail += fail[LENGTH]; 115 | overallFatal += fatal[LENGTH]; 116 | passTheInfo("(" + join.call([ 117 | pass[LENGTH], 118 | ci = fail[LENGTH], 119 | fatal[LENGTH] 120 | ], ", ") + ")"); 121 | node = node.parentNode; 122 | fatal[LENGTH] ? 123 | writeItOrdered(fatal, prefix = "error") 124 | : ( 125 | ci ? 126 | writeItOrdered(fail, prefix = "fail") 127 | : 128 | prefix = "pass" 129 | ) 130 | ; 131 | node.className = prefix; 132 | ci = 0; 133 | prefix = EMPTY; 134 | isGonnaBeLegen(); 135 | } 136 | 137 | // common functions for all versions 138 | function giveItATry(name) { 139 | if (iHasIt(current, name)) { 140 | try { 141 | current[name](tmp); 142 | } catch(doooodeThisIsBAD) { 143 | push.call(fatal, EMPTY + doooodeThisIsBAD); 144 | } 145 | } 146 | } 147 | 148 | function iHasIt(object, name) { 149 | return hasOwnProperty.call(object, name); 150 | } 151 | 152 | function messItUp() { 153 | return random() < .5 ? -1 : 1; 154 | } 155 | 156 | function clearDaryTimeou() { 157 | if (daryTimeout) { 158 | clearTimeout(daryTimeout); 159 | daryTimeout = 0; 160 | } 161 | giveItATry("teardown"); 162 | } 163 | 164 | 165 | var // wru library core 166 | wru = { 167 | timeout: TIMEOUT, 168 | assert: function assert(description, result) { 169 | 170 | // if no description provided, variables are shifted 171 | // these are both valid wru.assert calls indeed 172 | // wru.assert(truishValue); 173 | // wru.assert("test description", truishValue); 174 | if (arguments[LENGTH] == 1) { 175 | result = description; 176 | description = UNKNOWN; 177 | } 178 | 179 | // flag used in wru.async to verify at least 180 | // one assertion was performed 181 | called = TRUE; 182 | 183 | // store the result in the right collection 184 | push.call(result ? pass : fail, prefix + description); 185 | 186 | // just to add a bit of sugar 187 | return result; 188 | }, 189 | async: function async(description, callback, timeout, p) { 190 | var r, delay = timeout || wru.timeout || (wru.timeout = TIMEOUT); 191 | // p is used as sentinel 192 | // it defines the anonymous name 193 | // if necessary and it's used to flag the timeout 194 | p = ++waitForIt; 195 | 196 | // if no description provided, variables are shifted 197 | // these are all valid wru.async calls indeed, timeout is optional 198 | // wru.async(function () { ... }) 199 | // wru.async("test description", function () { ... }) 200 | // wru.async(function () { ... }, timeout) 201 | // wru.async("test description", function () { ... }, timeout) 202 | if (typeof description == "function") { 203 | delay = callback || wru.timeout; 204 | callback = description; 205 | description = "asynchronous test #" + p; 206 | } 207 | 208 | // if in *TIMEOUT* time nothing happens ... 209 | timeout = setTimeout(function () { 210 | 211 | // p is flagged as 0 212 | p = 0; 213 | 214 | // timeout is handled as failure, not error (could be the server) 215 | push.call(fail, description); 216 | 217 | // if there is no reason to waitForIt then is time to call Dary() 218 | --waitForIt || (daryTimeout = setTimeout(Dary, 0)); 219 | }, 220 | // timeout can be specified 221 | // this procedure ensure that it's 222 | // a number and it's greater than 0 223 | abs(delay) || wru.timeout 224 | ); 225 | 226 | // the async function is a wrap of the passed callback 227 | return function async() { 228 | 229 | // if it's executed after the timeout nothing happens 230 | // since the failure has been already notified 231 | if (!p) return; 232 | 233 | // called is always set as *TRUE* during any assertion 234 | // this indicates if the callback made at least one assertion 235 | // as example, in this case the callback could be called many time 236 | // with different readyState ... however, only on readyState 4 237 | // there is the assertion we are interested about, e.g. 238 | // 239 | // xhr.onreadystatechange = wru.async(function (){ 240 | // if (this.readyState == 4) 241 | // wru.assert("content", this.responseText.length) 242 | // ; 243 | // }); 244 | // 245 | // in above example called will be flagged as true 246 | // only during last readyState call 247 | called = FALSE; 248 | 249 | // simply recycled "string" variable 250 | // prefix will be internally used by assert during function execution 251 | prefix = description + ": "; 252 | 253 | // the original callback is called with proper *this* if specified 254 | try { 255 | r = callback.apply(this, arguments); 256 | } catch(doooodeThisIsBAD) { 257 | // if there is an Error 258 | // the test is screwed up 259 | // called has to be set as *TRUE* to invalidate the test 260 | called = TRUE; 261 | // message is "casted" to avoid IE host objects errors problem 262 | // (or any other possible edge case) 263 | push.call(fatal, prefix + doooodeThisIsBAD); 264 | } 265 | 266 | // prefix can be *EMPTY* string again now 267 | prefix = EMPTY; 268 | 269 | // a failure or at least an assertion 270 | if (called) { 271 | 272 | // timeout not necessary anymore 273 | clearTimeout(timeout); 274 | 275 | // if there is no reason to waitForIt then is time to call Dary() 276 | --waitForIt || (daryTimeout = setTimeout(Dary, 0)); 277 | } 278 | 279 | // return the eventual callback value 280 | return r; 281 | }; 282 | }, 283 | 284 | // wru.test({...test...}) 285 | // wru.test([{...test...}, {...test...}, ...]) 286 | // the {...test...} object should have a string name and a function test property 287 | // optionally a function setup and a function teardown too 288 | test: function test(list, after) { 289 | 290 | // in case you need to do something after 291 | wru.after = after || function () {}; 292 | 293 | // test may be called multiple times 294 | // queue should simply concatenate other calls 295 | queue = concat.apply(queue, [list]); 296 | 297 | // if wru.random is true, the queue is ranodomized 298 | // this is to make tests indipendent from each others 299 | wru.random && sort.call(queue, messItUp); 300 | 301 | // if there is no test to waitForIt 302 | // Dary() has been called already 303 | // we can procede with next test 304 | // invoking isGonnaBeLegen() 305 | waitForIt || isGonnaBeLegen(); 306 | } 307 | }, 308 | 309 | // common private variables / constants / shortcuts 310 | TRUE = true, 311 | FALSE = !TRUE, 312 | TIMEOUT = 100, 313 | EMPTY = " ", 314 | UNKNOWN = "unknown", 315 | LENGTH = "length", 316 | NAME = "name", 317 | DESCRIPTION = "description", 318 | LISTART = "
  • ", 319 | LIEND = "
  • ", 320 | cursor = "\\|/-", 321 | hasOwnProperty = wru.hasOwnProperty, 322 | prefix = EMPTY, 323 | charAt = prefix.charAt, 324 | slice = prefix.slice, 325 | queue = [], 326 | concat = queue.concat, 327 | join = queue.join, 328 | push = queue.push, 329 | shift = queue.shift, 330 | sort = queue.sort, 331 | waitForIt = 0, 332 | ci = 0, 333 | overallPass = 0, 334 | overallFail = 0, 335 | overallFatal = 0, 336 | daryTimeout = 0, 337 | 338 | 339 | // these variables are used on DOM version only 340 | INNERHTML = "innerHTML", 341 | document = window.document, 342 | createElement = document.createElement, 343 | 344 | 345 | // shared across the whole private scope 346 | Math, abs, random, setTimeout, clearTimeout, 347 | current, node, pass, fail, fatal, tmp, called 348 | ; 349 | 350 | // these are window/global object dependent 351 | // must be eventually defined after wru.export.js, if used 352 | Math = window.Math; 353 | abs = Math.abs; 354 | random = Math.random; 355 | setTimeout = window.setTimeout; 356 | clearTimeout = window.clearTimeout; 357 | 358 | 359 | // the default node to use for tests 360 | // feel free to specify another one 361 | // before wru.test() call 362 | wru.node = ( 363 | document.getElementById("wru") || 364 | document.body || 365 | document.documentElement 366 | ); 367 | 368 | // "THE CURSOR" http://3site.eu/cursor 369 | window.setInterval(function () { 370 | waitForIt && passTheInfo(charAt.call(cursor, ci++%4)); 371 | }, TIMEOUT); 372 | 373 | 374 | //^ this is useful to test internals on non minified version 375 | wru.debug = function (O_o) { 376 | return eval("(" + O_o + ")"); 377 | }; 378 | //$ and this block is removed at build time 379 | 380 | 381 | wru.log = function log(obj, alertOnly) { 382 | alertOnly ? 383 | alert(obj) : 384 | (typeof console != "undefined") && console.log(obj) 385 | ; 386 | }; 387 | 388 | 389 | TIMEOUT *= TIMEOUT; // by default, timeout is 10000 (10 seconds) 390 | // this is the place you can set it, e.g. 391 | // TIMEOUT = 2000; // 2 seconds 392 | 393 | wru.random = FALSE; // by default tests order is preseverd 394 | // set wru.random = TRUE to randomly sort them 395 | 396 | return wru; 397 | 398 | }(this); -------------------------------------------------------------------------------- /build/wru.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | (C) Andrea Giammarchi, @WebReflection - Mit Style License 3 | */ 4 | var wru=function(Y){function j(){A=K.call(m);if(A){if(typeof A=="function"){A={name:A[S]||"anonymous",test:A}}(P=l(l(Z.node,"div"),"span"))[E]=((ag(A,S)&&A[S])||(ag(A,e)&&A[e])||Q)+i+i;a=[];u=[];T=[];ab={};b("setup");T[ah]||b("test");N||r()}else{t()}}function p(aj){try{return O.call(h,aj)}catch(ai){return h.createElement(aj)}}function l(ai,aj){return ai.appendChild(p(aj))}function g(ai){P[E]=x.call(P[E],0,-2)+i+ai}function t(){var ak=Z.node.insertBefore(p("div"),Z.node.firstChild),al,aj,ai;if(ad){ai=aj="error";al="There Are Errors: "+ad}else{if(C){ai=aj="fail";al=C+" Tests Failed"}else{ai=aj="pass";al="Passed "+s+" Tests"}}Z.status=ai;ak[E]=""+al+"";ak.className=aj}function G(){var ai=this.lastChild.style;ai.display=ai.display=="none"?"block":"none"}function c(ai){P[E]+="";(P.onclick=G).call(P)}function r(){f();s+=a[ah];C+=u[ah];ad+=T[ah];g("("+v.call([a[ah],M=u[ah],T[ah]],", ")+")");P=P.parentNode;T[ah]?c(T,W="error"):(M?c(u,W="fail"):W="pass");P.className=W;M=0;W=i;j()}function b(ai){if(ag(A,ai)){try{A[ai](ab)}catch(aj){aa.call(T,i+aj)}}}function ag(aj,ai){return q.call(aj,ai)}function w(){return F()<0.5?-1:1}function f(){if(R){H(R);R=0}b("teardown")}var Z={timeout:y,assert:function U(aj,ai){if(arguments[ah]==1){ai=aj;aj=Q}z=I;aa.call(ai?a:u,W+aj);return ai},async:function V(al,ao,am,an){var ak,ai=am||Z.timeout||(Z.timeout=y);an=++N;if(typeof al=="function"){ai=ao||Z.timeout;ao=al;al="asynchronous test #"+an}am=X(function(){an=0;aa.call(u,al);--N||(R=X(r,0))},L(ai)||Z.timeout);return function aj(){if(!an){return}z=ae;W=al+": ";try{ak=ao.apply(this,arguments)}catch(ap){z=I;aa.call(T,W+ap)}W=i;if(z){H(am);--N||(R=X(r,0))}return ak}},test:function n(ai,aj){Z.after=aj||function(){};m=J.apply(m,[ai]);Z.random&&af.call(m,w);N||j()}},I=true,ae=!I,y=100,i=" ",Q="unknown",ah="length",S="name",e="description",D="
  • ",d="
  • ",k="\\|/-",q=Z.hasOwnProperty,W=i,ac=W.charAt,x=W.slice,m=[],J=m.concat,v=m.join,aa=m.push,K=m.shift,af=m.sort,N=0,M=0,s=0,C=0,ad=0,R=0,E="innerHTML",h=Y.document,O=h.createElement,B,L,F,X,H,A,P,a,u,T,ab,z;B=Y.Math;L=B.abs;F=B.random;X=Y.setTimeout;H=Y.clearTimeout;Z.node=(h.getElementById("wru")||h.body||h.documentElement);Y.setInterval(function(){N&&g(ac.call(k,M++%4))},y);undefined;Z.log=function o(aj,ai){ai?alert(aj):(typeof console!="undefined")&&console.log(aj)};y*=y;Z.random=ae;return Z}(this); -------------------------------------------------------------------------------- /builder/JSBuilder.js: -------------------------------------------------------------------------------- 1 | // JS builder by Andrea Giammarchi - mirror of the python one 2 | // Mit Style License 3 | // Note: this is not "extreme performances oriented" JS code 4 | // it should work on node.js 0.6+ 5 | var 6 | fs = require("fs"), 7 | exec = require('child_process').exec, 8 | zlib = require('zlib'), 9 | args = null 10 | ; 11 | 12 | function next() { 13 | args && queue(args); 14 | } 15 | 16 | function queue(queue) { 17 | for (var tmp, i = 0; i < queue.length; ++i) { 18 | tmp = queue[i]; 19 | if (typeof tmp == "string") console.log(tmp); 20 | else { 21 | args = queue.slice(i + 1); 22 | tmp(); 23 | return; 24 | } 25 | } 26 | args = null; 27 | } 28 | 29 | function fullPath(file) { 30 | file = __dirname + "/" + file; 31 | while (/([^/.]+)\/\.\.\//.test(file)) { 32 | file = file.replace(RegExp["$&"], ""); 33 | } 34 | return file; 35 | } 36 | 37 | function getSize(file) { 38 | var 39 | i = 0, 40 | sufix = ['bytes', 'Kb', 'Mb'], 41 | size = fs.statSync(fullPath(file)).size 42 | ; 43 | while (1023.0 < size) { 44 | size = size / 1024.0; 45 | i = i + 1; 46 | } 47 | return Math.round(size, 2) + ' ' + sufix[i]; 48 | } 49 | 50 | function read(file) { 51 | return fs.readFileSync(fullPath(file)); 52 | } 53 | 54 | function write(file, content) { 55 | fs.writeFileSync(fullPath(file), content); 56 | } 57 | 58 | function replace(content, search, replace) { 59 | content = content.toString(); 60 | for (var i = 0; i < search.length; ++i) { 61 | content = content.replace(search[i], replace[i]); 62 | } 63 | return content 64 | } 65 | 66 | function compile(copyright, fullName, minName, files, search, replace) { 67 | function onbuild(err, out) { 68 | write('../' + fullName, content); 69 | content = read('../' + minName); 70 | fs.createReadStream( 71 | fullPath('../' + minName) 72 | ).pipe( 73 | tmp = zlib.createGzip() 74 | ).pipe( 75 | fs.createWriteStream( 76 | fullPath('../' + minName + '.gz') 77 | ) 78 | ); 79 | tmp.on('end', function() { 80 | console.log('Full size: ' + getSize('../' + fullName)); 81 | console.log('Minified size: ' + getSize('../' + minName)); 82 | console.log('Minified size: ' + getSize('../' + minName + '.gz')); 83 | exec('rm "' + fullPath('../' + minName + '.gz') + '"', next); 84 | }); 85 | } 86 | var 87 | after = { 88 | and: function (_) { 89 | after._ = _; 90 | } 91 | }, 92 | multiCopyright = [ 93 | '/*!', copyright, '*/', 94 | '/**@license ' + copyright, '*/' 95 | ].join("\n"), 96 | i, tmp, content, cleanContent 97 | ; 98 | files = files.slice(); 99 | for (i = 0; i < files.length; ++i) { 100 | files[i] = read('../' + 'src/' + files[i]); 101 | } 102 | content = multiCopyright + "\n" + files.join("\n"); 103 | files = []; 104 | search && (content = replace(content, search, replace)); 105 | cleanContent = content.replace(/\/\/\^[^\0]*?\/\/\$[^\n\r]+/); 106 | write('../' + fullName, cleanContent); 107 | // YUICompressor 108 | exec('java -jar "' + fullPath('jar/yuicompressor-2.4.6.jar') + '" --type=js "' + fullPath('../' + fullName) + '" -o "' + fullPath('../' + minName) + '"', onbuild); 109 | // Uglify 110 | // exec('java -jar "' + fullPath('jar/js.jar') + '" "' + fullPath('uglify-js/exec.js') + '" "' + fullPath('uglify-js/uglify.js') + '" "' + fullPath('../' + fullName) + '" "' + copyright + '" > "' + fullPath('../' + minName) + '"', onbuild); 111 | // Closure Compiler 112 | // exec('java -jar "' + fullPath('jar/compiler.jar') + '" --compilation_level=SIMPLE_OPTIMIZATIONS --language_in ECMASCRIPT5_STRICT --js "' + fullPath('../' + fullName) + '" --js_output_file "' + fullPath('../' + minName) + '"', onbuild); 113 | } 114 | 115 | this.queue = queue; 116 | this.next = next; 117 | this.fullPath = fullPath; 118 | this.getSize = getSize; 119 | this.read = read; 120 | this.write = write; 121 | this.replace = replace; 122 | this.compile = compile; -------------------------------------------------------------------------------- /builder/JSBuilder.py_deprecated: -------------------------------------------------------------------------------- 1 | # python builder by Andrea Giammarchi 2 | # Mit Style License 3 | # Note: this is not "extreme performances oriented" Python code 4 | # it should work on Python 2.6+ tho 5 | import gzip, os, sys, string, re 6 | 7 | # I know this sucks but it kinda worked cross .py version ... 8 | def fullPath(file): 9 | i = len(os.path.split(sys.argv[0])[-1:][0]) 10 | return os.path.realpath(os.path.join(os.getcwd(), sys.argv[0][:-i], file)) 11 | 12 | # this could be surely optimized ... 13 | def getSize(file): 14 | i = 0 15 | sufix = ['bytes', 'Kb', 'Mb'] 16 | size = os.path.getsize(fullPath(file)) 17 | while 1023.0 < size: 18 | size = size / 1024.0 19 | i = i + 1 20 | return str(round(size, 2)) + ' ' + sufix[i] 21 | 22 | # this is a handy shortcut ... 23 | def read(file): 24 | f = open(fullPath(file), 'r') 25 | content = f.read() 26 | f.close() 27 | return content 28 | 29 | # ... as well as this one 30 | def write(file, content): 31 | f = open(fullPath(file), 'w') 32 | f.write(content) 33 | f.close() 34 | 35 | # utility 36 | def replace(content, search, replace): 37 | for i in range(len(search)): 38 | content = string.replace(content, search[i], replace[i]) 39 | return content 40 | 41 | # well ... this simply works as well :-D 42 | def compile(copyright, fullName, minName, files, search=None, replace=None): 43 | 44 | # create a copyright compatible with both YUICompressor and Google Closure Compiler 45 | multiCopyright = "\n".join([ 46 | '/*!', copyright, '*/', 47 | '/**@license ' + copyright, '*/' 48 | ]) 49 | 50 | #copy the list temporarely 51 | files = files[:] 52 | 53 | # read all files 54 | for i in range(len(files)): 55 | files[i] = read('../' + 'src/' + files[i]) 56 | 57 | # address the whole content 58 | content = multiCopyright + "\n".join(files) 59 | files = [] # just in case ... 60 | 61 | # replace something if necessary 62 | if search != None: 63 | content = replace(content, search, replace) 64 | 65 | # strip out code that should not be in the minified version 66 | cleanContent = re.sub(r'//\^[^\x00]*?//\$[^\n\r]+', '', content) 67 | 68 | # write the whole (cleaned) content 69 | write('../' + fullName, cleanContent) 70 | 71 | # MINIFY! 72 | 73 | # YUICompressor [faster, less greedy, bigger size] 74 | os.system('java -jar "' + fullPath('jar/yuicompressor-2.4.6.jar') + '" --type=js "' + fullPath('../' + fullName) + '" -o "' + fullPath('../' + minName) + '"') 75 | 76 | # Uglify JS [good performances, mediumly greedy, medium size] 77 | # os.system('java -jar "' + fullPath('jar/js.jar') + '" "' + fullPath('uglify-js/exec.js') + '" "' + fullPath('uglify-js/uglify.js') + '" "' + fullPath('../' + fullName) + '" "' + copyright + '" > "' + fullPath('../' + minName) + '"') 78 | 79 | # Google Closure Compiler [slowest, more greedy, smaller size] 80 | # os.system('java -jar "' + fullPath('jar/compiler.jar') + '" --compilation_level=SIMPLE_OPTIMIZATIONS --language_in ECMASCRIPT5_STRICT --js "' + fullPath('../' + fullName) + '" --js_output_file "' + fullPath('../' + minName) + '"') 81 | 82 | # put back code that should have not been included in the minified version 83 | write('../' + fullName, content) 84 | 85 | # create the gzip version 86 | content = read('../' + minName) 87 | tmp = gzip.open(fullPath('../' + minName + '.gz'), 'w') 88 | tmp.write(content) 89 | tmp.close() 90 | 91 | # print out the result of all precedent operations 92 | print('Full size: ' + getSize('../' + fullName)) 93 | print('Minified size: ' + getSize('../' + minName)) 94 | print('Min + Gzip size: ' + getSize('../' + minName + '.gz')) 95 | 96 | # remove the gzipped version 97 | os.remove(fullPath('../' + minName + '.gz')) 98 | -------------------------------------------------------------------------------- /builder/JSBuilder.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/wru/705b9629ce0eb1f86989f88fde4dc39796c1d545/builder/JSBuilder.pyc -------------------------------------------------------------------------------- /builder/build.js: -------------------------------------------------------------------------------- 1 | var 2 | copyright = '(C) Andrea Giammarchi, @WebReflection - Mit Style License', 3 | JSBuilder = require("./JSBuilder") 4 | ; 5 | 6 | JSBuilder.queue([ 7 | "", 8 | "-----------------------", 9 | "| wru DOM version |", 10 | "-----------------------", 11 | function () { 12 | JSBuilder.compile( 13 | copyright, 14 | 'build/wru.dom.js', 15 | 'build/wru.min.js', 16 | [ 17 | "wru.intro.js", 18 | "wru.DOM.functions.js", 19 | "wru.functions.js", 20 | "wru.js", 21 | "wru.variables.js", 22 | "wru.DOM.variables.js", 23 | "wru.shared.js", 24 | "wru.global.shortcuts.js", 25 | "wru.DOM.node.js", 26 | "wru.DOM.cursor.js", 27 | "wru.debug.js", 28 | "wru.DOM.log.js", 29 | "wru.outro.js" 30 | ] 31 | ); 32 | }, 33 | "----------------------", 34 | "", 35 | "", 36 | "-----------------------", 37 | "| wru console version |", 38 | "-----------------------", 39 | function () { 40 | JSBuilder.compile( 41 | copyright, 42 | 'build/wru.console.max.js', 43 | 'build/wru.console.js', 44 | [ 45 | "rhinoTimers.js", 46 | "wru.intro.js", 47 | "wru.console.functions.js", 48 | "wru.functions.js", 49 | "wru.js", 50 | "wru.variables.js", 51 | "wru.console.variables.js", 52 | "wru.shared.js", 53 | "wru.console.log.js", 54 | "wru.export.js", 55 | "wru.global.shortcuts.js", 56 | "wru.console.cursor.js", 57 | "wru.debug.js", 58 | "wru.outro.js" 59 | ] 60 | ); 61 | }, 62 | function () { 63 | JSBuilder.write( 64 | '../build/wru.console.js', 65 | JSBuilder.replace( 66 | JSBuilder.read('../build/wru.console.js'), 67 | [ 68 | /\w+=global.setInterval=/, 69 | /\w+=global.setTimeout=/, 70 | /\w+=global.clearInterval=/, 71 | /\w+=global.clearTimeout=/ 72 | ], 73 | [ 74 | 'setInterval=global.setInterval=', 75 | 'setTimeout=global.setTimeout=', 76 | 'clearInterval=global.clearInterval=', 77 | 'clearTimeout=global.clearTimeout=' 78 | ] 79 | ) 80 | ); 81 | JSBuilder.next(); 82 | }, 83 | "----------------------", 84 | "", 85 | function () { 86 | JSBuilder.write( 87 | '../build/template.html', 88 | JSBuilder.replace( 89 | JSBuilder.read('../src/template.html'), 90 | [ 91 | '{{CSS}}', 92 | '{{JS}}', 93 | 'var wru=', 94 | '}(this);' 95 | ], 96 | [ 97 | JSBuilder.read('../src/template.css'), 98 | JSBuilder.read('../build/wru.min.js'), 99 | 'wru(', 100 | '}(this));' 101 | ] 102 | ) 103 | ); 104 | JSBuilder.next(); 105 | }, 106 | function () { 107 | JSBuilder.write( 108 | '../build/test.html', 109 | JSBuilder.replace( 110 | JSBuilder.read('../src/test.html'), 111 | [ 112 | '{{CSS}}', 113 | '{{JS}}', 114 | 'var wru=', 115 | '}(this);' 116 | ], 117 | [ 118 | JSBuilder.read('../src/template.css'), 119 | JSBuilder.read('../build/wru.min.js'), 120 | 'wru(', 121 | '}(this));' 122 | ] 123 | ) 124 | ); 125 | JSBuilder.next(); 126 | }, 127 | function () { 128 | JSBuilder.write( 129 | '../build/template.js', 130 | JSBuilder.replace( 131 | JSBuilder.read('../src/template.js'), 132 | [ 133 | '{{JS}}', 134 | 'var wru=', 135 | '}(this);' 136 | ], 137 | [ 138 | JSBuilder.read('../build/wru.console.js'), 139 | 'wru(', 140 | '}(this));' 141 | ] 142 | ) 143 | ); 144 | JSBuilder.next(); 145 | }, 146 | function () { 147 | JSBuilder.write( 148 | '../build/template.phantom.js', 149 | 'var page=new WebPage;page.open(phantom.args[0]||"about:blank",function(){page.evaluate(function(){' + JSBuilder.replace( 150 | JSBuilder.read('../src/template.js'), 151 | [ 152 | '{{JS}}', 153 | 'var wru=', 154 | '}(this);' 155 | ], 156 | [ 157 | 'window.phantomExit=false;window.quit=function(){window.phantomExit=true};window.require=function(){return{wru:window.wru}};window.global=window;\n' + JSBuilder.read('../build/wru.console.js'), 158 | 'wru(', 159 | '}(this));' 160 | ] 161 | ) + '\n});page.onConsoleMessage=function(msg){if (!/^\s+(?:\\\\|\\/|\\||\\-)/.test(msg))console.log(msg.replace("\\n",""))};setInterval(function(){page.evaluate(function(){return window.phantomExit})&&phantom.exit()})});' 162 | ); 163 | JSBuilder.next(); 164 | }, 165 | function () { 166 | JSBuilder.write( 167 | '../node/wru.console.js', 168 | JSBuilder.read('../build/wru.console.js') 169 | ); 170 | JSBuilder.next(); 171 | }, 172 | function () { 173 | require('child_process').exec('cp build/wru.console.js node/wru.console.js', JSBuilder.next); 174 | }, 175 | function () { 176 | require('child_process').exec('cp src/program.js node/program.js', JSBuilder.next); 177 | }, 178 | "done" 179 | ]); -------------------------------------------------------------------------------- /builder/build.py_deprecated: -------------------------------------------------------------------------------- 1 | # JSBuilder http://code.google.com/p/javascript-builder/ 2 | 3 | copyright = '(C) Andrea Giammarchi, @WebReflection - Mit Style License' 4 | 5 | import JSBuilder, string, re 6 | 7 | # embedded DOM version for HTML tests 8 | print ("") 9 | print ("-----------------------") 10 | print ("| wru DOM version |") 11 | print ("-----------------------") 12 | JSBuilder.compile( 13 | copyright, 14 | 'build/wru.dom.js', 15 | 'build/wru.min.js', 16 | [ 17 | "wru.intro.js", 18 | "wru.DOM.functions.js", 19 | "wru.functions.js", 20 | "wru.js", 21 | "wru.variables.js", 22 | "wru.DOM.variables.js", 23 | "wru.shared.js", 24 | "wru.global.shortcuts.js", 25 | "wru.DOM.node.js", 26 | "wru.DOM.cursor.js", 27 | "wru.debug.js", 28 | "wru.DOM.log.js", 29 | "wru.outro.js" 30 | ] 31 | ) 32 | print ("----------------------") 33 | print ("") 34 | 35 | # node.js / Rhino console version 36 | print ("") 37 | print ("-----------------------") 38 | print ("| wru console version |") 39 | print ("-----------------------") 40 | JSBuilder.compile( 41 | copyright, 42 | 'build/wru.console.max.js', 43 | 'build/wru.console.js', 44 | [ 45 | "rhinoTimers.js", 46 | "wru.intro.js", 47 | "wru.console.functions.js", 48 | "wru.functions.js", 49 | "wru.js", 50 | "wru.variables.js", 51 | "wru.console.variables.js", 52 | "wru.shared.js", 53 | "wru.console.log.js", 54 | "wru.export.js", 55 | "wru.global.shortcuts.js", 56 | "wru.console.cursor.js", 57 | "wru.debug.js", 58 | "wru.outro.js" 59 | ] 60 | ) 61 | 62 | # YUICompressor bug fixed after build 63 | compiled = JSBuilder.read('../build/wru.console.js') 64 | compiled = re.sub(r'\w+=global.setInterval=', 'setInterval=global.setInterval=', compiled) 65 | compiled = re.sub(r'\w+=global.setTimeout=', 'setTimeout=global.setTimeout=', compiled) 66 | compiled = re.sub(r'\w+=global.clearInterval=', 'clearInterval=global.clearInterval=', compiled) 67 | compiled = re.sub(r'\w+=global.clearTimeout=', 'clearTimeout=global.clearTimeout=', compiled) 68 | JSBuilder.write('../build/wru.console.js', compiled) 69 | 70 | print ("----------------------") 71 | print ("") 72 | 73 | # web 74 | JSBuilder.write( 75 | '../build/template.html', 76 | JSBuilder.replace( 77 | JSBuilder.read('../src/template.html'), 78 | [ 79 | '{{CSS}}', 80 | '{{JS}}', 81 | 'var wru=', 82 | '}(this);' 83 | ], 84 | [ 85 | JSBuilder.read('../src/template.css'), 86 | JSBuilder.read('../build/wru.min.js'), 87 | 'wru(', 88 | '}(this));' 89 | ] 90 | ) 91 | ) 92 | 93 | # server 94 | JSBuilder.write( 95 | '../build/template.js', 96 | JSBuilder.replace( 97 | JSBuilder.read('../src/template.js'), 98 | [ 99 | '{{JS}}', 100 | 'var wru=', 101 | '}(this);' 102 | ], 103 | [ 104 | JSBuilder.read('../build/wru.console.js'), 105 | 'wru(', 106 | '}(this));' 107 | ] 108 | ) 109 | ) 110 | 111 | # phantomjs 112 | JSBuilder.write( 113 | '../build/template.phantom.js', 114 | 'var page=new WebPage;page.open(phantom.args[0]||"about:blank",function(){page.evaluate(function(){' + JSBuilder.replace( 115 | JSBuilder.read('../src/template.js'), 116 | [ 117 | '{{JS}}', 118 | 'var wru=', 119 | '}(this);' 120 | ], 121 | [ 122 | 'window.phantomExit=false;window.quit=function(){window.phantomExit=true};window.require=function(){return{wru:window.wru}};window.global=window;\n' + JSBuilder.read('../build/wru.console.js'), 123 | 'wru(', 124 | '}(this));' 125 | ] 126 | ) + '\n});page.onConsoleMessage=function(msg){if (!/^\s+(?:\\\\|\\/|\\||\\-)/.test(msg))console.log(msg.replace("\\n",""))};setInterval(function(){page.evaluate(function(){return window.phantomExit})&&phantom.exit()})});' 127 | ) 128 | 129 | JSBuilder.write( 130 | '../node/wru.console.js', 131 | JSBuilder.read('../build/wru.console.js') 132 | ); 133 | 134 | import os 135 | os.system('cp build/wru.console.js node/wru.console.js'); 136 | 137 | # let me read the result ... 138 | import time 139 | time.sleep(2) -------------------------------------------------------------------------------- /builder/jar/js.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/wru/705b9629ce0eb1f86989f88fde4dc39796c1d545/builder/jar/js.jar -------------------------------------------------------------------------------- /builder/jar/yuicompressor-2.4.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/wru/705b9629ce0eb1f86989f88fde4dc39796c1d545/builder/jar/yuicompressor-2.4.6.jar -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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= 0.4.0" } 17 | , "bin": { 18 | "wru": "./node/program.js" 19 | } 20 | } -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | wru 5 | 6 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | 40 |
    41 | 44 | 45 | -------------------------------------------------------------------------------- /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}} -------------------------------------------------------------------------------- /src/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | wru test 5 | 21 | 52 | 53 | 54 | 57 | 58 | 59 |
    60 | 63 | 64 | -------------------------------------------------------------------------------- /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.DOM.functions.js: -------------------------------------------------------------------------------- 1 | // DOM 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 | (node = putItThereAndGimmeBack( 9 | putItThereAndGimmeBack(wru.node, "div"), 10 | "span" 11 | ))[INNERHTML] = ( 12 | (iHasIt(current, NAME) && current[NAME]) 13 | || 14 | (iHasIt(current, DESCRIPTION) && current[DESCRIPTION]) 15 | || 16 | UNKNOWN 17 | ) + EMPTY + EMPTY; 18 | pass = []; 19 | fail = []; 20 | fatal = []; 21 | tmp = {}; 22 | giveItATry("setup"); 23 | fatal[LENGTH] || giveItATry("test"); 24 | waitForIt || Dary(); 25 | } else { 26 | showSummary(); 27 | } 28 | } 29 | 30 | function create(nodeName) { 31 | try { 32 | return createElement.call(document, nodeName); 33 | } catch($) { 34 | // unfortuantely IE < 7 does not support host objects via call 35 | return document.createElement(nodeName); 36 | } 37 | } 38 | 39 | function putItThereAndGimmeBack(node, nodeName) { 40 | return node.appendChild(create(nodeName)); 41 | } 42 | 43 | function passTheInfo(info) { 44 | node[INNERHTML] = slice.call(node[INNERHTML], 0, -2) + EMPTY + info; 45 | } 46 | 47 | function showSummary() { 48 | var 49 | node = wru.node.insertBefore( 50 | create("div"), 51 | wru.node.firstChild 52 | ), 53 | innerHTML, 54 | className, 55 | status 56 | ; 57 | if (overallFatal) { 58 | status = className = "error"; 59 | innerHTML = "There Are Errors: " + overallFatal; 60 | } else if(overallFail) { 61 | status = className = "fail"; 62 | innerHTML = overallFail + " Tests Failed"; 63 | } else { 64 | status = className = "pass"; 65 | innerHTML = "Passed " + overallPass + " Tests"; 66 | } 67 | wru.status = status; 68 | node[INNERHTML] = "" + innerHTML + ""; 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] += "
      " + LISTART + join.call(fail, LIEND + LISTART) + LIEND + "
    "; 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 | -------------------------------------------------------------------------------- /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.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.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.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.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.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.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 = "------------------------------", -------------------------------------------------------------------------------- /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.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.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.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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.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.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/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 | }]); -------------------------------------------------------------------------------- /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/solutions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | wru 5 | 118 | 119 | 120 | 121 | 122 | 123 | 155 | 156 | 157 |
    158 | 164 | 165 | -------------------------------------------------------------------------------- /test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | wru :: essential unit test framework 5 | 6 | 7 | 8 |
    9 | 10 | 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------