Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: Adding promise handling on qunit callbacks: begin, moduleStart, testStart, testDone, moduleDone #1307

Merged
merged 5 commits into from
Nov 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ module.exports = function( grunt ) {
"test/reorderError1.html",
"test/reorderError2.html",
"test/callbacks.html",
"test/callbacks-promises.html",
"test/events.html",
"test/events-in-test.html",
"test/logs.html",
Expand Down
13 changes: 12 additions & 1 deletion docs/callbacks/QUnit.begin.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ categories:

## `QUnit.begin( callback )`

Register a callback to fire whenever the test suite begins.
Register a callback to fire whenever the test suite begins. The callback can return a promise that will be waited for before the next callback is handled.

`QUnit.begin()` is called once before running any tests.

Expand Down Expand Up @@ -39,3 +39,14 @@ QUnit.begin( ( { totalTests } ) => {
console.log( `Test amount: ${totalTests}` );
});
```

Returning a promise:

```js
QUnit.begin( () => {
return new Promise(function(resolve, reject) {
// do some async work
resolve();
});
});
```
13 changes: 12 additions & 1 deletion docs/callbacks/QUnit.done.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ categories:

## `QUnit.done( callback )`

Register a callback to fire whenever the test suite ends.
Register a callback to fire whenever the test suite ends. The callback can return a promise that will be waited for before the next callback is handled.

| parameter | description |
|-----------|-------------|
Expand Down Expand Up @@ -40,3 +40,14 @@ QUnit.done( ( { total, failed, passed, runtime } ) => {
console.log( `Total: ${total}, Failed: ${failed}, Passed: ${passed}, Runtime: ${runtime}` );
});
```

Returning a promise:

```js
QUnit.done( () => {
return new Promise(function(resolve, reject) {
// do some async work
resolve();
});
});
```
2 changes: 2 additions & 0 deletions docs/callbacks/QUnit.log.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ The properties of the details argument are listed below as options.
|-----------|-------------|
| callback (function) | Callback to execute. Provides a single argument with the callback details object |

**NOTE: Callback in QUnit.log() does not handle promises and must be synchronous.**

#### Callback details: `callback( details: { result, actual, expected, message, source, module, name, runtime, todo } )`

| parameter | description |
Expand Down
15 changes: 13 additions & 2 deletions docs/callbacks/QUnit.moduleDone.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ categories:

## `QUnit.moduleDone( callback )`

Register a callback to fire whenever a module ends.
Register a callback to fire whenever a module ends. The callback can return a promise that will be waited for before the next callback is handled.

| parameter | description |
|-----------|-------------|
Expand Down Expand Up @@ -40,4 +40,15 @@ Using modern syntax:
QUnit.moduleDone( ( { name, failed, total } ) => {
console.log( `Finished running: ${name} Failed/total: ${failed}, ${total}` );
});
```
```

Returning a promise:

```js
QUnit.moduleDone( () => {
return new Promise(function(resolve, reject) {
// do some async work
resolve();
});
});
```
13 changes: 12 additions & 1 deletion docs/callbacks/QUnit.moduleStart.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ categories:

## `QUnit.moduleStart( callback )`

Register a callback to fire whenever a module begins.
Register a callback to fire whenever a module begins. The callback can return a promise that will be waited for before the next callback is handled.

| parameter | description |
|-----------|-------------|
Expand Down Expand Up @@ -37,3 +37,14 @@ QUnit.moduleStart( ( { name } ) => {
console.log( `Now running: ${name}` );
});
```

Returning a promise:

```js
QUnit.moduleStart( () => {
return new Promise(function(resolve, reject) {
// do some async work
resolve();
});
});
```
2 changes: 2 additions & 0 deletions docs/callbacks/QUnit.on.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Register a callback to fire whenever the specified event is emitted. Conforms to
| eventName (string) | The name of the event for which to execute the provided callback. |
| callback (function) | Callback to execute. Receives a single argument representing the data for the event. |

**NOTE: Callback in QUnit.on() does not handle promises and must be synchronous.**

### Example

Printing results of a test suite.
Expand Down
13 changes: 12 additions & 1 deletion docs/callbacks/QUnit.testDone.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ categories:

## `QUnit.testDone( callback )`

Register a callback to fire whenever a test ends.
Register a callback to fire whenever a test ends. The callback can return a promise that will be waited for before the next callback is handled.

| parameter | description |
|-----------|-------------|
Expand Down Expand Up @@ -70,3 +70,14 @@ QUnit.testDone( ( { module, name, total, passed, failed, skipped, todo, runtime
console.log( JSON.stringify( result, null, 2 ) );
} );
```

Returning a promise:

```js
QUnit.testDone( () => {
return new Promise(function(resolve, reject) {
// do some async work
resolve();
});
});
```
13 changes: 12 additions & 1 deletion docs/callbacks/QUnit.testStart.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ categories:

## `QUnit.testStart( callback )`

Register a callback to fire whenever a test begins.
Register a callback to fire whenever a test begins. The callback can return a promise that will be waited for before the next callback is handled.

| parameter | description |
|-----------|-------------|
Expand Down Expand Up @@ -40,3 +40,14 @@ QUnit.testStart( ( { module, name } ) => {
console.log( `Now running: ${module}: ${name}` );
});
```

Returning a promise:

```js
QUnit.testStart( () => {
return new Promise(function(resolve, reject) {
// do some async work
resolve();
});
});
```
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"babel-preset-env": "1.6.1",
"co": "4.6.0",
"commitplease": "3.1.0",
"es6-promise": "^4.2.5",
"eslint-config-jquery": "1.0.1",
"eslint-plugin-html": "4.0.1",
"eslint-plugin-qunit": "3.2.0",
Expand All @@ -70,6 +71,7 @@
"proxyquire": "1.8.0",
"requirejs": "2.3.5",
"rollup-plugin-babel": "3.0.2",
"rollup-plugin-node-resolve": "^3.4.0",
"semver": "5.4.1"
},
"scripts": {
Expand Down
6 changes: 3 additions & 3 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/* eslint-env node */

var babel = require( "rollup-plugin-babel" );
var resolve = require( "rollup-plugin-node-resolve" );

module.exports = {
format: "iife",
exports: "none",
plugins: [
babel( {
exclude: "node_modules/**"
} )
resolve( { modulesOnly: true } ),
babel()
],

// eslint-disable-next-line no-multi-str
Expand Down
12 changes: 8 additions & 4 deletions src/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ function scheduleBegin() {
}
}

function unblockAndAdvanceQueue() {
config.blocking = false;
ProcessingQueue.advance();
}

export function begin() {
var i, l,
modulesLog = [];
Expand Down Expand Up @@ -171,11 +176,10 @@ export function begin() {
runLoggingCallbacks( "begin", {
totalTests: Test.count,
modules: modulesLog
} );
} ).then( unblockAndAdvanceQueue );
} else {
unblockAndAdvanceQueue();
}

config.blocking = false;
ProcessingQueue.advance();
}

exportQUnit( QUnit );
Expand Down
22 changes: 17 additions & 5 deletions src/core/logging.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import config from "./config";
import { objectType } from "./utilities";
import Promise from "../promise";

// Register logging callbacks
export function registerLoggingCallbacks( obj ) {
Expand Down Expand Up @@ -34,10 +35,21 @@ export function registerLoggingCallbacks( obj ) {
}

export function runLoggingCallbacks( key, args ) {
var i, l, callbacks;

callbacks = config.callbacks[ key ];
for ( i = 0, l = callbacks.length; i < l; i++ ) {
callbacks[ i ]( args );
var callbacks = config.callbacks[ key ];

// Handling 'log' callbacks separately. Unlike the other callbacks,
// the log callback is not controlled by the processing queue,
// but rather used by asserts. Hence to promisfy the 'log' callback
// would mean promisfying each step of a test
if ( key === "log" ) {
step2yeung marked this conversation as resolved.
Show resolved Hide resolved
callbacks.map( callback => callback( args ) );
return;
}

// ensure that each callback is executed serially
return callbacks.reduce( ( promiseChain, callback ) => {
return promiseChain.then( () => {
return Promise.resolve( callback( args ) );
step2yeung marked this conversation as resolved.
Show resolved Hide resolved
} );
}, Promise.resolve( [] ) );
}
45 changes: 30 additions & 15 deletions src/core/processing-queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
runLoggingCallbacks
} from "./logging";

import Promise from "../promise";
import {
globalSuite
} from "../core";
Expand All @@ -33,31 +34,44 @@ const taskQueue = [];
function advance() {
advanceTaskQueue();

if ( !taskQueue.length ) {
if ( !taskQueue.length && !config.blocking && !config.current ) {
advanceTestQueue();
}
}

/**
* Advances the taskQueue to the next task if it is ready and not empty.
* Advances the taskQueue with an increased depth
*/
function advanceTaskQueue() {
const start = now();
config.depth = ( config.depth || 0 ) + 1;

while ( taskQueue.length && !config.blocking ) {
processTaskQueue( start );

config.depth--;
}

/**
* Process the first task on the taskQueue as a promise.
* Each task is a function returned by https://github.com/qunitjs/qunit/blob/master/src/test.js#L381
*/
function processTaskQueue( start ) {
if ( taskQueue.length && !config.blocking ) {
const elapsedTime = now() - start;

if ( !defined.setTimeout || config.updateRate <= 0 || elapsedTime < config.updateRate ) {
const task = taskQueue.shift();
task();
Promise.resolve( task() ).then( function() {
if ( !taskQueue.length ) {
advance();
} else {
processTaskQueue( start );
}
} );
} else {
setTimeout( advance );
break;
}
}

config.depth--;
}

/**
Expand Down Expand Up @@ -179,18 +193,19 @@ function done() {
failed: config.stats.bad,
total: config.stats.all,
runtime
} );
} ).then( () => {

// Clear own storage items if all tests passed
if ( storage && config.stats.bad === 0 ) {
for ( let i = storage.length - 1; i >= 0; i-- ) {
const key = storage.key( i );
// Clear own storage items if all tests passed
if ( storage && config.stats.bad === 0 ) {
for ( let i = storage.length - 1; i >= 0; i-- ) {
const key = storage.key( i );

if ( key.indexOf( "qunit-test-" ) === 0 ) {
storage.removeItem( key );
if ( key.indexOf( "qunit-test-" ) === 0 ) {
storage.removeItem( key );
}
}
}
}
} );
}

const ProcessingQueue = {
Expand Down
3 changes: 3 additions & 0 deletions src/promise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import _Promise from "es6-promise/lib/es6-promise";

export default typeof Promise !== "undefined" ? Promise : _Promise;
Loading