Skip to content

Commit

Permalink
Refactor assertion helpers into QUnit.assert (backwards compatible)
Browse files Browse the repository at this point in the history
- QUnit.assert
 * Now contains all assertion helpers
   (kept equals and same out of it, those are only in QUnit itself)
 * Had to move QUnit.test / QUnit.stop up in the code so that they're
   not inside QUnit.assert. The actual test/stop functions have not
   been altered.
 * Per #228 discussion, new standard format created by this:
<pre>
// Test runner
QUnit.assert.cowType = function ( cow, expected, message ) {
    QUnit.push( .. , expected, message );
};

// Test suite
QUnit.test( "FooCow and BarCow defaults", function ( assert ) {
	var bar, foo;
	expect( 1 );

	bar = new BarCow();
	foo = new FooCow();

	assert.strictEqual( foo.name, "", "A FooCow has no name" );
	assert.cowType( foo, "awesome", "A FooCow must be awesome" );
});
</pre>
 * Added unit tests for new QUnit.assert and test() "assert"
   argument. Also added unit tests for the old methods (global
   and via QUnit).
 * Kept rest of the test suite in tact to proof backwards
   compatibility.

- Issues:
 * fixes #228

- Misc:
 * Added few random bits of documentation.
   Turned some block comments into /* .. */
  • Loading branch information
Krinkle committed May 9, 2012
1 parent b0678b2 commit 9491c02
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 66 deletions.
167 changes: 101 additions & 66 deletions qunit/qunit.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,12 @@ Test.prototype = {
}

if ( config.notrycatch ) {
this.callback.call( this.testEnvironment );
this.callback.call( this.testEnvironment, QUnit.assert );
return;
}

try {
this.callback.call( this.testEnvironment );
this.callback.call( this.testEnvironment, QUnit.assert );
} catch( e ) {
QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + ": " + e.message, extractStacktrace( e, 1 ) );
// else next test will carry the responsibility
Expand Down Expand Up @@ -293,6 +293,7 @@ Test.prototype = {
}
};

// Root QUnit object.
// `QUnit` initialized at top of scope
QUnit = {

Expand Down Expand Up @@ -340,8 +341,59 @@ QUnit = {
config.current.expected = asserts;
},

// Asserts true.
// @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
start: function( count ) {
config.semaphore -= count || 1;
// don't start until equal number of stop-calls
if ( config.semaphore > 0 ) {
return;
}
// ignore if start is called more often then stop
if ( config.semaphore < 0 ) {
config.semaphore = 0;
}
// A slight delay, to avoid any current callbacks
if ( defined.setTimeout ) {
window.setTimeout(function() {
if ( config.semaphore > 0 ) {
return;
}
if ( config.timeout ) {
clearTimeout( config.timeout );
}

config.blocking = false;
process( true );
}, 13);
} else {
config.blocking = false;
process( true );
}
},

stop: function( count ) {
config.semaphore += count || 1;
config.blocking = true;

if ( config.testTimeout && defined.setTimeout ) {
clearTimeout( config.timeout );
config.timeout = window.setTimeout(function() {
QUnit.ok( false, "Test timed out" );
config.semaphore = 1;
QUnit.start();
}, config.testTimeout );
}
}
};

// Asssert helpers
// All of these must call either QUnit.push() or manually do:
// - runLoggingCallbacks( "log", .. );
// - config.current.assertions.push({ .. });
QUnit.assert = {
/**
* Asserts rough true-ish result.
* @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
*/
ok: function( result, msg ) {
if ( !config.current ) {
throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
Expand Down Expand Up @@ -371,8 +423,11 @@ QUnit = {
});
},

// Checks that the first two arguments are equal, with an optional message. Prints out both actual and expected values.
// @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes." );
/**
* Assert that the first two arguments are equal, with an optional message.
* Prints out both actual and expected values.
* @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
*/
equal: function( actual, expected, message ) {
QUnit.push( expected == actual, actual, expected, message );
},
Expand Down Expand Up @@ -429,50 +484,21 @@ QUnit = {
}

QUnit.ok( ok, message );
},

start: function( count ) {
config.semaphore -= count || 1;
// don't start until equal number of stop-calls
if ( config.semaphore > 0 ) {
return;
}
// ignore if start is called more often then stop
if ( config.semaphore < 0 ) {
config.semaphore = 0;
}
// A slight delay, to avoid any current callbacks
if ( defined.setTimeout ) {
window.setTimeout(function() {
if ( config.semaphore > 0 ) {
return;
}
if ( config.timeout ) {
clearTimeout( config.timeout );
}

config.blocking = false;
process( true );
}, 13);
} else {
config.blocking = false;
process( true );
}
},
}
};

