Apply coding conventions for JavaScript
[lhc/web/wiklou.git] / resources / src / jquery / jquery.qunit.completenessTest.js
index d6dfedd..556bf8c 100644 (file)
  *
  * @author Timo Tijhof, 2011-2012
  */
-( function ( $ ) {
+( function ( mw, $ ) {
        'use strict';
 
        var util,
                hasOwn = Object.prototype.hasOwnProperty,
-               log = (window.console && window.console.log)
-                       ? function () { return window.console.log.apply(window.console, arguments); }
+               log = ( window.console && window.console.log )
+                       ? function () { return window.console.log.apply( window.console, arguments ); }
                        : function () {};
 
        // Simplified version of a few jQuery methods, except that they don't
@@ -64,6 +64,8 @@
         *  were not called from that instance.
         */
        function CompletenessTest( masterVariable, ignoreFn ) {
+               var warn,
+                       that = this;
 
                // Keep track in these objects. Keyed by strings with the
                // method names (ie. 'my.foo', 'my.bar', etc.) values are boolean true.
                this.lazyLimit = 2000;
                this.lazyCounter = 0;
 
-               var that = this;
-
                // Bind begin and end to QUnit.
                QUnit.begin( function () {
-                       that.walkTheObject( null, masterVariable, masterVariable, [], CompletenessTest.ACTION_INJECT );
-                       log( 'CompletenessTest/walkTheObject/ACTION_INJECT', that );
-               });
+                       // Suppress warnings (e.g. deprecation notices for accessing the properties)
+                       warn = mw.log.warn;
+                       mw.log.warn = $.noop;
+
+                       that.walkTheObject( masterVariable, null, masterVariable, [] );
+                       log( 'CompletenessTest/walkTheObject', that );
+
+                       // Restore warnings
+                       mw.log.warn = warn;
+                       warn = undefined;
+               } );
 
                QUnit.done( function () {
                        that.populateMissingTests();
                                        var elItem = document.createElement( 'li' );
                                        elItem.textContent = key;
                                        elList.appendChild( elItem );
-                               });
+                               } );
 
                                elFoot = document.createElement( 'p' );
                                elFoot.innerHTML = '<em>&mdash; CompletenessTest</em>';
 
                                util.each( style, function ( key, value ) {
                                        elOutputWrapper.style[key] = value;
-                               });
+                               } );
                                return elOutputWrapper;
                        }
 
                        if ( toolbar ) {
                                toolbar.insertBefore( testResults, toolbar.firstChild );
                        }
-               });
+               } );
 
                return this;
        }
 
