diff --git a/qunit/qunit.js b/qunit/qunit.js index e1b355b1c..1bb8918a5 100644 --- a/qunit/qunit.js +++ b/qunit/qunit.js @@ -21,18 +21,36 @@ var QUnit, // Keep a local reference to Date (GH-283) Date = window.Date, defined = { - setTimeout: typeof window.setTimeout !== "undefined", - sessionStorage: (function() { - var x = "qunit-test-string"; - try { - sessionStorage.setItem( x, x ); - sessionStorage.removeItem( x ); - return true; - } catch( e ) { - return false; + setTimeout: typeof window.setTimeout !== "undefined", + sessionStorage: (function() { + var x = "qunit-test-string"; + try { + sessionStorage.setItem( x, x ); + sessionStorage.removeItem( x ); + return true; + } catch( e ) { + return false; + } + }()) + }, + /** + * Makes a clone of an object using only Array or Object as base, + * and copies over the own enumerable properties. + * + * @param {Object} obj + * @return {Object} New object with only the own properties (recursively). + */ + objectValues = function( obj ) { + var key, val, + vals = QUnit.is( "array", obj ) ? [] : {}; + for ( key in obj ) { + if ( hasOwn.call( obj, key ) ) { + val = obj[key]; + vals[key] = val === Object(val) ? objectValues(val) : val; + } } - }()) -}; + return vals; + }; function Test( settings ) { extend( this, settings ); @@ -45,7 +63,7 @@ Test.count = 0; Test.prototype = { init: function() { var a, b, li, - tests = id( "qunit-tests" ); + tests = id( "qunit-tests" ); if ( tests ) { b = document.createElement( "strong" ); @@ -473,6 +491,26 @@ assert = { QUnit.push( expected != actual, actual, expected, message ); }, + /** + * @name propEqual + * @function + */ + propEqual: function( actual, expected, message ) { + actual = objectValues(actual); + expected = objectValues(expected); + QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); + }, + + /** + * @name notPropEqual + * @function + */ + notPropEqual: function( actual, expected, message ) { + actual = objectValues(actual); + expected = objectValues(expected); + QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); + }, + /** * @name deepEqual * @function @@ -942,8 +980,8 @@ QUnit.load = function() { // Initialize the config, saving the execution queue var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, urlConfigCheckboxes, moduleFilter, - numModules = 0, - moduleFilterHtml = "", + numModules = 0, + moduleFilterHtml = "", urlConfigHtml = "", oldconfig = extend( {}, config ); @@ -1045,7 +1083,7 @@ QUnit.load = function() { moduleFilter.innerHTML = moduleFilterHtml; addEvent( moduleFilter, "change", function() { var selectBox = moduleFilter.getElementsByTagName("select")[0], - selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value); + selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value); window.location = QUnit.url( { module: ( selectedModule === "" ) ? undefined : selectedModule } ); }); diff --git a/test/test.js b/test/test.js index 1e3a121c9..8058e6bcb 100644 --- a/test/test.js +++ b/test/test.js @@ -327,7 +327,84 @@ test("jsDump output", function() { }); module("assertions"); -test("raises",function() { + +test("propEqual", 5, function( assert ) { + var objectCreate = Object.create || function ( origin ) { + function O() {} + O.prototype = origin; + var r = new O(); + return r; + }; + + function Foo( x, y, z ) { + this.x = x; + this.y = y; + this.z = z; + } + Foo.prototype.doA = function () {}; + Foo.prototype.doB = function () {}; + Foo.prototype.bar = 'prototype'; + + function Bar() { + } + Bar.prototype = objectCreate( Foo.prototype ); + Bar.prototype.constructor = Bar; + + assert.propEqual( + new Foo( 1, '2', [] ), + { + x: 1, + y: '2', + z: [] + } + ); + + assert.notPropEqual( + new Foo( '1', 2, 3 ), + { + x: 1, + y: '2', + z: 3 + }, + 'Primitive values are strictly compared' + ); + + assert.notPropEqual( + new Foo( 1, '2', [] ), + { + x: 1, + y: '2', + z: {} + }, + 'Array type is preserved' + ); + + assert.notPropEqual( + new Foo( 1, '2', {} ), + { + x: 1, + y: '2', + z: [] + }, + 'Empty array is not the same as empty object' + ); + + assert.propEqual( + new Foo( 1, '2', new Foo( [ 3 ], new Bar(), null ) ), + { + x: 1, + y: '2', + z: { + x: [ 3 ], + y: {}, + z: null + } + }, + 'Complex nesting of different types, inheritance and constructors' + ); +}); + +test("raises", 8, function() { function CustomError( message ) { this.message = message; }