I've started using QUnit and discovered that it's support for asynchronous tests really didn't meet my needs. After working with it for a while I figured out what I needed and implemented it as an extension to the existing testrunner.js:
ok() does not take a function as an assertion, the fix to extend ok() to accept function callbacks is trivial. start() and stop() are non-blocking tests in the sense that assertions that after a stopped test will be run out of order. My use cases require that the assertions always be run in order because the conditions they test for are asynchronous and dependent on each other. Also you have to hand code polling every time you use start()/ stop() which is a pain. Multiple pending stop() are not supported. Because of the existing tests depend on start()/stop() it is too late to propose changing their behavior. I propose a new function called wait(callback, msg, timeout, increment) that implements a polling test/retest that has adjustable timeout period and test increment frequency and blocks progress in the process() function until the assertion passes or the timeout is exceeded. Multiple pending wait() are supported. The only problem I have with this solution is that the assertions are still reported in the order of callback result processing rather than the order as declared in the code. However asynchronous assertion execution is now properly ordered at run time. I'm open for suggestions on how to get the code order of assertions to match the reporting order with either start()/stop() or my new wait() function. I'm thinking tagging the assertions order at declaration time and then sorting the output at report time will be required. A working proposed implementation of these changes is below. config.waiting is added, wait() is exported to the global environment, ok() and process() are modified and the implementation of wait() is added: var config = { stats: { all: 0, bad: 0 }, queue: [], // block until document ready blocking: true, waiting: [], //restrict modules/tests by get parameters filters: GETParams, isLocal: !!(window.location.protocol == 'file:') }; // public API as global methods $.extend(window, { test: test, module: module, expect: expect, ok: ok, equals: equals, start: start, stop: stop, wait: wait, ... function ok(a, msg) { if (typeof a === "function") { a = a(); } config.assertions.push({ result: !!a, message: msg }); } ... function process() { var wait, callback, msg, time, increment, test; while (config.queue.length && !config.blocking) { while (config.waiting.length > 0) { wait = config.waiting[0]; callback = wait[0]; msg = wait[1]; time = wait[2]; increment = wait[3]; if (time && time < (new Date()).getTime()) { test = false; } else { test = callback(); if (!test) { setTimeout(function () { process(); }, increment); return; } } ok(test, msg); config.waiting = config.waiting.slice(1); } config.queue.shift()(); } } function wait(callback, msg, timeout, increment) { if (!increment) { increment = 100; // Default increment 1/10 second } var time = (new Date()).getTime() + timeout; config.waiting.push([callback, msg, time, increment]); } Here is an example test using the new wait() and ok() with a callback: test("Ensure iframe is properly constructed.", function() { expect(5); wait(function() { return $("#test").attr("contentWindow"); }, "Wait for contentWindow to exist.", 2500, 250); wait(function() { return $("#test").attr ("contentWindow").frames.length === 9; }, "Wait for 9 Iframes to be loaded in the content window.", 2500, 100); ok(function() { return TestSomeThingInsideThe9Frames() }, "Test iframe contents."); }); --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "jQuery Development" group. To post to this group, send email to jquery-dev@googlegroups.com To unsubscribe from this group, send email to jquery-dev+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/jquery-dev?hl=en -~----------~----~----~----~------~----~------~--~---