javascriptes6-promisequnit

How does the qunit knows when the async test callback is complete even though the tests does not return promise?


Qunit executes the async tests one by one, but how does it know a test is complete, since the tests does not return a Promise which the qunit can await?

In this demo example https://jsfiddle.net/6bnLmyof/

function squareAfter1Second(x) {
    const timeout = x * 1000;
    console.log("squareAfter1Second x:", x);
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(x * x);
        }, timeout);
    });
}

const { test } = QUnit;

test( "an async test", async t => {
    console.log("starting test1");
    t.equal( await squareAfter1Second(3), 9 );
    t.equal( await squareAfter1Second(4), 16 );
});

test( "an async test2", async t => {
    console.log("starting test2");
    t.equal( await squareAfter1Second(1), 1 );
});

There are 2 async tests that run one by one. The tests post a macro task (setTimeout) to the event loop, but somehow qunit is able to wait for the test to complete despite the tests not returning promise. Also, in the souce code of qunit there are no await keywords present.


Solution

  • Async functions always return Promises which resolve once the end of their block is reached (or a return is reached). So even though nothing is explicitly returned, the awaits mean that both async callbacks implicitly return Promises that resolve after all awaits in the function complete. So test just needs to iterate through each callback function it's called with, and await each call.

    Here's an example of how you can implement this yourself too, without changing any of the code inside squareAfter1Second or the test calls:

    const queue = []
    const test = (_, callback) => {
      queue.push(callback);
    };
    
    function squareAfter1Second(x) {
        const timeout = x * 1000;
        console.log("squareAfter1Second x:", x);
        return new Promise(resolve => {
            setTimeout(() => {
                resolve(x * x);
            }, timeout);
        });
    }
    
    test( "an async test", async t => {
        console.log("starting test1");
        t.equal( await squareAfter1Second(3), 9 );
        t.equal( await squareAfter1Second(4), 16 );
    });
    
    test( "an async test2", async t => {
        console.log("starting test2");
        t.equal( await squareAfter1Second(1), 1 );
    });
    
    (async () => {
      for (const callback of queue) {
        console.log('Awaiting callback...');
        await callback({ equal: () => void 0 });
        console.log('Callback done');
      }
    })();