├── .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",d="",i="\\|/-",m=V.hasOwnProperty,S=g,Y=S.charAt,t=S.slice,j=[],E=j.concat,r=j.join,W=j.push,F=j.shift,ac=j.sort,I=0,H=0,o=0,z=0,aa=0,M=0,N="\x1B[1;31mERROR\x1B[0m",J="\x1B[0;31mFAILURE\x1B[0m",y="\x1B[0;32mOK\x1B[0m",Z="------------------------------",x,G,B,T,C,w,K,a,q,P,X,v;V.log=function(ah,ag){try{if(ag){throw new Error}console.log(ah)}catch(af){l(ah,0)}};if(typeof __dirname!="undefined"){U.wru=V;U.assert=V.assert;U.async=V.async;U.test=V.test;U.log=V.log;U.random=false;Object.defineProperty(U,"status",{get:function(){return V.status}});Object.defineProperty(U,"timeout",{get:function(){return V.timeout},set:function(af){V.timeout=parseInt(af,10)||V.timeout}});U=global}x=U.Math;G=x.abs;B=x.random;T=U.setTimeout;C=U.clearTimeout;U.setInterval(function(){I&&l(g+Y.call(i,H++%4)+"\b\b",true)},u);undefined;u*=u;V.random=ab;return V}(this));
--------------------------------------------------------------------------------
/build/template.phantom.js:
--------------------------------------------------------------------------------
1 | var page=new WebPage;page.open(phantom.args[0]||"about:blank",function(){page.evaluate(function(){// 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 | window.phantomExit=false;window.quit=function(){window.phantomExit=true};window.require=function(){return{wru:window.wru}};window.global=window;
33 | /*!
34 | (C) Andrea Giammarchi, @WebReflection - Mit Style License
35 | */
36 | 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",d="",i="\\|/-",m=V.hasOwnProperty,S=g,Y=S.charAt,t=S.slice,j=[],E=j.concat,r=j.join,W=j.push,F=j.shift,ac=j.sort,I=0,H=0,o=0,z=0,aa=0,M=0,N="\x1B[1;31mERROR\x1B[0m",J="\x1B[0;31mFAILURE\x1B[0m",y="\x1B[0;32mOK\x1B[0m",Z="------------------------------",x,G,B,T,C,w,K,a,q,P,X,v;V.log=function(ah,ag){try{if(ag){throw new Error}console.log(ah)}catch(af){l(ah,0)}};if(typeof __dirname!="undefined"){U.wru=V;U.assert=V.assert;U.async=V.async;U.test=V.test;U.log=V.log;U.random=false;Object.defineProperty(U,"status",{get:function(){return V.status}});Object.defineProperty(U,"timeout",{get:function(){return V.timeout},set:function(af){V.timeout=parseInt(af,10)||V.timeout}});U=global}x=U.Math;G=x.abs;B=x.random;T=U.setTimeout;C=U.clearTimeout;U.setInterval(function(){I&&l(g+Y.call(i,H++%4)+"\b\b",true)},u);undefined;u*=u;V.random=ab;return V}(this));
37 | });page.onConsoleMessage=function(msg){if (!/^s+(?:\\|\/|\||\-)/.test(msg))console.log(msg.replace("\n",""))};setInterval(function(){page.evaluate(function(){return window.phantomExit})&&phantom.exit()})});
--------------------------------------------------------------------------------
/build/test.html:
--------------------------------------------------------------------------------
1 |
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",d="",i="\\|/-",m=V.hasOwnProperty,S=g,Y=S.charAt,t=S.slice,j=[],E=j.concat,r=j.join,W=j.push,F=j.shift,ac=j.sort,I=0,H=0,o=0,z=0,aa=0,M=0,N="\x1B[1;31mERROR\x1B[0m",J="\x1B[0;31mFAILURE\x1B[0m",y="\x1B[0;32mOK\x1B[0m",Z="------------------------------",x,G,B,T,C,w,K,a,q,P,X,v;V.log=function(ah,ag){try{if(ag){throw new Error}console.log(ah)}catch(af){l(ah,0)}};if(typeof __dirname!="undefined"){U.wru=V;U.assert=V.assert;U.async=V.async;U.test=V.test;U.log=V.log;U.random=false;Object.defineProperty(U,"status",{get:function(){return V.status}});Object.defineProperty(U,"timeout",{get:function(){return V.timeout},set:function(af){V.timeout=parseInt(af,10)||V.timeout}});U=global}x=U.Math;G=x.abs;B=x.random;T=U.setTimeout;C=U.clearTimeout;U.setInterval(function(){I&&l(g+Y.call(i,H++%4)+"\b\b",true)},u);undefined;u*=u;V.random=ab;return V}(this);
--------------------------------------------------------------------------------
/build/wru.console.max.js:
--------------------------------------------------------------------------------
1 | /*!
2 | (C) Andrea Giammarchi, @WebReflection - Mit Style License
3 | */
4 | /**@license (C) Andrea Giammarchi, @WebReflection - Mit Style License
5 | */
6 | // revisited by Andrea Giammarchi, @WebReflection
7 | // compatible with both Rhino and Node
8 | // now it is possible to include this file in the server console without rhinoTimers dependencies
9 | // @link http://stackoverflow.com/questions/2261705/how-to-run-a-javascript-function-asynchronously-without-using-settimeout
10 | // glory and fortune to to Weston C for the inital hint
11 | // but it's also RIDICULOUS Rhino does not implement in core timers properly!
12 |
13 | // condition to avoid problems with jsc
14 | if (typeof global != "undefined") {
15 |
16 | var
17 | setTimeout = global.setTimeout,
18 | setInterval = global.setInterval,
19 | clearInterval = global.clearInterval,
20 | clearTimeout = global.clearTimeout
21 | ;
22 |
23 | setTimeout || (function (timer, ids, slice, counter) {
24 |
25 | // did you know?
26 | // all browsers but IE accept one or more arguments
27 | // to pass to the callbacl after the timer/delay number
28 | // ... so does Rhino now!
29 |
30 | setInterval = global.setInterval = function setInterval(fn, delay) {
31 | return schedule(fn, delay, slice.call(arguments, 2), 1);
32 | };
33 |
34 | setTimeout = global.setTimeout = function setTimeout(fn, delay) {
35 | return schedule(fn, delay, slice.call(arguments, 2));
36 | };
37 |
38 | clearInterval = global.clearInterval =
39 | clearTimeout = global.clearTimeout = function clearInterval(id) {
40 | ids[id].cancel();
41 | timer.purge();
42 | delete ids[id];
43 | };
44 |
45 | function schedule(fn, delay, args, interval) {
46 | var id = ++counter;
47 | ids[id] = new JavaAdapter(java.util.TimerTask,{run: function () {
48 | fn.apply(null, args);
49 | }});
50 | interval ?
51 | timer.schedule(ids[id], delay, delay)
52 | :
53 | timer.schedule(ids[id], delay)
54 | ;
55 | return id;
56 | }
57 |
58 | })(new java.util.Timer(), {}, [].slice, 0);
59 |
60 | } else { // jsc specific hack
61 | !function (global, i, cbs, slice) {
62 | function setTimeout(cb, delay) {
63 | var t = new Date;
64 | while (new Date - t < delay);
65 | cb.apply(null, slice.call(arguments, 2));
66 | }
67 | slice = cbs.slice;
68 | global.setTimeout = global.setInterval = setTimeout;
69 | global.clearInterval = global.clearTimeout = function () {};
70 | }(this, 0, []);
71 | }
72 |
73 | var wru = function (window) {"use strict";
74 |
75 | /**
76 | * Copyright (C) 2011 by Andrea Giammarchi, @WebReflection
77 | *
78 | * Permission is hereby granted, free of charge, to any person obtaining a copy
79 | * of this software and associated documentation files (the "Software"), to deal
80 | * in the Software without restriction, including without limitation the rights
81 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
82 | * copies of the Software, and to permit persons to whom the Software is
83 | * furnished to do so, subject to the following conditions:
84 | *
85 | * The above copyright notice and this permission notice shall be included in
86 | * all copies or substantial portions of the Software.
87 | *
88 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
89 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
90 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
91 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
92 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
93 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
94 | * THE SOFTWARE.
95 | */
96 |
97 | // console specific version
98 | function isGonnaBeLegen() {
99 | current = shift.call(queue);
100 | if (current) {
101 | if (typeof current == "function") {
102 | current = {name: current[NAME] || "anonymous", test: current};
103 | }
104 | log(OUTPUT_SEPARATOR);
105 | log(
106 | (iHasIt(current, NAME) && current[NAME])
107 | ||
108 | (iHasIt(current, DESCRIPTION) && current[DESCRIPTION])
109 | ||
110 | UNKNOWN
111 | );
112 | pass = [];
113 | fail = [];
114 | fatal = [];
115 | tmp = {};
116 | giveItATry("setup");
117 | fatal[LENGTH] || giveItATry("test");
118 | waitForIt || Dary();
119 | } else {
120 | showSummary();
121 | }
122 | }
123 |
124 | function log(info, avoidNewLine) {
125 | info = info + (avoidNewLine ? "" : "\n");
126 | try {
127 | // node 0.11+ alternative ...
128 | process.stdout.write(info);
129 | } catch(up) {
130 | try {
131 | // node 0.6
132 | require("util").print(info);
133 | } catch(up) {
134 | try {
135 | // node 0.4
136 | require("sys").print(info);
137 | } catch(up) {
138 | try {
139 | // hello Rhino
140 | // print uses println ... while we need print without \n
141 | java.lang.System.out.print(info);
142 | } catch(up) {
143 | try {
144 | // phantomjs or default fallback
145 | console.log(info);
146 | } catch(up) {
147 | // jsc and others
148 | print(info);
149 | }
150 | }
151 | }
152 | }
153 | }
154 | }
155 |
156 | function showSummary() {
157 | var code = 0, status;
158 | log(EMPTY);
159 | log(OUTPUT_SEPARATOR);
160 | switch (true) {
161 | case !!overallFatal:
162 | code++;
163 | status = "error";
164 | log(ERROR + " " + overallFatal + " Errors");
165 | break;
166 | case !!overallFail:
167 | code++;
168 | status = "fail";
169 | log(FAILURE + EMPTY + overallFail + " Failures");
170 | break;
171 | default:
172 | status = "pass";
173 | log(OK + " " + overallPass + " Passes");
174 | }
175 | wru.status = status;
176 | log(OUTPUT_SEPARATOR);
177 | log(EMPTY);
178 | wru.after();
179 | try {
180 | // node.js
181 | process.exit(code);
182 | } catch(up) {
183 | // rhino
184 | quit();
185 | }
186 | }
187 |
188 | function writeItOrdered(fail) {
189 | for (var
190 | i = 0, length = fail[LENGTH];
191 | i < length;
192 | log(" " + (++i) + ". " + fail[i - 1])
193 | );
194 | }
195 |
196 | function Dary() {
197 | clearDaryTimeou();
198 | overallPass += pass[LENGTH];
199 | overallFail += fail[LENGTH];
200 | overallFatal += fatal[LENGTH];
201 | if (fatal[LENGTH]) {
202 | prefix = ERROR;
203 | writeItOrdered(fatal);
204 | } else if(fail[LENGTH]) {
205 | prefix = FAILURE;
206 | writeItOrdered(fail);
207 | } else {
208 | prefix = OK;
209 | }
210 | log(prefix + " passes: " + pass[LENGTH] + ", fails: " + fail[LENGTH] + ", errors: " + fatal[LENGTH]);
211 | ci = 0;
212 | prefix = EMPTY;
213 | isGonnaBeLegen();
214 | }
215 |
216 | // common functions for all versions
217 | function giveItATry(name) {
218 | if (iHasIt(current, name)) {
219 | try {
220 | current[name](tmp);
221 | } catch(doooodeThisIsBAD) {
222 | push.call(fatal, EMPTY + doooodeThisIsBAD);
223 | }
224 | }
225 | }
226 |
227 | function iHasIt(object, name) {
228 | return hasOwnProperty.call(object, name);
229 | }
230 |
231 | function messItUp() {
232 | return random() < .5 ? -1 : 1;
233 | }
234 |
235 | function clearDaryTimeou() {
236 | if (daryTimeout) {
237 | clearTimeout(daryTimeout);
238 | daryTimeout = 0;
239 | }
240 | giveItATry("teardown");
241 | }
242 |
243 |
244 | var // wru library core
245 | wru = {
246 | timeout: TIMEOUT,
247 | assert: function assert(description, result) {
248 |
249 | // if no description provided, variables are shifted
250 | // these are both valid wru.assert calls indeed
251 | // wru.assert(truishValue);
252 | // wru.assert("test description", truishValue);
253 | if (arguments[LENGTH] == 1) {
254 | result = description;
255 | description = UNKNOWN;
256 | }
257 |
258 | // flag used in wru.async to verify at least
259 | // one assertion was performed
260 | called = TRUE;
261 |
262 | // store the result in the right collection
263 | push.call(result ? pass : fail, prefix + description);
264 |
265 | // just to add a bit of sugar
266 | return result;
267 | },
268 | async: function async(description, callback, timeout, p) {
269 | var r, delay = timeout || wru.timeout || (wru.timeout = TIMEOUT);
270 | // p is used as sentinel
271 | // it defines the anonymous name
272 | // if necessary and it's used to flag the timeout
273 | p = ++waitForIt;
274 |
275 | // if no description provided, variables are shifted
276 | // these are all valid wru.async calls indeed, timeout is optional
277 | // wru.async(function () { ... })
278 | // wru.async("test description", function () { ... })
279 | // wru.async(function () { ... }, timeout)
280 | // wru.async("test description", function () { ... }, timeout)
281 | if (typeof description == "function") {
282 | delay = callback || wru.timeout;
283 | callback = description;
284 | description = "asynchronous test #" + p;
285 | }
286 |
287 | // if in *TIMEOUT* time nothing happens ...
288 | timeout = setTimeout(function () {
289 |
290 | // p is flagged as 0
291 | p = 0;
292 |
293 | // timeout is handled as failure, not error (could be the server)
294 | push.call(fail, description);
295 |
296 | // if there is no reason to waitForIt then is time to call Dary()
297 | --waitForIt || (daryTimeout = setTimeout(Dary, 0));
298 | },
299 | // timeout can be specified
300 | // this procedure ensure that it's
301 | // a number and it's greater than 0
302 | abs(delay) || wru.timeout
303 | );
304 |
305 | // the async function is a wrap of the passed callback
306 | return function async() {
307 |
308 | // if it's executed after the timeout nothing happens
309 | // since the failure has been already notified
310 | if (!p) return;
311 |
312 | // called is always set as *TRUE* during any assertion
313 | // this indicates if the callback made at least one assertion
314 | // as example, in this case the callback could be called many time
315 | // with different readyState ... however, only on readyState 4
316 | // there is the assertion we are interested about, e.g.
317 | //
318 | // xhr.onreadystatechange = wru.async(function (){
319 | // if (this.readyState == 4)
320 | // wru.assert("content", this.responseText.length)
321 | // ;
322 | // });
323 | //
324 | // in above example called will be flagged as true
325 | // only during last readyState call
326 | called = FALSE;
327 |
328 | // simply recycled "string" variable
329 | // prefix will be internally used by assert during function execution
330 | prefix = description + ": ";
331 |
332 | // the original callback is called with proper *this* if specified
333 | try {
334 | r = callback.apply(this, arguments);
335 | } catch(doooodeThisIsBAD) {
336 | // if there is an Error
337 | // the test is screwed up
338 | // called has to be set as *TRUE* to invalidate the test
339 | called = TRUE;
340 | // message is "casted" to avoid IE host objects errors problem
341 | // (or any other possible edge case)
342 | push.call(fatal, prefix + doooodeThisIsBAD);
343 | }
344 |
345 | // prefix can be *EMPTY* string again now
346 | prefix = EMPTY;
347 |
348 | // a failure or at least an assertion
349 | if (called) {
350 |
351 | // timeout not necessary anymore
352 | clearTimeout(timeout);
353 |
354 | // if there is no reason to waitForIt then is time to call Dary()
355 | --waitForIt || (daryTimeout = setTimeout(Dary, 0));
356 | }
357 |
358 | // return the eventual callback value
359 | return r;
360 | };
361 | },
362 |
363 | // wru.test({...test...})
364 | // wru.test([{...test...}, {...test...}, ...])
365 | // the {...test...} object should have a string name and a function test property
366 | // optionally a function setup and a function teardown too
367 | test: function test(list, after) {
368 |
369 | // in case you need to do something after
370 | wru.after = after || function () {};
371 |
372 | // test may be called multiple times
373 | // queue should simply concatenate other calls
374 | queue = concat.apply(queue, [list]);
375 |
376 | // if wru.random is true, the queue is ranodomized
377 | // this is to make tests indipendent from each others
378 | wru.random && sort.call(queue, messItUp);
379 |
380 | // if there is no test to waitForIt
381 | // Dary() has been called already
382 | // we can procede with next test
383 | // invoking isGonnaBeLegen()
384 | waitForIt || isGonnaBeLegen();
385 | }
386 | },
387 |
388 | // common private variables / constants / shortcuts
389 | TRUE = true,
390 | FALSE = !TRUE,
391 | TIMEOUT = 100,
392 | EMPTY = " ",
393 | UNKNOWN = "unknown",
394 | LENGTH = "length",
395 | NAME = "name",
396 | DESCRIPTION = "description",
397 | LISTART = "
",
398 | LIEND = "
",
399 | cursor = "\\|/-",
400 | hasOwnProperty = wru.hasOwnProperty,
401 | prefix = EMPTY,
402 | charAt = prefix.charAt,
403 | slice = prefix.slice,
404 | queue = [],
405 | concat = queue.concat,
406 | join = queue.join,
407 | push = queue.push,
408 | shift = queue.shift,
409 | sort = queue.sort,
410 | waitForIt = 0,
411 | ci = 0,
412 | overallPass = 0,
413 | overallFail = 0,
414 | overallFatal = 0,
415 | daryTimeout = 0,
416 |
417 |
418 | // these variables are used on console version only
419 | ERROR = "\x1B[1;31mERROR\x1B[0m",
420 | FAILURE = "\x1B[0;31mFAILURE\x1B[0m",
421 | OK = "\x1B[0;32mOK\x1B[0m",
422 | OUTPUT_SEPARATOR = "------------------------------",
423 |
424 | // shared across the whole private scope
425 | Math, abs, random, setTimeout, clearTimeout,
426 | current, node, pass, fail, fatal, tmp, called
427 | ;
428 |
429 |
430 | wru.log = function (obj, printOnly) {
431 | try {
432 | if (printOnly) {
433 | throw new Error;
434 | }
435 | console.log(obj);
436 | } catch(o_O) {
437 | log(obj, 0);
438 | }
439 | };
440 |
441 | // node.js exports
442 | if (typeof __dirname != "undefined") {
443 |
444 | window.wru = wru;
445 | window.assert = wru.assert;
446 | window.async = wru.async;
447 | window.test = wru.test;
448 | window.log = wru.log;
449 | window.random = false;
450 | Object.defineProperty(window, "status", {get: function () {
451 | return wru.status;
452 | }});
453 | Object.defineProperty(window, "timeout", {
454 | get: function () {
455 | return wru.timeout;
456 | },
457 | set: function (value) {
458 | wru.timeout = parseInt(value, 10) || wru.timeout;
459 | }
460 | });
461 |
462 | // re-assign window to make it global
463 | window = global;
464 | }
465 |
466 | // these are window/global object dependent
467 | // must be eventually defined after wru.export.js, if used
468 | Math = window.Math;
469 | abs = Math.abs;
470 | random = Math.random;
471 | setTimeout = window.setTimeout;
472 | clearTimeout = window.clearTimeout;
473 |
474 | // "THE CURSOR" http://3site.eu/cursor
475 | window.setInterval(function () {
476 | waitForIt && log(EMPTY + charAt.call(cursor, ci++%4) + "\b\b", true);
477 | }, TIMEOUT);
478 |
479 |
480 | //^ this is useful to test internals on non minified version
481 | wru.debug = function (O_o) {
482 | return eval("(" + O_o + ")");
483 | };
484 | //$ and this block is removed at build time
485 |
486 |
487 | TIMEOUT *= TIMEOUT; // by default, timeout is 10000 (10 seconds)
488 | // this is the place you can set it, e.g.
489 | // TIMEOUT = 2000; // 2 seconds
490 |
491 | wru.random = FALSE; // by default tests order is preseverd
492 | // set wru.random = TRUE to randomly sort them
493 |
494 | return wru;
495 |
496 | }(this);
--------------------------------------------------------------------------------
/build/wru.dom.js:
--------------------------------------------------------------------------------
1 | /*!
2 | (C) Andrea Giammarchi, @WebReflection - Mit Style License
3 | */
4 | /**@license (C) Andrea Giammarchi, @WebReflection - Mit Style License
5 | */
6 | var wru = function (window) {"use strict";
7 |
8 | /**
9 | * Copyright (C) 2011 by Andrea Giammarchi, @WebReflection
10 | *
11 | * Permission is hereby granted, free of charge, to any person obtaining a copy
12 | * of this software and associated documentation files (the "Software"), to deal
13 | * in the Software without restriction, including without limitation the rights
14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | * copies of the Software, and to permit persons to whom the Software is
16 | * furnished to do so, subject to the following conditions:
17 | *
18 | * The above copyright notice and this permission notice shall be included in
19 | * all copies or substantial portions of the Software.
20 | *
21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 | * THE SOFTWARE.
28 | */
29 |
30 | // DOM specific version
31 | function isGonnaBeLegen() {
32 | current = shift.call(queue);
33 | if (current) {
34 | if (typeof current == "function") {
35 | current = {name: current[NAME] || "anonymous", test: current};
36 | }
37 | (node = putItThereAndGimmeBack(
38 | putItThereAndGimmeBack(wru.node, "div"),
39 | "span"
40 | ))[INNERHTML] = (
41 | (iHasIt(current, NAME) && current[NAME])
42 | ||
43 | (iHasIt(current, DESCRIPTION) && current[DESCRIPTION])
44 | ||
45 | UNKNOWN
46 | ) + EMPTY + EMPTY;
47 | pass = [];
48 | fail = [];
49 | fatal = [];
50 | tmp = {};
51 | giveItATry("setup");
52 | fatal[LENGTH] || giveItATry("test");
53 | waitForIt || Dary();
54 | } else {
55 | showSummary();
56 | }
57 | }
58 |
59 | function create(nodeName) {
60 | try {
61 | return createElement.call(document, nodeName);
62 | } catch($) {
63 | // unfortuantely IE < 7 does not support host objects via call
64 | return document.createElement(nodeName);
65 | }
66 | }
67 |
68 | function putItThereAndGimmeBack(node, nodeName) {
69 | return node.appendChild(create(nodeName));
70 | }
71 |
72 | function passTheInfo(info) {
73 | node[INNERHTML] = slice.call(node[INNERHTML], 0, -2) + EMPTY + info;
74 | }
75 |
76 | function showSummary() {
77 | var
78 | node = wru.node.insertBefore(
79 | create("div"),
80 | wru.node.firstChild
81 | ),
82 | innerHTML,
83 | className,
84 | status
85 | ;
86 | if (overallFatal) {
87 | status = className = "error";
88 | innerHTML = "There Are Errors: " + overallFatal;
89 | } else if(overallFail) {
90 | status = className = "fail";
91 | innerHTML = overallFail + " Tests Failed";
92 | } else {
93 | status = className = "pass";
94 | innerHTML = "Passed " + overallPass + " Tests";
95 | }
96 | wru.status = status;
97 | node[INNERHTML] = "" + innerHTML + "";
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]+="