-       /* Static members */
-       CompletenessTest.ACTION_INJECT = 500;
-       CompletenessTest.ACTION_CHECK = 501;
-
        /* Public methods */
        CompletenessTest.fn = CompletenessTest.prototype = {
 
                 *  Initially this is the same as currVar.
                 * @param parentPathArray {Array} Array of names that indicate our breadcrumb path starting at
                 *  masterVariable. Not including currName.
-                * @param action {Number} What is this function supposed to do (ACTION_INJECT or ACTION_CHECK)
                 */
-               walkTheObject: function ( currName, currVar, masterVariable, parentPathArray, action ) {
-                       var key, value, currPathArray,
-                               type = util.type( currVar ),
-                               that = this;
+               walkTheObject: function ( currObj, currName, masterVariable, parentPathArray ) {
+                       var key, currVal, type,
+                               ct = this,
+                               currPathArray = parentPathArray;
 
-                       currPathArray = parentPathArray;
                        if ( currName ) {
                                currPathArray.push( currName );
+                               currVal = currObj[currName];
+                       } else {
+                               currName = '(root)';
+                               currVal = currObj;
                        }
 
+                       type = util.type( currVal );
+
                        // Hard ignores
-                       if ( this.ignoreFn( currVar, that, currPathArray ) ) {
+                       if ( this.ignoreFn( currVal, this, currPathArray ) ) {
                                return null;
                        }
 
 
                        // Functions
                        if ( type === 'function' ) {
-
-                               if ( !currVar.prototype || util.isEmptyObject( currVar.prototype ) ) {
-
-                                       if ( action === CompletenessTest.ACTION_INJECT ) {
-
-                                               that.injectionTracker[ currPathArray.join( '.' ) ] = true;
-                                               that.injectCheck( masterVariable, currPathArray, function () {
-                                                       that.methodCallTracker[ currPathArray.join( '.' ) ] = true;
-                                               } );
-                                       }
-
-                               // We don't support checking object constructors yet...
-                               // ...we can check the prototypes fine, though.
-                               } else {
-                                       if ( action === CompletenessTest.ACTION_INJECT ) {
-
-                                               for ( key in currVar.prototype ) {
-                                                       if ( hasOwn.call( currVar.prototype, key ) ) {
-                                                               value = currVar.prototype[key];
-                                                               if ( key === 'constructor' ) {
-                                                                       continue;
-                                                               }
-
-                                                               that.walkTheObject( key, value, masterVariable, currPathArray.concat( 'prototype' ), action );
-                                                       }
-                                               }
-
-                                       }
+                               // Don't put a spy in constructor functions as it messes with
+                               // instanceof etc.
+                               if ( !currVal.prototype || util.isEmptyObject( currVal.prototype ) ) {
+                                       this.injectionTracker[ currPathArray.join( '.' ) ] = true;
+                                       this.injectCheck( currObj, currName, function () {
+                                               ct.methodCallTracker[ currPathArray.join( '.' ) ] = true;
+                                       } );
                                }
-
                        }
 
                        // Recursively. After all, this is the *completeness* test
-                       if ( type === 'function' || type === 'object' ) {
-                               for ( key in currVar ) {
-                                       if ( hasOwn.call( currVar, key ) ) {
-                                               value = currVar[key];
-
-                                               that.walkTheObject( key, value, masterVariable, currPathArray.slice(), action );
+                       // This also traverses static properties and the prototype of a constructor
+                       if ( type === 'object' || type === 'function' ) {
+                               for ( key in currVal ) {
+                                       if ( hasOwn.call( currVal, key ) ) {
+                                               this.walkTheObject( currVal, key, masterVariable, currPathArray.slice() );
                                        }
                                }
                        }
                        var ct = this;
                        util.each( ct.injectionTracker, function ( key ) {
                                ct.hasTest( key );
-                       });
+                       } );
                },
 
                /**
                 * @param objectPathArray {Array}
                 * @param injectFn {Function}
                 */
-               injectCheck: function ( masterVariable, objectPathArray, injectFn ) {
-                       var i, len, prev, memberName, lastMember,
-                               curr = masterVariable;
-
-                       // Get the object in question through the path from the master variable,
-                       // We can't pass the value directly because we need to re-define the object
-                       // member and keep references to the parent object, member name and member
-                       // value at all times.
-                       for ( i = 0, len = objectPathArray.length; i < len; i++ ) {
-                               memberName = objectPathArray[i];
-
-                               prev = curr;
-                               curr = prev[memberName];
-                               lastMember = memberName;
-                       }
+               injectCheck: function ( obj, key, injectFn ) {
+                       var spy,
+                               val = obj[ key ];
 
-                       // Objects are by reference, members (unless objects) are not.
-                       prev[lastMember] = function () {
+                       spy = function () {
                                injectFn();
-                               return curr.apply( this, arguments );
+                               return val.apply( this, arguments );
                        };
+
+                       // Make the spy inherit from the original so that its static methods are also
+                       // visible in the spy (e.g. when we inject a check into mw.log, mw.log.warn
+                       // must remain accessible).
+                       /*jshint proto:true */
+                       spy.__proto__ = val;
+
+                       // Objects are by reference, members (unless objects) are not.
+                       obj[ key ] = spy;
                }
        };
 
        /* Expose */
        window.CompletenessTest = CompletenessTest;
 
-}( jQuery ) );
+}( mediaWiki, jQuery ) );