stop: function( count ) {
config.semaphore += count || 1;
config.blocking = true;
// @deprecated: Kept assertion helpers in root for backwards compatibility
extend( QUnit, QUnit.assert );

if ( config.testTimeout && defined.setTimeout ) {
clearTimeout( config.timeout );
config.timeout = window.setTimeout(function() {
QUnit.ok( false, "Test timed out" );
config.semaphore = 1;
QUnit.start();
}, config.testTimeout );
}
}
/**
* @deprecated: Kept for backwards compatibility
* next step: remove entirely
*/
QUnit.equals = function() {
QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
};
QUnit.same = function() {
QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
};

// We want access to the constructor's prototype
Expand All @@ -484,17 +510,11 @@ QUnit = {
QUnit.constructor = F;
}());

// deprecated; still export them to window to provide clear error messages
// next step: remove entirely
QUnit.equals = function() {
QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
};
QUnit.same = function() {
QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
};

// Maintain internal state
// `config` initialized at top of scope
/**
* Config object: Maintain internal state
* Later exposed as QUnit.config
* `config` initialized at top of scope
*/
config = {
// The queue of tests to run
queue: [],
Expand Down Expand Up @@ -525,7 +545,7 @@ config = {
moduleDone: []
};

// Load paramaters
// Initialize more QUnit.config and QUnit.urlParams
(function() {
var i,
location = window.location || { search: "", protocol: "file:" },
Expand All @@ -551,14 +571,17 @@ config = {
QUnit.isLocal = location.protocol === "file:";
}());

// Expose the API as global variables, unless an 'exports' object exists,
// in that case we assume we're in CommonJS - export everything at the end
// Export global variables, unless an 'exports' object exists,
// in that case we assume we're in CommonJS (dealt with on the bottom of the script)
if ( typeof exports === "undefined" ) {
extend( window, QUnit );

// Expose QUnit object
window.QUnit = QUnit;
}

// define these after exposing globals to keep them in these QUnit namespace only
// Extend QUnit object,
// these after set here because they should not be exposed as global functions
extend( QUnit, {
config: config,

Expand Down Expand Up @@ -766,25 +789,37 @@ extend( QUnit, {
extend: extend,
id: id,
addEvent: addEvent
// load, equiv, jsDump, diff: Attached later
});

// QUnit.constructor is set to the empty F() above so that we can add to it's prototype later
// Doing this allows us to tell if the following methods have been overwritten on the actual
// QUnit object, which is a deprecated way of using the callbacks.
/**
* @deprecated: Created for backwards compatibility with test runner that set the hook function
* into QUnit.{hook}, instead of invoking it and passing the hook function.
* QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
* Doing this allows us to tell if the following methods have been overwritten on the actual
* QUnit object.
*/
extend( QUnit.constructor.prototype, {

// Logging callbacks; all receive a single argument with the listed properties
// run test/logs.html for any related changes
begin: registerLoggingCallback( "begin" ),

// done: { failed, passed, total, runtime }
done: registerLoggingCallback( "done" ),

// log: { result, actual, expected, message }
log: registerLoggingCallback( "log" ),

// testStart: { name }
testStart: registerLoggingCallback( "testStart" ),

// testDone: { name, failed, passed, total }
testDone: registerLoggingCallback( "testDone" ),

// moduleStart: { name }
moduleStart: registerLoggingCallback( "moduleStart" ),

// moduleDone: { name, failed, passed, total }
moduleDone: registerLoggingCallback( "moduleDone" )
});
Expand Down
15 changes: 15 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,21 @@ test("expect in test", 1, function() {
ok(true);
});

QUnit.module("assertion helpers");

QUnit.test( "QUnit.assert compatibility", function( assert ) {
QUnit.expect(4);

assert.ok( true, "Calling method on `assert` argument to test() callback" );

// Should also work, although not documented
QUnit.assert.ok( true, "Calling method on QUnit.assert object" );

// Test compatibility aliases
QUnit.ok( true, "Calling aliased method in QUnit root object" );
ok( true, "Calling aliased function in global namespace" );
});

module("setup test", {
setup: function() {
ok(true);
Expand Down

0 comments on commit 9491c02

Please sign in to comment.